@gtkx/cli 1.0.0-rc.4 → 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 (149) hide show
  1. package/README.md +5 -5
  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 +0 -2
  10. package/dist/codegen/freshness.js.map +1 -1
  11. package/dist/codegen/run-codegen.d.ts +1 -1
  12. package/dist/codegen/run-codegen.d.ts.map +1 -1
  13. package/dist/codegen/run-codegen.js +1 -0
  14. package/dist/codegen/run-codegen.js.map +1 -1
  15. package/dist/codegen/store-resolver.d.ts +1 -1
  16. package/dist/codegen/store-resolver.d.ts.map +1 -1
  17. package/dist/codegen/store-resolver.js.map +1 -1
  18. package/dist/commands/dev.d.ts.map +1 -1
  19. package/dist/commands/dev.js +3 -1
  20. package/dist/commands/dev.js.map +1 -1
  21. package/dist/dev/entry-env.d.ts +3 -0
  22. package/dist/dev/entry-env.d.ts.map +1 -0
  23. package/dist/dev/entry-env.js +3 -0
  24. package/dist/dev/entry-env.js.map +1 -0
  25. package/dist/dev/runner-deps.d.ts.map +1 -1
  26. package/dist/dev/runner-deps.js +1 -0
  27. package/dist/dev/runner-deps.js.map +1 -1
  28. package/dist/dev/runner-main.d.ts.map +1 -1
  29. package/dist/dev/runner-main.js +3 -3
  30. package/dist/dev/runner-main.js.map +1 -1
  31. package/dist/dev/runner.d.ts +1 -0
  32. package/dist/dev/runner.d.ts.map +1 -1
  33. package/dist/dev/runner.js +22 -12
  34. package/dist/dev/runner.js.map +1 -1
  35. package/dist/dev/supervisor.d.ts +10 -3
  36. package/dist/dev/supervisor.d.ts.map +1 -1
  37. package/dist/dev/supervisor.js +23 -4
  38. package/dist/dev/supervisor.js.map +1 -1
  39. package/dist/internal/application-args.d.ts +6 -0
  40. package/dist/internal/application-args.d.ts.map +1 -0
  41. package/dist/internal/application-args.js +10 -0
  42. package/dist/internal/application-args.js.map +1 -0
  43. package/dist/internal/errors.d.ts +1 -2
  44. package/dist/internal/errors.d.ts.map +1 -1
  45. package/dist/internal/errors.js +1 -1
  46. package/dist/internal/errors.js.map +1 -1
  47. package/dist/internal/run-cli-tool.d.ts +10 -0
  48. package/dist/internal/run-cli-tool.d.ts.map +1 -0
  49. package/dist/internal/run-cli-tool.js +15 -0
  50. package/dist/internal/run-cli-tool.js.map +1 -0
  51. package/dist/internal.d.ts +3 -0
  52. package/dist/internal.d.ts.map +1 -0
  53. package/dist/internal.js +3 -0
  54. package/dist/internal.js.map +1 -0
  55. package/dist/mcp/client.d.ts +1 -1
  56. package/dist/mcp/client.d.ts.map +1 -1
  57. package/dist/mcp/client.js.map +1 -1
  58. package/dist/mcp/handlers.d.ts +1 -1
  59. package/dist/mcp/handlers.d.ts.map +1 -1
  60. package/dist/mcp/handlers.js +78 -41
  61. package/dist/mcp/handlers.js.map +1 -1
  62. package/dist/mcp/index.d.ts.map +1 -1
  63. package/dist/mcp/index.js +1 -0
  64. package/dist/mcp/index.js.map +1 -1
  65. package/dist/mcp/serialize-widget.d.ts +2 -2
  66. package/dist/mcp/serialize-widget.d.ts.map +1 -1
  67. package/dist/mcp/serialize-widget.js +47 -17
  68. package/dist/mcp/serialize-widget.js.map +1 -1
  69. package/dist/mcp/widget-properties.d.ts +6 -0
  70. package/dist/mcp/widget-properties.d.ts.map +1 -0
  71. package/dist/mcp/widget-properties.js +102 -0
  72. package/dist/mcp/widget-properties.js.map +1 -0
  73. package/dist/settings/compile.d.ts.map +1 -1
  74. package/dist/settings/compile.js +8 -12
  75. package/dist/settings/compile.js.map +1 -1
  76. package/dist/vite-plugins/asset-emitter.d.ts +9 -0
  77. package/dist/vite-plugins/asset-emitter.d.ts.map +1 -0
  78. package/dist/vite-plugins/asset-emitter.js +2 -0
  79. package/dist/vite-plugins/asset-emitter.js.map +1 -0
  80. package/dist/vite-plugins/asset-extensions.d.ts +1 -2
  81. package/dist/vite-plugins/asset-extensions.d.ts.map +1 -1
  82. package/dist/vite-plugins/asset-extensions.js +1 -1
  83. package/dist/vite-plugins/asset-extensions.js.map +1 -1
  84. package/dist/vite-plugins/built-url.d.ts.map +1 -1
  85. package/dist/vite-plugins/built-url.js +14 -10
  86. package/dist/vite-plugins/built-url.js.map +1 -1
  87. package/dist/vite-plugins/ensure-store.d.ts +4 -0
  88. package/dist/vite-plugins/ensure-store.d.ts.map +1 -0
  89. package/dist/vite-plugins/ensure-store.js +10 -0
  90. package/dist/vite-plugins/ensure-store.js.map +1 -0
  91. package/dist/vite-plugins/icons.d.ts.map +1 -1
  92. package/dist/vite-plugins/icons.js.map +1 -1
  93. package/dist/vite-plugins/react-compiler.d.ts +1 -1
  94. package/dist/vite-plugins/react-compiler.d.ts.map +1 -1
  95. package/dist/vite-plugins/react-compiler.js.map +1 -1
  96. package/dist/vite-plugins/resources.d.ts +1 -1
  97. package/dist/vite-plugins/resources.d.ts.map +1 -1
  98. package/dist/vite-plugins/resources.js +6 -15
  99. package/dist/vite-plugins/resources.js.map +1 -1
  100. package/dist/vite-plugins/settings.js.map +1 -1
  101. package/dist/vite-plugins/undeclared-library.d.ts +1 -1
  102. package/dist/vite-plugins/undeclared-library.d.ts.map +1 -1
  103. package/dist/vite-plugins/undeclared-library.js.map +1 -1
  104. package/dist/vite-plugins/worker.d.ts +4 -0
  105. package/dist/vite-plugins/worker.d.ts.map +1 -0
  106. package/dist/vite-plugins/worker.js +136 -0
  107. package/dist/vite-plugins/worker.js.map +1 -0
  108. package/dist/vitest-plugin.d.ts +7 -0
  109. package/dist/vitest-plugin.d.ts.map +1 -1
  110. package/dist/vitest-plugin.js +14 -1
  111. package/dist/vitest-plugin.js.map +1 -1
  112. package/package.json +16 -12
  113. package/src/builder.ts +3 -2
  114. package/src/cli.ts +4 -2
  115. package/src/codegen/freshness.ts +0 -2
  116. package/src/codegen/run-codegen.ts +1 -1
  117. package/src/codegen/store-resolver.ts +1 -1
  118. package/src/commands/dev.ts +3 -1
  119. package/src/dev/entry-env.ts +3 -0
  120. package/src/dev/runner-deps.ts +1 -0
  121. package/src/dev/runner-main.ts +3 -4
  122. package/src/dev/runner.ts +44 -13
  123. package/src/dev/supervisor.ts +54 -11
  124. package/src/internal/application-args.ts +13 -0
  125. package/src/internal/errors.ts +1 -1
  126. package/src/internal/run-cli-tool.ts +22 -0
  127. package/src/internal.ts +2 -0
  128. package/src/mcp/client.ts +1 -1
  129. package/src/mcp/handlers.ts +123 -56
  130. package/src/mcp/index.ts +1 -0
  131. package/src/mcp/serialize-widget.ts +71 -21
  132. package/src/mcp/widget-properties.ts +153 -0
  133. package/src/settings/compile.ts +8 -11
  134. package/src/vite-plugins/asset-emitter.ts +5 -0
  135. package/src/vite-plugins/asset-extensions.ts +1 -1
  136. package/src/vite-plugins/built-url.ts +19 -13
  137. package/src/vite-plugins/ensure-store.ts +14 -0
  138. package/src/vite-plugins/icons.ts +2 -5
  139. package/src/vite-plugins/react-compiler.ts +2 -1
  140. package/src/vite-plugins/resources.ts +10 -19
  141. package/src/vite-plugins/settings.ts +2 -2
  142. package/src/vite-plugins/undeclared-library.ts +1 -1
  143. package/src/vite-plugins/worker.ts +235 -0
  144. package/src/vitest-plugin.ts +15 -1
  145. package/dist/internal/resolve-cli-tool.d.ts +0 -3
  146. package/dist/internal/resolve-cli-tool.d.ts.map +0 -1
  147. package/dist/internal/resolve-cli-tool.js +0 -27
  148. package/dist/internal/resolve-cli-tool.js.map +0 -1
  149. 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
- isSensitive: widget.getSensitive(),
35
- isVisible: 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 };
@@ -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 };
@@ -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 { ConfigLoader } from "@gtkx/config";
1
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";