@gtkx/cli 1.0.0-rc.3 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/README.md +8 -9
  2. package/dist/builder.d.ts +1 -1
  3. package/dist/builder.d.ts.map +1 -1
  4. package/dist/builder.js +2 -1
  5. package/dist/builder.js.map +1 -1
  6. package/dist/cli.js +4 -2
  7. package/dist/cli.js.map +1 -1
  8. package/dist/codegen/freshness.d.ts.map +1 -1
  9. package/dist/codegen/freshness.js +3 -6
  10. package/dist/codegen/freshness.js.map +1 -1
  11. package/dist/codegen/run-codegen.d.ts +4 -4
  12. package/dist/codegen/run-codegen.d.ts.map +1 -1
  13. package/dist/codegen/run-codegen.js +15 -14
  14. package/dist/codegen/run-codegen.js.map +1 -1
  15. package/dist/codegen/store-resolver.d.ts +1 -3
  16. package/dist/codegen/store-resolver.d.ts.map +1 -1
  17. package/dist/codegen/store-resolver.js +13 -32
  18. package/dist/codegen/store-resolver.js.map +1 -1
  19. package/dist/commands/build.js +1 -1
  20. package/dist/commands/build.js.map +1 -1
  21. package/dist/commands/codegen.js +1 -1
  22. package/dist/commands/codegen.js.map +1 -1
  23. package/dist/commands/dev.d.ts.map +1 -1
  24. package/dist/commands/dev.js +4 -2
  25. package/dist/commands/dev.js.map +1 -1
  26. package/dist/commands/docs.js +13 -13
  27. package/dist/commands/docs.js.map +1 -1
  28. package/dist/dev/entry-env.d.ts +3 -0
  29. package/dist/dev/entry-env.d.ts.map +1 -0
  30. package/dist/dev/entry-env.js +3 -0
  31. package/dist/dev/entry-env.js.map +1 -0
  32. package/dist/dev/runner-deps.d.ts.map +1 -1
  33. package/dist/dev/runner-deps.js +1 -0
  34. package/dist/dev/runner-deps.js.map +1 -1
  35. package/dist/dev/runner-main.d.ts.map +1 -1
  36. package/dist/dev/runner-main.js +3 -3
  37. package/dist/dev/runner-main.js.map +1 -1
  38. package/dist/dev/runner.d.ts +1 -0
  39. package/dist/dev/runner.d.ts.map +1 -1
  40. package/dist/dev/runner.js +22 -12
  41. package/dist/dev/runner.js.map +1 -1
  42. package/dist/dev/supervisor.d.ts +10 -3
  43. package/dist/dev/supervisor.d.ts.map +1 -1
  44. package/dist/dev/supervisor.js +37 -18
  45. package/dist/dev/supervisor.js.map +1 -1
  46. package/dist/internal/application-args.d.ts +6 -0
  47. package/dist/internal/application-args.d.ts.map +1 -0
  48. package/dist/internal/application-args.js +10 -0
  49. package/dist/internal/application-args.js.map +1 -0
  50. package/dist/internal/errors.d.ts +1 -2
  51. package/dist/internal/errors.d.ts.map +1 -1
  52. package/dist/internal/errors.js +1 -1
  53. package/dist/internal/errors.js.map +1 -1
  54. package/dist/internal/run-cli-tool.d.ts +10 -0
  55. package/dist/internal/run-cli-tool.d.ts.map +1 -0
  56. package/dist/internal/run-cli-tool.js +15 -0
  57. package/dist/internal/run-cli-tool.js.map +1 -0
  58. package/dist/internal.d.ts +3 -0
  59. package/dist/internal.d.ts.map +1 -0
  60. package/dist/internal.js +3 -0
  61. package/dist/internal.js.map +1 -0
  62. package/dist/mcp/client.d.ts +1 -1
  63. package/dist/mcp/client.d.ts.map +1 -1
  64. package/dist/mcp/client.js.map +1 -1
  65. package/dist/mcp/handlers.d.ts +1 -1
  66. package/dist/mcp/handlers.d.ts.map +1 -1
  67. package/dist/mcp/handlers.js +79 -42
  68. package/dist/mcp/handlers.js.map +1 -1
  69. package/dist/mcp/index.d.ts.map +1 -1
  70. package/dist/mcp/index.js +1 -0
  71. package/dist/mcp/index.js.map +1 -1
  72. package/dist/mcp/serialize-widget.d.ts +2 -2
  73. package/dist/mcp/serialize-widget.d.ts.map +1 -1
  74. package/dist/mcp/serialize-widget.js +47 -17
  75. package/dist/mcp/serialize-widget.js.map +1 -1
  76. package/dist/mcp/widget-properties.d.ts +6 -0
  77. package/dist/mcp/widget-properties.d.ts.map +1 -0
  78. package/dist/mcp/widget-properties.js +102 -0
  79. package/dist/mcp/widget-properties.js.map +1 -0
  80. package/dist/refresh-runtime.d.ts +0 -10
  81. package/dist/refresh-runtime.d.ts.map +1 -1
  82. package/dist/refresh-runtime.js +0 -17
  83. package/dist/refresh-runtime.js.map +1 -1
  84. package/dist/settings/compile.d.ts.map +1 -1
  85. package/dist/settings/compile.js +8 -12
  86. package/dist/settings/compile.js.map +1 -1
  87. package/dist/settings/schema.d.ts +1 -1
  88. package/dist/settings/schema.d.ts.map +1 -1
  89. package/dist/settings/schema.js +1 -1
  90. package/dist/settings/schema.js.map +1 -1
  91. package/dist/vite-plugins/asset-emitter.d.ts +9 -0
  92. package/dist/vite-plugins/asset-emitter.d.ts.map +1 -0
  93. package/dist/vite-plugins/asset-emitter.js +2 -0
  94. package/dist/vite-plugins/asset-emitter.js.map +1 -0
  95. package/dist/vite-plugins/asset-extensions.d.ts +1 -2
  96. package/dist/vite-plugins/asset-extensions.d.ts.map +1 -1
  97. package/dist/vite-plugins/asset-extensions.js +1 -1
  98. package/dist/vite-plugins/asset-extensions.js.map +1 -1
  99. package/dist/vite-plugins/built-url.d.ts.map +1 -1
  100. package/dist/vite-plugins/built-url.js +14 -10
  101. package/dist/vite-plugins/built-url.js.map +1 -1
  102. package/dist/vite-plugins/ensure-store.d.ts +4 -0
  103. package/dist/vite-plugins/ensure-store.d.ts.map +1 -0
  104. package/dist/vite-plugins/ensure-store.js +10 -0
  105. package/dist/vite-plugins/ensure-store.js.map +1 -0
  106. package/dist/vite-plugins/icons.d.ts.map +1 -1
  107. package/dist/vite-plugins/icons.js.map +1 -1
  108. package/dist/vite-plugins/react-compiler.d.ts +1 -1
  109. package/dist/vite-plugins/react-compiler.d.ts.map +1 -1
  110. package/dist/vite-plugins/react-compiler.js.map +1 -1
  111. package/dist/vite-plugins/resources.d.ts +1 -1
  112. package/dist/vite-plugins/resources.d.ts.map +1 -1
  113. package/dist/vite-plugins/resources.js +6 -15
  114. package/dist/vite-plugins/resources.js.map +1 -1
  115. package/dist/vite-plugins/settings.js.map +1 -1
  116. package/dist/vite-plugins/undeclared-library.d.ts +1 -1
  117. package/dist/vite-plugins/undeclared-library.d.ts.map +1 -1
  118. package/dist/vite-plugins/undeclared-library.js.map +1 -1
  119. package/dist/vite-plugins/worker.d.ts +4 -0
  120. package/dist/vite-plugins/worker.d.ts.map +1 -0
  121. package/dist/vite-plugins/worker.js +136 -0
  122. package/dist/vite-plugins/worker.js.map +1 -0
  123. package/dist/vitest-plugin.d.ts +4 -3
  124. package/dist/vitest-plugin.d.ts.map +1 -1
  125. package/dist/vitest-plugin.js +11 -4
  126. package/dist/vitest-plugin.js.map +1 -1
  127. package/env.d.ts +25 -0
  128. package/package.json +17 -13
  129. package/src/builder.ts +3 -2
  130. package/src/cli.ts +4 -2
  131. package/src/codegen/freshness.ts +3 -8
  132. package/src/codegen/run-codegen.ts +19 -19
  133. package/src/codegen/store-resolver.ts +14 -40
  134. package/src/commands/build.ts +1 -1
  135. package/src/commands/codegen.ts +1 -1
  136. package/src/commands/dev.ts +4 -2
  137. package/src/commands/docs.ts +15 -15
  138. package/src/dev/entry-env.ts +3 -0
  139. package/src/dev/runner-deps.ts +1 -0
  140. package/src/dev/runner-main.ts +3 -4
  141. package/src/dev/runner.ts +44 -13
  142. package/src/dev/supervisor.ts +70 -27
  143. package/src/internal/application-args.ts +13 -0
  144. package/src/internal/errors.ts +1 -1
  145. package/src/internal/run-cli-tool.ts +22 -0
  146. package/src/internal.ts +2 -0
  147. package/src/mcp/client.ts +1 -1
  148. package/src/mcp/handlers.ts +124 -57
  149. package/src/mcp/index.ts +1 -0
  150. package/src/mcp/serialize-widget.ts +71 -21
  151. package/src/mcp/widget-properties.ts +153 -0
  152. package/src/refresh-runtime.ts +0 -17
  153. package/src/settings/compile.ts +8 -11
  154. package/src/settings/schema.ts +2 -2
  155. package/src/vite-plugins/asset-emitter.ts +5 -0
  156. package/src/vite-plugins/asset-extensions.ts +1 -1
  157. package/src/vite-plugins/built-url.ts +19 -13
  158. package/src/vite-plugins/ensure-store.ts +14 -0
  159. package/src/vite-plugins/icons.ts +2 -5
  160. package/src/vite-plugins/react-compiler.ts +3 -2
  161. package/src/vite-plugins/resources.ts +10 -19
  162. package/src/vite-plugins/settings.ts +2 -2
  163. package/src/vite-plugins/undeclared-library.ts +1 -1
  164. package/src/vite-plugins/worker.ts +235 -0
  165. package/src/vitest-plugin.ts +12 -4
  166. package/dist/internal/resolve-cli-tool.d.ts +0 -3
  167. package/dist/internal/resolve-cli-tool.d.ts.map +0 -1
  168. package/dist/internal/resolve-cli-tool.js +0 -27
  169. package/dist/internal/resolve-cli-tool.js.map +0 -1
  170. package/src/internal/resolve-cli-tool.ts +0 -37
@@ -1,41 +1,91 @@
1
1
  import type * as Gtk from "@gtkx/gi/gtk";
2
- import type { SerializedWidget } from "@gtkx/mcp/internal";
2
+ import { DEFAULT_SUBTREE_DEPTH, MAX_SUBTREE_WIDGETS, type SerializedWidget } from "@gtkx/mcp/internal";
3
3
 
4
4
  type WidgetIdResolver = (widget: Gtk.Widget) => string;
5
5
 
6
6
  type WidgetFormatting = {
7
7
  formatRole(role: Gtk.AccessibleRole): string;
8
- getWidgetNodeText(widget: Gtk.Widget): string | null;
8
+ getWidgetText(widget: Gtk.Widget): string | null;
9
+ };
10
+
11
+ type PendingWidget = {
12
+ depth: number;
13
+ node: SerializedWidget;
14
+ widget: Gtk.Widget;
15
+ };
16
+
17
+ type Expansion = {
18
+ budget: number;
19
+ maxDepth: number;
20
+ pending: PendingWidget[];
21
+ resolveId: WidgetIdResolver;
22
+ testing: WidgetFormatting;
23
+ };
24
+
25
+ const getChildren = (widget: Gtk.Widget): Gtk.Widget[] => {
26
+ const children: Gtk.Widget[] = [];
27
+ let child = widget.getFirstChild();
28
+
29
+ while (child) {
30
+ children.push(child);
31
+ child = child.getNextSibling();
32
+ }
33
+
34
+ return children;
35
+ };
36
+
37
+ const createNode = (widget: Gtk.Widget, expansion: Expansion): SerializedWidget => ({
38
+ id: expansion.resolveId(widget),
39
+ type: widget.constructor.name,
40
+ role: expansion.testing.formatRole(widget.getAccessibleRole()),
41
+ name: widget.getName() || null,
42
+ text: expansion.testing.getWidgetText(widget),
43
+ isSensitive: widget.getSensitive(),
44
+ isVisible: widget.getVisible(),
45
+ cssClasses: widget.getCssClasses(),
46
+ children: [],
47
+ });
48
+
49
+ const expandNode = (expansion: Expansion, parent: PendingWidget): void => {
50
+ const children = getChildren(parent.widget);
51
+ const canDescend = parent.depth < expansion.maxDepth;
52
+ const shownCount = canDescend ? Math.min(children.length, expansion.budget) : 0;
53
+ expansion.budget -= shownCount;
54
+
55
+ for (const child of children.slice(0, shownCount)) {
56
+ const node = createNode(child, expansion);
57
+ parent.node.children.push(node);
58
+ expansion.pending.push({ depth: parent.depth + 1, node, widget: child });
59
+ }
60
+
61
+ if (shownCount < children.length) {
62
+ parent.node.hiddenChildren = children.length - shownCount;
63
+ }
9
64
  };
10
65
 
11
66
  const serializeWidget = (
12
67
  widget: Gtk.Widget,
13
68
  resolveId: WidgetIdResolver,
14
69
  testing: WidgetFormatting,
15
- maxDepth = Infinity,
70
+ maxDepth = DEFAULT_SUBTREE_DEPTH,
16
71
  ): SerializedWidget => {
17
- const children: SerializedWidget[] = [];
72
+ const expansion: Expansion = {
73
+ budget: MAX_SUBTREE_WIDGETS - 1,
74
+ maxDepth,
75
+ pending: [],
76
+ resolveId,
77
+ testing,
78
+ };
18
79
 
19
- if (maxDepth > 0) {
20
- let child = widget.getFirstChild();
80
+ const root = createNode(widget, expansion);
81
+ let parent: PendingWidget | undefined = { depth: 0, node: root, widget };
21
82
 
22
- while (child) {
23
- children.push(serializeWidget(child, resolveId, testing, maxDepth - 1));
24
- child = child.getNextSibling();
25
- }
83
+ while (parent !== undefined) {
84
+ expandNode(expansion, parent);
85
+ parent = expansion.pending.shift();
26
86
  }
27
87
 
28
- return {
29
- id: resolveId(widget),
30
- type: widget.constructor.name,
31
- role: testing.formatRole(widget.getAccessibleRole()),
32
- name: widget.getName() || null,
33
- text: testing.getWidgetNodeText(widget),
34
- sensitive: widget.getSensitive(),
35
- visible: widget.getVisible(),
36
- cssClasses: widget.getCssClasses(),
37
- children,
38
- };
88
+ return root;
39
89
  };
40
90
 
41
91
  export { serializeWidget, type WidgetFormatting };
@@ -0,0 +1,153 @@
1
+ import * as GObject from "@gtkx/gi/gobject";
2
+ import * as Gtk from "@gtkx/gi/gtk";
3
+ import { propertyNotFoundError, type SerializedProperty } from "@gtkx/mcp/internal";
4
+ import {
5
+ type ExternalObject,
6
+ getHandle,
7
+ getInstanceType,
8
+ type Handle,
9
+ TYPE_ENUM,
10
+ TYPE_FLAGS,
11
+ typeIsA,
12
+ typeName,
13
+ } from "@gtkx/runtime";
14
+ import { fromValue, getValueType } from "@gtkx/runtime/internal";
15
+ import { camelCase, errorMessage, kebabCase } from "@gtkx/utils";
16
+ import type { WidgetRegistry } from "./widget-registry.js";
17
+
18
+ type PropertyRepresentation = Omit<SerializedProperty, "type">;
19
+ type NativeProperty = { type: bigint; handle: ExternalObject<Handle> };
20
+
21
+ const SCALAR_TYPES: Set<string> = new Set(["string", "number", "boolean"]);
22
+
23
+ const canonicalName = (name: string): string => kebabCase(name).replaceAll("_", "-");
24
+ const widgetTypeName = (widget: Gtk.Widget): string => typeName(getInstanceType(widget)) ?? widget.constructor.name;
25
+ const byName = (left: string, right: string): number => left.localeCompare(right);
26
+
27
+ const isScalar = (value: unknown): value is string | number | boolean | null =>
28
+ value === null || SCALAR_TYPES.has(typeof value);
29
+
30
+ function prototypesFor(widget: Gtk.Widget): object[] {
31
+ const prototypes: object[] = [];
32
+ let prototype = Object.getPrototypeOf(widget) as object | null;
33
+
34
+ while (prototype !== null && prototype !== Object.prototype) {
35
+ prototypes.push(prototype);
36
+ prototype = Object.getPrototypeOf(prototype) as object | null;
37
+ }
38
+
39
+ return prototypes;
40
+ }
41
+
42
+ function getAccessor(widget: Gtk.Widget, member: string): PropertyDescriptor | undefined {
43
+ for (const prototype of prototypesFor(widget)) {
44
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, member);
45
+
46
+ if (descriptor !== undefined) {
47
+ return descriptor;
48
+ }
49
+ }
50
+
51
+ return undefined;
52
+ }
53
+
54
+ function getReadableNames(prototype: object): string[] {
55
+ const descriptors = Object.entries(Object.getOwnPropertyDescriptors(prototype));
56
+
57
+ return descriptors.filter(([, descriptor]) => descriptor.get !== undefined).map(([member]) => kebabCase(member));
58
+ }
59
+
60
+ function readablePropertyNames(widget: Gtk.Widget): string[] {
61
+ const names = prototypesFor(widget).flatMap((prototype) => getReadableNames(prototype));
62
+
63
+ return [...new Set(names)].toSorted(byName);
64
+ }
65
+
66
+ const requireReadableProperty = (widget: Gtk.Widget, property: string): void => {
67
+ if (getAccessor(widget, camelCase(property))?.get !== undefined) {
68
+ return;
69
+ }
70
+
71
+ throw propertyNotFoundError(widgetTypeName(widget), property, readablePropertyNames(widget));
72
+ };
73
+
74
+ function readNativeProperty(widget: Gtk.Widget, property: string): NativeProperty {
75
+ const value = new GObject.Value();
76
+ widget.getProperty(property, value);
77
+ const handle = getHandle(value);
78
+
79
+ return { type: getValueType(handle), handle };
80
+ }
81
+
82
+ function describeObjectValue(value: unknown, registry: WidgetRegistry): PropertyRepresentation {
83
+ if (typeof value !== "object" || value === null) {
84
+ return { value: String(value) };
85
+ }
86
+
87
+ const name = typeName(getInstanceType(value)) ?? value.constructor.name;
88
+
89
+ if (!(value instanceof Gtk.Widget)) {
90
+ return { value: name };
91
+ }
92
+
93
+ registry.register(value);
94
+
95
+ return { value: name, widgetId: registry.getOrCreateId(value) };
96
+ }
97
+
98
+ function representValue(value: unknown, registry: WidgetRegistry): PropertyRepresentation {
99
+ if (isScalar(value)) {
100
+ return { value };
101
+ }
102
+
103
+ if (typeof value === "bigint") {
104
+ return { value: value.toString() };
105
+ }
106
+
107
+ if (Array.isArray(value)) {
108
+ return { value: value.map(String) };
109
+ }
110
+
111
+ return describeObjectValue(value, registry);
112
+ }
113
+
114
+ function representNativeProperty(native: NativeProperty, registry: WidgetRegistry): PropertyRepresentation {
115
+ if (typeIsA(native.type, TYPE_ENUM)) {
116
+ return { value: GObject.enumToString(native.type, Number(fromValue(native.handle))) };
117
+ }
118
+
119
+ if (typeIsA(native.type, TYPE_FLAGS)) {
120
+ return { value: GObject.flagsToString(native.type, Number(fromValue(native.handle))) };
121
+ }
122
+
123
+ return representValue(fromValue(native.handle), registry);
124
+ }
125
+
126
+ function serializeProperty(widget: Gtk.Widget, property: string, registry: WidgetRegistry): SerializedProperty {
127
+ const native = readNativeProperty(widget, property);
128
+ const type = typeName(native.type) ?? String(native.type);
129
+
130
+ try {
131
+ return { type, ...representNativeProperty(native, registry) };
132
+ } catch (error) {
133
+ return { type, value: null, note: `cannot be represented over MCP: ${errorMessage(error)}` };
134
+ }
135
+ }
136
+
137
+ function readWidgetProperties(
138
+ widget: Gtk.Widget,
139
+ properties: string[],
140
+ registry: WidgetRegistry,
141
+ ): Record<string, SerializedProperty> {
142
+ const values: Record<string, SerializedProperty> = {};
143
+
144
+ for (const name of properties) {
145
+ const property = canonicalName(name);
146
+ requireReadableProperty(widget, property);
147
+ values[property] = serializeProperty(widget, property, registry);
148
+ }
149
+
150
+ return values;
151
+ }
152
+
153
+ export { readWidgetProperties };
@@ -3,13 +3,6 @@ import "./refresh-globals.js";
3
3
 
4
4
  type ComponentType = (...args: unknown[]) => unknown;
5
5
 
6
- /**
7
- * Builds the per-module `$RefreshReg$` and `$RefreshSig$` hooks that React Refresh
8
- * transformed code expects, scoping every registered component to the given module.
9
- *
10
- * @param moduleId Identifier used to namespace registered component types so they are unique across modules.
11
- * @returns An object with `$RefreshReg$` and `$RefreshSig$` to install as module-local globals.
12
- */
13
6
  function createModuleRegistration(moduleId: string): {
14
7
  $RefreshReg$: (type: ComponentType, id: string) => void;
15
8
  $RefreshSig$: typeof RefreshRuntime.createSignatureFunctionForTransform;
@@ -22,13 +15,6 @@ function createModuleRegistration(moduleId: string): {
22
15
  };
23
16
  }
24
17
 
25
- /**
26
- * Determines whether a module can act as a React Refresh boundary, meaning every one of
27
- * its exports (ignoring `__esModule`) looks like a React component type.
28
- *
29
- * @param moduleExports The module's export object to inspect.
30
- * @returns `true` when the module qualifies as a refresh boundary, `false` otherwise.
31
- */
32
18
  const isComponentExport = (key: string, value: unknown): boolean =>
33
19
  key === "__esModule" || RefreshRuntime.isLikelyComponentType(value);
34
20
 
@@ -54,9 +40,6 @@ function isRefreshBoundary(moduleExports: Record<string, unknown>): boolean {
54
40
  return Object.keys(moduleExports).some((k) => k !== "__esModule");
55
41
  }
56
42
 
57
- /**
58
- * Applies all pending React Refresh updates, re-rendering components whose modules have changed.
59
- */
60
43
  function performRefresh(): void {
61
44
  RefreshRuntime.performReactRefresh();
62
45
  }
@@ -1,22 +1,19 @@
1
- import { formatChildProcessError } from "@gtkx/utils";
2
- import { execFileSync } from "node:child_process";
3
- import { resolveCliTool } from "../internal/resolve-cli-tool.js";
1
+ import { runCliTool } from "../internal/run-cli-tool.js";
4
2
 
5
3
  const SCHEMA_COMPILER = "glib-compile-schemas";
6
4
  const SCHEMA_COMPILE_TIMEOUT_MS = 30_000;
7
5
 
8
6
  const compileSchemas = (dir: string): void => {
9
- try {
10
- execFileSync(resolveCliTool(SCHEMA_COMPILER), ["--strict", dir], {
7
+ runCliTool({
8
+ tool: SCHEMA_COMPILER,
9
+ args: ["--strict", dir],
10
+ target: dir,
11
+ options: {
11
12
  stdio: ["ignore", "pipe", "pipe"],
12
13
  timeout: SCHEMA_COMPILE_TIMEOUT_MS,
13
14
  encoding: "utf8",
14
- });
15
- } catch (error) {
16
- const details = formatChildProcessError(error);
17
- const suffix = details ? `:\n${details}` : "";
18
- throw new Error(`glib-compile-schemas failed for ${dir}${suffix}`, { cause: error });
19
- }
15
+ },
16
+ });
20
17
  };
21
18
 
22
19
  export { compileSchemas };
@@ -11,7 +11,7 @@ import { renderEnvModule } from "./render.js";
11
11
 
12
12
  type SchemaEnvResult = {
13
13
  path: string;
14
- written: boolean;
14
+ isWritten: boolean;
15
15
  };
16
16
 
17
17
  const SCHEMA_SUFFIX = ".gschema.xml";
@@ -155,7 +155,7 @@ const emitSchemaEnv = (rootDir: string, dataDir: string | null): SchemaEnvResult
155
155
  const path = schemaEnvPath(rootDir);
156
156
  const isWritten = didWriteChanges(path, content);
157
157
 
158
- return { path, written: isWritten };
158
+ return { path, isWritten };
159
159
  };
160
160
 
161
161
  export {
@@ -0,0 +1,5 @@
1
+ type AssetEmitter = {
2
+ emitFile: (file: { type: "asset"; fileName: string; source: Buffer }) => void;
3
+ };
4
+
5
+ export { type AssetEmitter };
@@ -26,4 +26,4 @@ const ASSET_EXTENSIONS = [
26
26
  const ASSET_RE = new RegExp(String.raw`\.(?:${ASSET_EXTENSIONS.join("|")})$`, "i");
27
27
  const ASSET_PATH_RE = new RegExp(String.raw`\.(?:${ASSET_EXTENSIONS.join("|")})(?:\?.*)?$`, "i");
28
28
 
29
- export { ASSET_EXTENSIONS, ASSET_RE, ASSET_PATH_RE };
29
+ export { ASSET_RE, ASSET_PATH_RE };
@@ -1,4 +1,20 @@
1
1
  import type { Plugin } from "vite";
2
+ import { posix } from "node:path";
3
+
4
+ const EXECUTABLE_URL_BASE = "`file://${process.execPath}`";
5
+ const LEADING_SLASHES = /^\/+/;
6
+
7
+ const executableRelativeUrl = (target: string): string => {
8
+ const specifier = JSON.stringify(target.replace(LEADING_SLASHES, "./"));
9
+
10
+ return `decodeURIComponent(new URL(${specifier}, ${EXECUTABLE_URL_BASE}).pathname)`;
11
+ };
12
+
13
+ const bundleRelativeUrl = (filename: string): string => {
14
+ const specifier = JSON.stringify(`./${filename}`);
15
+
16
+ return `decodeURIComponent(new URL(${specifier}, import.meta.url).pathname)`;
17
+ };
2
18
 
3
19
  const renderAssetUrl = (
4
20
  filename: string,
@@ -9,21 +25,11 @@ const renderAssetUrl = (
9
25
  return undefined;
10
26
  }
11
27
 
12
- if (assetBase) {
13
- const executableDir = "require(\"path\").dirname(process.execPath)";
14
-
15
- return {
16
- runtime:
17
- `require("path").join(${executableDir},` +
18
- `${JSON.stringify(assetBase)},${JSON.stringify(filename)})`,
19
- };
28
+ if (assetBase === undefined) {
29
+ return { runtime: bundleRelativeUrl(filename) };
20
30
  }
21
31
 
22
- const filenameLiteral = JSON.stringify(`./${filename}`);
23
-
24
- return {
25
- runtime: `new URL(${filenameLiteral}, import.meta.url).pathname`,
26
- };
32
+ return { runtime: executableRelativeUrl(posix.join(assetBase, filename)) };
27
33
  };
28
34
 
29
35
  function gtkxBuiltUrl(assetBase?: string): Plugin {
@@ -0,0 +1,14 @@
1
+ import type { Plugin } from "vite";
2
+ import { ensureGenerated } from "../codegen/run-codegen.js";
3
+
4
+ const TEST_MODE = "test";
5
+
6
+ const gtkxEnsureStore = (): Plugin => ({
7
+ name: "gtkx:ensure-store",
8
+
9
+ async config() {
10
+ await ensureGenerated(process.cwd(), { shouldAnnounce: true, mode: TEST_MODE });
11
+ },
12
+ });
13
+
14
+ export { gtkxEnsureStore };
@@ -2,6 +2,7 @@ import type { Plugin, UserConfig } from "vite";
2
2
  import { info } from "@gtkx/utils";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
+ import type { AssetEmitter } from "./asset-emitter.js";
5
6
  import { prependBanner } from "../internal/banner.js";
6
7
  import { resolveDataDir } from "../internal/data-dir.js";
7
8
  import { type ListedFile, listFilesRecursive } from "../internal/list-files.js";
@@ -10,10 +11,6 @@ type PluginState = {
10
11
  iconsDir: string | null;
11
12
  };
12
13
 
13
- type PluginContext = {
14
- emitFile: (file: { type: "asset"; fileName: string; source: Buffer }) => void;
15
- };
16
-
17
14
  const ICONS_DIR = "icons";
18
15
 
19
16
  const XDG_ENV_BANNER = [
@@ -33,7 +30,7 @@ const resolveIconsDir = (config: UserConfig): string | null => {
33
30
  return dataDir === null ? null : join(root, dataDir, ICONS_DIR);
34
31
  };
35
32
 
36
- const emitIcons = (ctx: PluginContext, icons: ListedFile[]): void => {
33
+ const emitIcons = (ctx: AssetEmitter, icons: ListedFile[]): void => {
37
34
  for (const { absPath, rel } of icons) {
38
35
  ctx.emitFile({ type: "asset", fileName: join(ICONS_DIR, rel), source: readFileSync(absPath) });
39
36
  }
@@ -1,8 +1,9 @@
1
- import type { ResolvedReactCompilerOptions } from "@gtkx/config";
1
+ import type { ConfigLoader } from "@gtkx/config";
2
+ import type { ResolvedReactCompilerOptions } from "@gtkx/config/internal";
2
3
  import type { Plugin, ResolvedConfig, UserConfig } from "vite";
3
4
  import { transformAsync } from "@babel/core";
4
5
  import babelPresetTypescriptNs from "@babel/preset-typescript";
5
- import { type ConfigLoader, createConfigLoader } from "@gtkx/config/internal";
6
+ import { createConfigLoader } from "@gtkx/config/internal";
6
7
  import babelPluginReactCompilerNs from "babel-plugin-react-compiler";
7
8
 
8
9
  type ReactCompilerState = {
@@ -1,13 +1,14 @@
1
+ import type { ConfigLoader } from "@gtkx/config";
1
2
  import type { Plugin, ResolvedConfig, UserConfig, ViteDevServer } from "vite";
2
- import { type ConfigLoader, createConfigLoader } from "@gtkx/config/internal";
3
- import { error, formatChildProcessError, info, sortStrings } from "@gtkx/utils";
4
- import { execFileSync } from "node:child_process";
3
+ import { createConfigLoader } from "@gtkx/config/internal";
4
+ import { error, info, sortStrings } from "@gtkx/utils";
5
5
  import { copyFileSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
6
6
  import { tmpdir } from "node:os";
7
7
  import { dirname, join } from "node:path";
8
+ import type { AssetEmitter } from "./asset-emitter.js";
8
9
  import { DATA_IMPORT_PREFIX, resolveDataDir } from "../internal/data-dir.js";
9
10
  import { type ListedFile, listFilesRecursive } from "../internal/list-files.js";
10
- import { resolveCliTool } from "../internal/resolve-cli-tool.js";
11
+ import { runCliTool } from "../internal/run-cli-tool.js";
11
12
  import { withStagingDir } from "../internal/staging-dir.js";
12
13
  import { ASSET_PATH_RE, ASSET_RE } from "./asset-extensions.js";
13
14
  import { renderInitModule } from "./resource-init-module.js";
@@ -83,17 +84,10 @@ const stageBundle = (dir: string, entries: Map<string, ResourceEntry>): string =
83
84
  };
84
85
 
85
86
  const runCompiler = (sourceDir: string, manifest: string, outputPath: string): Buffer => {
86
- try {
87
- execFileSync(resolveCliTool(RESOURCE_COMPILER), [
88
- `--sourcedir=${sourceDir}`,
89
- `--target=${outputPath}`,
90
- manifest,
91
- ]);
92
- } catch (error) {
93
- const details = formatChildProcessError(error);
94
- const suffix = details ? `:\n${details}` : "";
95
- throw new Error(`${RESOURCE_COMPILER} failed${suffix}`, { cause: error });
96
- }
87
+ runCliTool({
88
+ tool: RESOURCE_COMPILER,
89
+ args: [`--sourcedir=${sourceDir}`, `--target=${outputPath}`, manifest],
90
+ });
97
91
 
98
92
  return readFileSync(outputPath);
99
93
  };
@@ -229,10 +223,7 @@ const loadAssetModule = (state: PluginState, virtualId: string): string => {
229
223
  ].join("\n");
230
224
  };
231
225
 
232
- const emitBuildBundle = (
233
- ctx: { emitFile: (asset: { type: "asset"; fileName: string; source: Buffer }) => string },
234
- state: PluginState,
235
- ): void => {
226
+ const emitBuildBundle = (ctx: AssetEmitter, state: PluginState): void => {
236
227
  if (!state.isBuild || state.entries.size === 0) {
237
228
  return;
238
229
  }
@@ -3,6 +3,7 @@ import { error, errorMessage, info } from "@gtkx/utils";
3
3
  import { mkdtempSync, readFileSync } from "node:fs";
4
4
  import { tmpdir } from "node:os";
5
5
  import { basename, join } from "node:path";
6
+ import type { AssetEmitter } from "./asset-emitter.js";
6
7
  import { prependBanner } from "../internal/banner.js";
7
8
  import { resolveDataDir } from "../internal/data-dir.js";
8
9
  import { removeTempDir, withStagingDir } from "../internal/staging-dir.js";
@@ -22,9 +23,8 @@ type PluginState = {
22
23
  cleanupProcessExit: (() => void) | null;
23
24
  };
24
25
 
25
- type PluginContext = {
26
+ type PluginContext = AssetEmitter & {
26
27
  error: (message: string) => never;
27
- emitFile: (file: { type: "asset"; fileName: string; source: Buffer }) => void;
28
28
  };
29
29
 
30
30
  const VIRTUAL_PREFIX = "\0gtkx-settings:";
@@ -1,4 +1,4 @@
1
- import type { ConfigLoader } from "@gtkx/config/internal";
1
+ import type { ConfigLoader } from "@gtkx/config";
2
2
  import type { Plugin, UserConfig } from "vite";
3
3
  import { discoverGirNamespaces, resolveGirPath } from "@gtkx/codegen";
4
4
  import { createConfigLoader } from "@gtkx/config/internal";