@gtkx/cli 1.0.0-rc.1 → 1.0.0-rc.3

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 (286) hide show
  1. package/dist/builder.d.ts +3 -2
  2. package/dist/builder.d.ts.map +1 -1
  3. package/dist/builder.js +2 -1
  4. package/dist/builder.js.map +1 -1
  5. package/dist/cli.d.ts +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +21 -6
  8. package/dist/cli.js.map +1 -1
  9. package/dist/codegen/freshness.d.ts +5 -5
  10. package/dist/codegen/freshness.d.ts.map +1 -1
  11. package/dist/codegen/freshness.js +26 -23
  12. package/dist/codegen/freshness.js.map +1 -1
  13. package/dist/codegen/report.d.ts +2 -1
  14. package/dist/codegen/report.d.ts.map +1 -1
  15. package/dist/codegen/report.js +4 -2
  16. package/dist/codegen/report.js.map +1 -1
  17. package/dist/codegen/run-codegen.d.ts +10 -10
  18. package/dist/codegen/run-codegen.d.ts.map +1 -1
  19. package/dist/codegen/run-codegen.js +72 -47
  20. package/dist/codegen/run-codegen.js.map +1 -1
  21. package/dist/codegen/store-resolver.d.ts +9 -6
  22. package/dist/codegen/store-resolver.d.ts.map +1 -1
  23. package/dist/codegen/store-resolver.js +24 -7
  24. package/dist/codegen/store-resolver.js.map +1 -1
  25. package/dist/commands/build.d.ts +4 -3
  26. package/dist/commands/build.d.ts.map +1 -1
  27. package/dist/commands/build.js +2 -1
  28. package/dist/commands/build.js.map +1 -1
  29. package/dist/commands/codegen.d.ts +2 -1
  30. package/dist/commands/codegen.d.ts.map +1 -1
  31. package/dist/commands/codegen.js +6 -4
  32. package/dist/commands/codegen.js.map +1 -1
  33. package/dist/commands/dev.d.ts +2 -1
  34. package/dist/commands/dev.d.ts.map +1 -1
  35. package/dist/commands/dev.js +2 -1
  36. package/dist/commands/dev.js.map +1 -1
  37. package/dist/commands/docs.d.ts +2 -1
  38. package/dist/commands/docs.d.ts.map +1 -1
  39. package/dist/commands/docs.js +25 -7
  40. package/dist/commands/docs.js.map +1 -1
  41. package/dist/dev/icon-dir.d.ts +2 -1
  42. package/dist/dev/icon-dir.d.ts.map +1 -1
  43. package/dist/dev/icon-dir.js +6 -3
  44. package/dist/dev/icon-dir.js.map +1 -1
  45. package/dist/dev/refresh-tracker.d.ts +5 -4
  46. package/dist/dev/refresh-tracker.d.ts.map +1 -1
  47. package/dist/dev/refresh-tracker.js +6 -5
  48. package/dist/dev/refresh-tracker.js.map +1 -1
  49. package/dist/dev/runner-deps.d.ts +2 -1
  50. package/dist/dev/runner-deps.d.ts.map +1 -1
  51. package/dist/dev/runner-deps.js +20 -4
  52. package/dist/dev/runner-deps.js.map +1 -1
  53. package/dist/dev/runner-main.d.ts +2 -1
  54. package/dist/dev/runner-main.d.ts.map +1 -1
  55. package/dist/dev/runner-main.js +3 -2
  56. package/dist/dev/runner-main.js.map +1 -1
  57. package/dist/dev/runner.d.ts +6 -5
  58. package/dist/dev/runner.d.ts.map +1 -1
  59. package/dist/dev/runner.js +69 -38
  60. package/dist/dev/runner.js.map +1 -1
  61. package/dist/dev/schema-dir.d.ts +2 -1
  62. package/dist/dev/schema-dir.d.ts.map +1 -1
  63. package/dist/dev/schema-dir.js +4 -2
  64. package/dist/dev/schema-dir.js.map +1 -1
  65. package/dist/dev/supervisor.d.ts +7 -6
  66. package/dist/dev/supervisor.d.ts.map +1 -1
  67. package/dist/dev/supervisor.js +107 -67
  68. package/dist/dev/supervisor.js.map +1 -1
  69. package/dist/dev/vite-dev-server.d.ts +3 -3
  70. package/dist/dev/vite-dev-server.d.ts.map +1 -1
  71. package/dist/dev/vite-dev-server.js +2 -1
  72. package/dist/dev/vite-dev-server.js.map +1 -1
  73. package/dist/internal/banner.d.ts +2 -1
  74. package/dist/internal/banner.d.ts.map +1 -1
  75. package/dist/internal/banner.js +2 -1
  76. package/dist/internal/banner.js.map +1 -1
  77. package/dist/internal/data-dir.d.ts +3 -2
  78. package/dist/internal/data-dir.d.ts.map +1 -1
  79. package/dist/internal/data-dir.js +29 -14
  80. package/dist/internal/data-dir.js.map +1 -1
  81. package/dist/internal/entry-arg.d.ts +5 -4
  82. package/dist/internal/entry-arg.d.ts.map +1 -1
  83. package/dist/internal/entry-arg.js +10 -7
  84. package/dist/internal/entry-arg.js.map +1 -1
  85. package/dist/internal/errors.d.ts +3 -2
  86. package/dist/internal/errors.d.ts.map +1 -1
  87. package/dist/internal/errors.js +7 -5
  88. package/dist/internal/errors.js.map +1 -1
  89. package/dist/internal/list-files.d.ts +3 -2
  90. package/dist/internal/list-files.d.ts.map +1 -1
  91. package/dist/internal/list-files.js +5 -3
  92. package/dist/internal/list-files.js.map +1 -1
  93. package/dist/internal/resolve-cli-tool.d.ts +2 -1
  94. package/dist/internal/resolve-cli-tool.d.ts.map +1 -1
  95. package/dist/internal/resolve-cli-tool.js +17 -10
  96. package/dist/internal/resolve-cli-tool.js.map +1 -1
  97. package/dist/internal/staging-dir.d.ts +3 -2
  98. package/dist/internal/staging-dir.d.ts.map +1 -1
  99. package/dist/internal/staging-dir.js +3 -2
  100. package/dist/internal/staging-dir.js.map +1 -1
  101. package/dist/internal/xdg-data-dirs.d.ts +2 -1
  102. package/dist/internal/xdg-data-dirs.d.ts.map +1 -1
  103. package/dist/internal/xdg-data-dirs.js +4 -2
  104. package/dist/internal/xdg-data-dirs.js.map +1 -1
  105. package/dist/mcp/client.d.ts +9 -4
  106. package/dist/mcp/client.d.ts.map +1 -1
  107. package/dist/mcp/client.js +106 -81
  108. package/dist/mcp/client.js.map +1 -1
  109. package/dist/mcp/handlers.d.ts +3 -2
  110. package/dist/mcp/handlers.d.ts.map +1 -1
  111. package/dist/mcp/handlers.js +89 -61
  112. package/dist/mcp/handlers.js.map +1 -1
  113. package/dist/mcp/index.d.ts +2 -2
  114. package/dist/mcp/index.d.ts.map +1 -1
  115. package/dist/mcp/index.js +30 -13
  116. package/dist/mcp/index.js.map +1 -1
  117. package/dist/mcp/serialize-widget.d.ts +3 -3
  118. package/dist/mcp/serialize-widget.d.ts.map +1 -1
  119. package/dist/mcp/serialize-widget.js +9 -6
  120. package/dist/mcp/serialize-widget.js.map +1 -1
  121. package/dist/mcp/testing-loader.d.ts +3 -4
  122. package/dist/mcp/testing-loader.d.ts.map +1 -1
  123. package/dist/mcp/testing-loader.js +43 -21
  124. package/dist/mcp/testing-loader.js.map +1 -1
  125. package/dist/mcp/widget-registry.d.ts +3 -2
  126. package/dist/mcp/widget-registry.d.ts.map +1 -1
  127. package/dist/mcp/widget-registry.js +4 -3
  128. package/dist/mcp/widget-registry.js.map +1 -1
  129. package/dist/refresh-globals.d.ts +2 -0
  130. package/dist/refresh-globals.d.ts.map +1 -0
  131. package/dist/refresh-globals.js +8 -0
  132. package/dist/refresh-globals.js.map +1 -0
  133. package/dist/refresh-runtime.d.ts +5 -11
  134. package/dist/refresh-runtime.d.ts.map +1 -1
  135. package/dist/refresh-runtime.js +16 -14
  136. package/dist/refresh-runtime.js.map +1 -1
  137. package/dist/settings/compile.d.ts +2 -1
  138. package/dist/settings/compile.d.ts.map +1 -1
  139. package/dist/settings/compile.js +6 -4
  140. package/dist/settings/compile.js.map +1 -1
  141. package/dist/settings/parser.d.ts +6 -5
  142. package/dist/settings/parser.d.ts.map +1 -1
  143. package/dist/settings/parser.js +26 -22
  144. package/dist/settings/parser.js.map +1 -1
  145. package/dist/settings/render.d.ts +3 -2
  146. package/dist/settings/render.d.ts.map +1 -1
  147. package/dist/settings/render.js +38 -64
  148. package/dist/settings/render.js.map +1 -1
  149. package/dist/settings/schema.d.ts +8 -8
  150. package/dist/settings/schema.d.ts.map +1 -1
  151. package/dist/settings/schema.js +61 -43
  152. package/dist/settings/schema.js.map +1 -1
  153. package/dist/vite-plugins/asset-extensions.d.ts +4 -3
  154. package/dist/vite-plugins/asset-extensions.d.ts.map +1 -1
  155. package/dist/vite-plugins/asset-extensions.js +4 -3
  156. package/dist/vite-plugins/asset-extensions.js.map +1 -1
  157. package/dist/vite-plugins/built-url.d.ts +2 -1
  158. package/dist/vite-plugins/built-url.d.ts.map +1 -1
  159. package/dist/vite-plugins/built-url.js +19 -13
  160. package/dist/vite-plugins/built-url.js.map +1 -1
  161. package/dist/vite-plugins/css.d.ts +2 -1
  162. package/dist/vite-plugins/css.d.ts.map +1 -1
  163. package/dist/vite-plugins/css.js +23 -15
  164. package/dist/vite-plugins/css.js.map +1 -1
  165. package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts +5 -4
  166. package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts.map +1 -1
  167. package/dist/vite-plugins/fast-refresh/refresh-filter.js +11 -7
  168. package/dist/vite-plugins/fast-refresh/refresh-filter.js.map +1 -1
  169. package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts +4 -3
  170. package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts.map +1 -1
  171. package/dist/vite-plugins/fast-refresh/swc-refresh.js +45 -41
  172. package/dist/vite-plugins/fast-refresh/swc-refresh.js.map +1 -1
  173. package/dist/vite-plugins/icons.d.ts +2 -1
  174. package/dist/vite-plugins/icons.d.ts.map +1 -1
  175. package/dist/vite-plugins/icons.js +24 -20
  176. package/dist/vite-plugins/icons.js.map +1 -1
  177. package/dist/vite-plugins/index.d.ts +2 -1
  178. package/dist/vite-plugins/index.d.ts.map +1 -1
  179. package/dist/vite-plugins/index.js +4 -3
  180. package/dist/vite-plugins/index.js.map +1 -1
  181. package/dist/vite-plugins/native.d.ts +2 -1
  182. package/dist/vite-plugins/native.d.ts.map +1 -1
  183. package/dist/vite-plugins/native.js +6 -4
  184. package/dist/vite-plugins/native.js.map +1 -1
  185. package/dist/vite-plugins/react-compiler.d.ts +3 -2
  186. package/dist/vite-plugins/react-compiler.d.ts.map +1 -1
  187. package/dist/vite-plugins/react-compiler.js +26 -20
  188. package/dist/vite-plugins/react-compiler.js.map +1 -1
  189. package/dist/vite-plugins/react-dom-prebundle.d.ts +2 -1
  190. package/dist/vite-plugins/react-dom-prebundle.d.ts.map +1 -1
  191. package/dist/vite-plugins/react-dom-prebundle.js +2 -1
  192. package/dist/vite-plugins/react-dom-prebundle.js.map +1 -1
  193. package/dist/vite-plugins/resource-init-module.d.ts +2 -1
  194. package/dist/vite-plugins/resource-init-module.d.ts.map +1 -1
  195. package/dist/vite-plugins/resource-init-module.js +39 -38
  196. package/dist/vite-plugins/resource-init-module.js.map +1 -1
  197. package/dist/vite-plugins/resource-shared.d.ts +7 -5
  198. package/dist/vite-plugins/resource-shared.d.ts.map +1 -1
  199. package/dist/vite-plugins/resource-shared.js +17 -10
  200. package/dist/vite-plugins/resource-shared.js.map +1 -1
  201. package/dist/vite-plugins/resources.d.ts +3 -2
  202. package/dist/vite-plugins/resources.d.ts.map +1 -1
  203. package/dist/vite-plugins/resources.js +95 -55
  204. package/dist/vite-plugins/resources.js.map +1 -1
  205. package/dist/vite-plugins/settings-worker-env.d.ts +2 -1
  206. package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -1
  207. package/dist/vite-plugins/settings-worker-env.js +4 -2
  208. package/dist/vite-plugins/settings-worker-env.js.map +1 -1
  209. package/dist/vite-plugins/settings.d.ts +2 -1
  210. package/dist/vite-plugins/settings.d.ts.map +1 -1
  211. package/dist/vite-plugins/settings.js +56 -32
  212. package/dist/vite-plugins/settings.js.map +1 -1
  213. package/dist/vite-plugins/strip-query.d.ts +3 -0
  214. package/dist/vite-plugins/strip-query.d.ts.map +1 -0
  215. package/dist/vite-plugins/strip-query.js +6 -0
  216. package/dist/vite-plugins/strip-query.js.map +1 -0
  217. package/dist/vite-plugins/undeclared-library.d.ts +3 -1
  218. package/dist/vite-plugins/undeclared-library.d.ts.map +1 -1
  219. package/dist/vite-plugins/undeclared-library.js +11 -8
  220. package/dist/vite-plugins/undeclared-library.js.map +1 -1
  221. package/dist/vite-plugins/virtual-module.d.ts +2 -2
  222. package/dist/vite-plugins/virtual-module.d.ts.map +1 -1
  223. package/dist/vite-plugins/virtual-module.js +5 -3
  224. package/dist/vite-plugins/virtual-module.js.map +1 -1
  225. package/dist/vitest-plugin.d.ts.map +1 -1
  226. package/dist/vitest-plugin.js.map +1 -1
  227. package/env.d.ts +21 -61
  228. package/package.json +12 -14
  229. package/src/babel-modules.d.ts +0 -2
  230. package/src/builder.ts +7 -6
  231. package/src/cli.ts +26 -6
  232. package/src/codegen/freshness.ts +39 -26
  233. package/src/codegen/report.ts +10 -2
  234. package/src/codegen/run-codegen.ts +131 -66
  235. package/src/codegen/store-resolver.ts +52 -15
  236. package/src/commands/build.ts +3 -2
  237. package/src/commands/codegen.ts +9 -4
  238. package/src/commands/dev.ts +3 -4
  239. package/src/commands/docs.ts +47 -7
  240. package/src/dev/icon-dir.ts +13 -3
  241. package/src/dev/refresh-tracker.ts +12 -8
  242. package/src/dev/runner-deps.ts +30 -7
  243. package/src/dev/runner-main.ts +4 -3
  244. package/src/dev/runner.ts +100 -41
  245. package/src/dev/schema-dir.ts +7 -2
  246. package/src/dev/supervisor.ts +169 -71
  247. package/src/dev/vite-dev-server.ts +4 -2
  248. package/src/internal/banner.ts +5 -1
  249. package/src/internal/data-dir.ts +47 -16
  250. package/src/internal/entry-arg.ts +16 -8
  251. package/src/internal/errors.ts +15 -7
  252. package/src/internal/list-files.ts +10 -4
  253. package/src/internal/resolve-cli-tool.ts +28 -12
  254. package/src/internal/staging-dir.ts +5 -2
  255. package/src/internal/xdg-data-dirs.ts +8 -2
  256. package/src/mcp/client.ts +143 -85
  257. package/src/mcp/handlers.ts +126 -69
  258. package/src/mcp/index.ts +38 -14
  259. package/src/mcp/serialize-widget.ts +15 -8
  260. package/src/mcp/testing-loader.ts +64 -23
  261. package/src/mcp/widget-registry.ts +11 -5
  262. package/src/refresh-globals.ts +10 -0
  263. package/src/refresh-runtime.ts +20 -16
  264. package/src/settings/compile.ts +7 -4
  265. package/src/settings/parser.ts +69 -30
  266. package/src/settings/render.ts +66 -66
  267. package/src/settings/schema.ts +100 -46
  268. package/src/vite-plugins/asset-extensions.ts +4 -3
  269. package/src/vite-plugins/built-url.ts +30 -15
  270. package/src/vite-plugins/css.ts +35 -20
  271. package/src/vite-plugins/fast-refresh/refresh-filter.ts +18 -10
  272. package/src/vite-plugins/fast-refresh/swc-refresh.ts +60 -47
  273. package/src/vite-plugins/icons.ts +40 -24
  274. package/src/vite-plugins/index.ts +7 -4
  275. package/src/vite-plugins/native.ts +14 -5
  276. package/src/vite-plugins/react-compiler.ts +44 -30
  277. package/src/vite-plugins/react-dom-prebundle.ts +4 -1
  278. package/src/vite-plugins/resource-init-module.ts +41 -38
  279. package/src/vite-plugins/resource-shared.ts +27 -14
  280. package/src/vite-plugins/resources.ts +154 -60
  281. package/src/vite-plugins/settings-worker-env.ts +7 -2
  282. package/src/vite-plugins/settings.ts +107 -42
  283. package/src/vite-plugins/strip-query.ts +7 -0
  284. package/src/vite-plugins/undeclared-library.ts +26 -16
  285. package/src/vite-plugins/virtual-module.ts +9 -2
  286. package/src/vitest-plugin.ts +1 -2
@@ -3,25 +3,30 @@ import type { SerializedWidget } from "@gtkx/mcp/internal";
3
3
 
4
4
  type WidgetIdResolver = (widget: Gtk.Widget) => string;
5
5
 
6
- export type WidgetFormatting = {
6
+ type WidgetFormatting = {
7
7
  formatRole(role: Gtk.AccessibleRole): string;
8
8
  getWidgetNodeText(widget: Gtk.Widget): string | null;
9
9
  };
10
10
 
11
- export const serializeWidget = (
11
+ const serializeWidget = (
12
12
  widget: Gtk.Widget,
13
- idFor: WidgetIdResolver,
13
+ resolveId: WidgetIdResolver,
14
14
  testing: WidgetFormatting,
15
+ maxDepth = Infinity,
15
16
  ): SerializedWidget => {
16
17
  const children: SerializedWidget[] = [];
17
- let child = widget.getFirstChild();
18
- while (child) {
19
- children.push(serializeWidget(child, idFor, testing));
20
- child = child.getNextSibling();
18
+
19
+ if (maxDepth > 0) {
20
+ let child = widget.getFirstChild();
21
+
22
+ while (child) {
23
+ children.push(serializeWidget(child, resolveId, testing, maxDepth - 1));
24
+ child = child.getNextSibling();
25
+ }
21
26
  }
22
27
 
23
28
  return {
24
- id: idFor(widget),
29
+ id: resolveId(widget),
25
30
  type: widget.constructor.name,
26
31
  role: testing.formatRole(widget.getAccessibleRole()),
27
32
  name: widget.getName() || null,
@@ -32,3 +37,5 @@ export const serializeWidget = (
32
37
  children,
33
38
  };
34
39
  };
40
+
41
+ export { serializeWidget, type WidgetFormatting };
@@ -1,32 +1,73 @@
1
1
  type TestingModule = typeof import("@gtkx/testing");
2
+ type TestingModuleLoader = () => Promise<TestingModule>;
2
3
 
3
- export type TestingModuleLoader = () => Promise<TestingModule>;
4
+ type TestingModuleCache = {
5
+ setLoader: (next: TestingModuleLoader | null) => void;
6
+ load: () => Promise<TestingModule>;
7
+ };
8
+
9
+ type TestingModuleState = {
10
+ loader: TestingModuleLoader;
11
+ testingModule: TestingModule | null;
12
+ testingLoadError: Error | null;
13
+ };
4
14
 
5
- const defaultLoader: TestingModuleLoader = () => import("@gtkx/testing");
15
+ const { setLoader: setTestingModuleLoader, load: loadTestingModule } = createTestingModuleCache();
6
16
 
7
- let loader: TestingModuleLoader = defaultLoader;
8
- let testingModule: TestingModule | null = null;
9
- let testingLoadError: Error | null = null;
17
+ function defaultLoader(): Promise<TestingModule> {
18
+ return import("@gtkx/testing");
19
+ }
10
20
 
11
- export const setTestingModuleLoader = (next: TestingModuleLoader | null): void => {
12
- loader = next ?? defaultLoader;
13
- testingModule = null;
14
- testingLoadError = null;
15
- };
21
+ function missingTestingPackageError(cause: unknown): Error {
22
+ return new Error(
23
+ "@gtkx/testing is not installed, install it to enable MCP widget interactions: " +
24
+ `pnpm add -D @gtkx/testing (import failed: ${String(cause)})`,
25
+ { cause },
26
+ );
27
+ }
16
28
 
17
- export const loadTestingModule = async (): Promise<TestingModule> => {
18
- if (testingModule) return testingModule;
19
- if (testingLoadError) throw testingLoadError;
29
+ function replaceLoader(state: TestingModuleState, next: TestingModuleLoader | null): void {
30
+ state.loader = next ?? defaultLoader;
31
+ state.testingModule = null;
32
+ state.testingLoadError = null;
33
+ }
20
34
 
35
+ async function importTestingModule(state: TestingModuleState): Promise<TestingModule> {
21
36
  try {
22
- testingModule = await loader();
23
- return testingModule;
24
- } catch (cause) {
25
- testingLoadError = new Error(
26
- "@gtkx/testing is not installed, install it to enable MCP widget interactions: " +
27
- `pnpm add -D @gtkx/testing (import failed: ${String(cause)})`,
28
- { cause },
29
- );
30
- throw testingLoadError;
37
+ state.testingModule = await state.loader();
38
+
39
+ return state.testingModule;
40
+ } catch (error) {
41
+ state.testingLoadError = missingTestingPackageError(error);
42
+ throw state.testingLoadError;
31
43
  }
32
- };
44
+ }
45
+
46
+ function loadCachedTestingModule(state: TestingModuleState): Promise<TestingModule> {
47
+ if (state.testingModule) {
48
+ return Promise.resolve(state.testingModule);
49
+ }
50
+
51
+ if (state.testingLoadError) {
52
+ return Promise.reject(state.testingLoadError);
53
+ }
54
+
55
+ return importTestingModule(state);
56
+ }
57
+
58
+ function createTestingModuleCache(): TestingModuleCache {
59
+ const state: TestingModuleState = {
60
+ loader: defaultLoader,
61
+ testingModule: null,
62
+ testingLoadError: null,
63
+ };
64
+
65
+ return {
66
+ setLoader: (next: TestingModuleLoader | null): void => {
67
+ replaceLoader(state, next);
68
+ },
69
+ load: (): Promise<TestingModule> => loadCachedTestingModule(state),
70
+ };
71
+ }
72
+
73
+ export { setTestingModuleLoader, loadTestingModule, type TestingModule, type TestingModuleLoader };
@@ -1,14 +1,15 @@
1
1
  import * as Gtk from "@gtkx/gi/gtk";
2
2
 
3
- export class WidgetRegistry {
4
- private idByWidget = new WeakMap<Gtk.Widget, string>();
3
+ class WidgetRegistry {
4
+ private idByWidget: WeakMap<Gtk.Widget, string> = new WeakMap();
5
5
  private nextId = 0;
6
- private widgetById = new Map<string, Gtk.Widget>();
6
+ private widgetById: Map<string, Gtk.Widget> = new Map();
7
7
  private toplevelWindows: Gtk.Window[] = [];
8
8
 
9
9
  refresh(): void {
10
10
  this.widgetById.clear();
11
11
  this.toplevelWindows = Gtk.Window.listToplevels() as Gtk.Window[];
12
+
12
13
  for (const window of this.toplevelWindows) {
13
14
  this.register(window);
14
15
  }
@@ -19,21 +20,24 @@ export class WidgetRegistry {
19
20
  }
20
21
 
21
22
  register(widget: Gtk.Widget): void {
22
- const id = this.idFor(widget);
23
+ const id = this.getOrCreateId(widget);
23
24
  this.widgetById.set(id, widget);
24
25
  let child = widget.getFirstChild();
26
+
25
27
  while (child) {
26
28
  this.register(child);
27
29
  child = child.getNextSibling();
28
30
  }
29
31
  }
30
32
 
31
- idFor(widget: Gtk.Widget): string {
33
+ getOrCreateId(widget: Gtk.Widget): string {
32
34
  let id = this.idByWidget.get(widget);
35
+
33
36
  if (!id) {
34
37
  id = String(this.nextId++);
35
38
  this.idByWidget.set(widget, id);
36
39
  }
40
+
37
41
  return id;
38
42
  }
39
43
 
@@ -41,3 +45,5 @@ export class WidgetRegistry {
41
45
  return this.widgetById.get(id);
42
46
  }
43
47
  }
48
+
49
+ export { WidgetRegistry };
@@ -0,0 +1,10 @@
1
+ import RefreshRuntime from "react-refresh/runtime";
2
+
3
+ const identity = (type: unknown): unknown => type;
4
+
5
+ RefreshRuntime.injectIntoGlobalHook(globalThis);
6
+
7
+ Object.assign(globalThis, {
8
+ $RefreshReg$: (): void => undefined,
9
+ $RefreshSig$: () => identity,
10
+ });
@@ -1,11 +1,8 @@
1
1
  import RefreshRuntime from "react-refresh/runtime";
2
+ import "./refresh-globals.js";
2
3
 
3
4
  type ComponentType = (...args: unknown[]) => unknown;
4
5
 
5
- RefreshRuntime.injectIntoGlobalHook(globalThis);
6
- globalThis.$RefreshReg$ = () => {};
7
- globalThis.$RefreshSig$ = () => (type: unknown) => type;
8
-
9
6
  /**
10
7
  * Builds the per-module `$RefreshReg$` and `$RefreshSig$` hooks that React Refresh
11
8
  * transformed code expects, scoping every registered component to the given module.
@@ -13,7 +10,7 @@ globalThis.$RefreshSig$ = () => (type: unknown) => type;
13
10
  * @param moduleId Identifier used to namespace registered component types so they are unique across modules.
14
11
  * @returns An object with `$RefreshReg$` and `$RefreshSig$` to install as module-local globals.
15
12
  */
16
- export function createModuleRegistration(moduleId: string): {
13
+ function createModuleRegistration(moduleId: string): {
17
14
  $RefreshReg$: (type: ComponentType, id: string) => void;
18
15
  $RefreshSig$: typeof RefreshRuntime.createSignatureFunctionForTransform;
19
16
  } {
@@ -32,21 +29,26 @@ export function createModuleRegistration(moduleId: string): {
32
29
  * @param moduleExports The module's export object to inspect.
33
30
  * @returns `true` when the module qualifies as a refresh boundary, `false` otherwise.
34
31
  */
35
- export function isRefreshBoundary(moduleExports: Record<string, unknown>): boolean {
36
- if (RefreshRuntime.isLikelyComponentType(moduleExports)) {
37
- return true;
38
- }
32
+ const isComponentExport = (key: string, value: unknown): boolean =>
33
+ key === "__esModule" || RefreshRuntime.isLikelyComponentType(value);
39
34
 
35
+ const areAllExportsComponents = (moduleExports: Record<string, unknown>): boolean => {
40
36
  for (const key in moduleExports) {
41
- if (key === "__esModule") {
42
- continue;
37
+ if (!isComponentExport(key, moduleExports[key])) {
38
+ return false;
43
39
  }
40
+ }
44
41
 
45
- const value = moduleExports[key];
42
+ return true;
43
+ };
46
44
 
47
- if (!RefreshRuntime.isLikelyComponentType(value)) {
48
- return false;
49
- }
45
+ function isRefreshBoundary(moduleExports: Record<string, unknown>): boolean {
46
+ if (RefreshRuntime.isLikelyComponentType(moduleExports)) {
47
+ return true;
48
+ }
49
+
50
+ if (!areAllExportsComponents(moduleExports)) {
51
+ return false;
50
52
  }
51
53
 
52
54
  return Object.keys(moduleExports).some((k) => k !== "__esModule");
@@ -55,6 +57,8 @@ export function isRefreshBoundary(moduleExports: Record<string, unknown>): boole
55
57
  /**
56
58
  * Applies all pending React Refresh updates, re-rendering components whose modules have changed.
57
59
  */
58
- export function performRefresh(): void {
60
+ function performRefresh(): void {
59
61
  RefreshRuntime.performReactRefresh();
60
62
  }
63
+
64
+ export { createModuleRegistration, isRefreshBoundary, performRefresh };
@@ -1,19 +1,22 @@
1
- import { execFileSync } from "node:child_process";
2
1
  import { formatChildProcessError } from "@gtkx/utils";
2
+ import { execFileSync } from "node:child_process";
3
3
  import { resolveCliTool } from "../internal/resolve-cli-tool.js";
4
4
 
5
5
  const SCHEMA_COMPILER = "glib-compile-schemas";
6
6
  const SCHEMA_COMPILE_TIMEOUT_MS = 30_000;
7
7
 
8
- export const compileSchemas = (dir: string): void => {
8
+ const compileSchemas = (dir: string): void => {
9
9
  try {
10
10
  execFileSync(resolveCliTool(SCHEMA_COMPILER), ["--strict", dir], {
11
11
  stdio: ["ignore", "pipe", "pipe"],
12
12
  timeout: SCHEMA_COMPILE_TIMEOUT_MS,
13
- encoding: "utf-8",
13
+ encoding: "utf8",
14
14
  });
15
15
  } catch (error) {
16
16
  const details = formatChildProcessError(error);
17
- throw new Error(`glib-compile-schemas failed for ${dir}${details ? `:\n${details}` : ""}`, { cause: error });
17
+ const suffix = details ? `:\n${details}` : "";
18
+ throw new Error(`glib-compile-schemas failed for ${dir}${suffix}`, { cause: error });
18
19
  }
19
20
  };
21
+
22
+ export { compileSchemas };
@@ -1,6 +1,6 @@
1
1
  import { XMLParser } from "fast-xml-parser";
2
2
 
3
- export type ParsedKey = {
3
+ type ParsedKey = {
4
4
  name: string;
5
5
  variantType: string | null;
6
6
  enumId: string | null;
@@ -9,23 +9,34 @@ export type ParsedKey = {
9
9
  summary: string | null;
10
10
  };
11
11
 
12
- export type ParsedSchema = {
12
+ type ParsedSchema = {
13
13
  id: string;
14
14
  path: string | null;
15
15
  keys: ParsedKey[];
16
16
  };
17
17
 
18
- export type ParsedSchemaFile = {
18
+ type ParsedSchemaFile = {
19
19
  fileName: string;
20
20
  schemas: ParsedSchema[];
21
21
  enums: Map<string, string[]>;
22
22
  flags: Map<string, string[]>;
23
23
  };
24
24
 
25
- export class SchemaParseError extends Error {}
26
-
27
25
  type RawNode = Record<string, unknown>;
28
26
 
27
+ type RawSchema = {
28
+ id: string;
29
+ path: string | null;
30
+ extendsId: string | null;
31
+ keys: ParsedKey[];
32
+ };
33
+
34
+ type MergeContext = {
35
+ byId: Map<string, RawSchema>;
36
+ merged: Map<string, ParsedKey>;
37
+ visited: Set<string>;
38
+ };
39
+
29
40
  const MULTI_TAGS: Set<string> = new Set(["schema", "key", "enum", "flags", "value", "choice"]);
30
41
 
31
42
  const PARSER = new XMLParser({
@@ -41,17 +52,23 @@ const isRawNode = (value: unknown): value is RawNode => typeof value === "object
41
52
 
42
53
  const attr = (node: RawNode, name: string): string | null => {
43
54
  const value = node[`@_${name}`];
55
+
44
56
  return typeof value === "string" ? value : null;
45
57
  };
46
58
 
47
59
  const text = (node: RawNode, name: string): string | null => {
48
60
  const value = node[name];
61
+
49
62
  return typeof value === "string" && value.length > 0 ? value : null;
50
63
  };
51
64
 
52
65
  const children = (node: RawNode, name: string): RawNode[] => {
53
66
  const value = node[name];
54
- if (!Array.isArray(value)) return [];
67
+
68
+ if (!Array.isArray(value)) {
69
+ return [];
70
+ }
71
+
55
72
  return value.filter(isRawNode);
56
73
  };
57
74
 
@@ -61,17 +78,26 @@ const parseNicks = (definition: RawNode): string[] =>
61
78
  .filter((nick): nick is string => nick !== null);
62
79
 
63
80
  const parseDefinitions = (schemalist: RawNode, tag: string): Map<string, string[]> => {
64
- const definitions = new Map<string, string[]>();
81
+ const definitions: Map<string, string[]> = new Map();
82
+
65
83
  for (const definition of children(schemalist, tag)) {
66
84
  const id = attr(definition, "id");
67
- if (id !== null) definitions.set(id, parseNicks(definition));
85
+
86
+ if (id !== null) {
87
+ definitions.set(id, parseNicks(definition));
88
+ }
68
89
  }
90
+
69
91
  return definitions;
70
92
  };
71
93
 
72
94
  const parseChoices = (key: RawNode): string[] => {
73
95
  const choices = key.choices;
74
- if (!isRawNode(choices)) return [];
96
+
97
+ if (!isRawNode(choices)) {
98
+ return [];
99
+ }
100
+
75
101
  return children(choices, "choice")
76
102
  .map((choice) => attr(choice, "value"))
77
103
  .filter((value): value is string => value !== null);
@@ -79,9 +105,11 @@ const parseChoices = (key: RawNode): string[] => {
79
105
 
80
106
  const parseKey = (key: RawNode, fileName: string): ParsedKey => {
81
107
  const name = attr(key, "name");
108
+
82
109
  if (name === null) {
83
110
  throw new SchemaParseError(`A <key> in ${fileName} has no name attribute`);
84
111
  }
112
+
85
113
  return {
86
114
  name,
87
115
  variantType: attr(key, "type"),
@@ -92,18 +120,13 @@ const parseKey = (key: RawNode, fileName: string): ParsedKey => {
92
120
  };
93
121
  };
94
122
 
95
- type RawSchema = {
96
- id: string;
97
- path: string | null;
98
- extendsId: string | null;
99
- keys: ParsedKey[];
100
- };
101
-
102
123
  const parseRawSchema = (schema: RawNode, fileName: string): RawSchema => {
103
124
  const id = attr(schema, "id");
125
+
104
126
  if (id === null) {
105
127
  throw new SchemaParseError(`A <schema> in ${fileName} has no id attribute`);
106
128
  }
129
+
107
130
  return {
108
131
  id,
109
132
  path: attr(schema, "path"),
@@ -112,35 +135,47 @@ const parseRawSchema = (schema: RawNode, fileName: string): RawSchema => {
112
135
  };
113
136
  };
114
137
 
138
+ const collectInheritedKeys = (context: MergeContext, current: RawSchema): void => {
139
+ if (context.visited.has(current.id)) {
140
+ return;
141
+ }
142
+
143
+ context.visited.add(current.id);
144
+ const parent = current.extendsId === null ? undefined : context.byId.get(current.extendsId);
145
+
146
+ if (parent !== undefined) {
147
+ collectInheritedKeys(context, parent);
148
+ }
149
+
150
+ for (const key of current.keys) {
151
+ context.merged.set(key.name, key);
152
+ }
153
+ };
154
+
115
155
  const mergeInheritedKeys = (schema: RawSchema, byId: Map<string, RawSchema>): ParsedKey[] => {
116
- const merged = new Map<string, ParsedKey>();
117
- const visited = new Set<string>();
118
- const collect = (current: RawSchema): void => {
119
- if (visited.has(current.id)) return;
120
- visited.add(current.id);
121
- if (current.extendsId !== null) {
122
- const parent = byId.get(current.extendsId);
123
- if (parent !== undefined) collect(parent);
124
- }
125
- for (const key of current.keys) merged.set(key.name, key);
126
- };
127
- collect(schema);
128
- return [...merged.values()];
156
+ const context: MergeContext = { byId, merged: new Map(), visited: new Set() };
157
+ collectInheritedKeys(context, schema);
158
+
159
+ return context.merged.values().toArray();
129
160
  };
130
161
 
131
- export const parseSchemaXml = (xml: string, fileName: string): ParsedSchemaFile => {
162
+ const parseSchemaXml = (xml: string, fileName: string): ParsedSchemaFile => {
132
163
  let document: unknown;
164
+
133
165
  try {
134
166
  document = PARSER.parse(xml);
135
167
  } catch (error) {
136
168
  throw new SchemaParseError(`Failed to parse ${fileName} as XML: ${String(error)}`, { cause: error });
137
169
  }
170
+
138
171
  if (!isRawNode(document) || !("schemalist" in document)) {
139
172
  throw new SchemaParseError(`${fileName} has no <schemalist> root element`);
140
173
  }
174
+
141
175
  const schemalist = isRawNode(document.schemalist) ? document.schemalist : {};
142
176
  const rawSchemas = children(schemalist, "schema").map((schema) => parseRawSchema(schema, fileName));
143
177
  const byId = new Map(rawSchemas.map((schema) => [schema.id, schema]));
178
+
144
179
  return {
145
180
  fileName,
146
181
  schemas: rawSchemas.map((schema) => ({
@@ -152,3 +187,7 @@ export const parseSchemaXml = (xml: string, fileName: string): ParsedSchemaFile
152
187
  flags: parseDefinitions(schemalist, "flags"),
153
188
  };
154
189
  };
190
+
191
+ class SchemaParseError extends Error {}
192
+
193
+ export { parseSchemaXml, SchemaParseError, type ParsedKey, type ParsedSchema, type ParsedSchemaFile };