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

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 +8 -8
  18. package/dist/codegen/run-codegen.d.ts.map +1 -1
  19. package/dist/codegen/run-codegen.js +72 -43
  20. package/dist/codegen/run-codegen.js.map +1 -1
  21. package/dist/codegen/store-resolver.d.ts +8 -5
  22. package/dist/codegen/store-resolver.d.ts.map +1 -1
  23. package/dist/codegen/store-resolver.js +26 -6
  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 +22 -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 +3 -2
  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 +94 -54
  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 +2 -1
  218. package/dist/vite-plugins/undeclared-library.d.ts.map +1 -1
  219. package/dist/vite-plugins/undeclared-library.js +8 -5
  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 +38 -25
  233. package/src/codegen/report.ts +10 -2
  234. package/src/codegen/run-codegen.ts +135 -60
  235. package/src/codegen/store-resolver.ts +56 -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 +36 -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 +6 -3
  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 +153 -59
  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 +21 -12
  285. package/src/vite-plugins/virtual-module.ts +9 -2
  286. package/src/vitest-plugin.ts +1 -2
@@ -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 };
@@ -7,132 +7,123 @@ const GTKX_ENV_MODULE_HEADER = `/**
7
7
  * \`gtkx build\`; do not edit.
8
8
  */`;
9
9
 
10
- const VARIANT_TS_TYPES: Record<string, string> = {
11
- b: "boolean",
12
- i: "number",
13
- u: "number",
14
- x: "number",
15
- t: "number",
16
- d: "number",
17
- s: "string",
18
- as: "string[]",
19
- };
20
-
21
- const VARIANT_FALLBACK_TS_TYPE = `import("@gtkx/gi/glib").Variant`;
22
-
23
10
  const ENUM_KIND = "enum";
24
11
  const FLAGS_KIND = "flags";
25
12
 
26
13
  const toJsStringLiteral = (value: string): string =>
27
- JSON.stringify(value).replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");
14
+ JSON.stringify(value).replaceAll("\u{2028}", String.raw`\u2028`).replaceAll("\u{2029}", String.raw`\u2029`);
28
15
 
29
- const exportNameFor = (schemaId: string): string => schemaId.replaceAll(".", "_");
16
+ const getExportName = (schemaId: string): string => schemaId.replaceAll(".", "_");
30
17
 
31
- const unionOf = (values: string[]): string => values.map(toJsStringLiteral).join(" | ");
32
-
33
- const tsTypeForKey = (key: ParsedKey, file: ParsedSchemaFile): string => {
18
+ const runtimeKindForKey = (key: ParsedKey): string => {
34
19
  if (key.enumId !== null) {
35
- const nicks = file.enums.get(key.enumId);
36
- return nicks !== undefined && nicks.length > 0 ? unionOf(nicks) : "string";
20
+ return ENUM_KIND;
37
21
  }
22
+
38
23
  if (key.flagsId !== null) {
39
- const nicks = file.flags.get(key.flagsId);
40
- return nicks !== undefined && nicks.length > 0 ? `(${unionOf(nicks)})[]` : "string[]";
41
- }
42
- if (key.variantType === "s" && key.choices.length > 0) {
43
- return unionOf(key.choices);
24
+ return FLAGS_KIND;
44
25
  }
45
- return VARIANT_TS_TYPES[key.variantType ?? ""] ?? VARIANT_FALLBACK_TS_TYPE;
46
- };
47
26
 
48
- const runtimeKindForKey = (key: ParsedKey): string => {
49
- if (key.enumId !== null) return ENUM_KIND;
50
- if (key.flagsId !== null) return FLAGS_KIND;
51
27
  return key.variantType ?? "";
52
28
  };
53
29
 
54
- const runtimeKeysFor = (schema: ParsedSchema): string => {
30
+ const getRuntimeKeys = (schema: ParsedSchema): string => {
55
31
  const entries = schema.keys.map(
56
32
  (key) => `${toJsStringLiteral(key.name)}: ${toJsStringLiteral(runtimeKindForKey(key))}`,
57
33
  );
34
+
58
35
  return `{ ${entries.join(", ")} }`;
59
36
  };
60
37
 
61
- export const renderRuntimeModule = (file: ParsedSchemaFile): string => {
38
+ const renderRuntimeModule = (file: ParsedSchemaFile): string => {
62
39
  const lines: string[] = [];
63
- file.schemas.forEach((schema, index) => {
64
- const keysName = `keys_${index}`;
40
+
41
+ for (const [index, schema] of file.schemas.entries()) {
42
+ const keysName = `keys_${String(index)}`;
65
43
  const id = toJsStringLiteral(schema.id);
66
- lines.push(`const ${keysName} = ${runtimeKeysFor(schema)};`);
44
+ lines.push(`const ${keysName} = ${getRuntimeKeys(schema)};`);
45
+
67
46
  if (schema.path === null) {
68
47
  lines.push(
69
- `export const ${exportNameFor(schema.id)} = { id: ${id}, keys: ${keysName}, at: (path) => ({ id: ${id}, path, keys: ${keysName} }) };`,
48
+ `export const ${getExportName(schema.id)} = { id: ${id}, keys: ${keysName}, ` +
49
+ `at: (path) => ({ id: ${id}, path, keys: ${keysName} }) };`,
70
50
  );
71
51
  } else {
72
- lines.push(`export const ${exportNameFor(schema.id)} = { id: ${id}, path: null, keys: ${keysName} };`);
52
+ lines.push(`export const ${getExportName(schema.id)} = { id: ${id}, path: null, keys: ${keysName} };`);
73
53
  }
74
- });
54
+ }
55
+
75
56
  const first = file.schemas[0];
57
+
76
58
  if (first !== undefined) {
77
- lines.push(`export default ${exportNameFor(first.id)};`);
59
+ lines.push(`export default ${getExportName(first.id)};`);
78
60
  }
61
+
79
62
  return lines.join("\n");
80
63
  };
81
64
 
82
- const sanitizeSummary = (summary: string): string => summary.replaceAll("*/", "*\\/");
65
+ const sanitizeSummary = (summary: string): string => summary.replaceAll("*/", String.raw`*\/`);
83
66
 
84
- const interfaceNameFor = (schemaId: string, usedNames: Set<string>): string => {
67
+ const allocateInterfaceName = (schemaId: string, usedNames: Set<string>): string => {
85
68
  const base = `${schemaId
86
69
  .split(/[^a-zA-Z0-9]+/)
87
70
  .filter((part) => part.length > 0)
88
- .map(upperFirst)
71
+ .map((part) => upperFirst(part))
89
72
  .join("")}Keys`;
73
+
90
74
  let name = base;
91
75
  let suffix = 2;
76
+
92
77
  while (usedNames.has(name)) {
93
- name = `${base}${suffix}`;
78
+ name = `${base}${String(suffix)}`;
94
79
  suffix += 1;
95
80
  }
81
+
96
82
  usedNames.add(name);
83
+
97
84
  return name;
98
85
  };
99
86
 
100
- const renderKeysType = (name: string, schema: ParsedSchema, file: ParsedSchemaFile): string[] => {
87
+ const renderKeysType = (name: string, schema: ParsedSchema): string[] => {
101
88
  const lines = [` type ${name} = {`];
89
+
102
90
  for (const key of schema.keys) {
103
91
  if (key.summary !== null) {
104
92
  lines.push(` /** ${sanitizeSummary(key.summary)} */`);
105
93
  }
106
- lines.push(` ${toJsStringLiteral(key.name)}: ${tsTypeForKey(key, file)};`);
94
+
95
+ lines.push(` ${toJsStringLiteral(key.name)}: ${toJsStringLiteral(runtimeKindForKey(key))};`);
107
96
  }
97
+
108
98
  lines.push(" };");
99
+
109
100
  return lines;
110
101
  };
111
102
 
112
103
  const boundRefTypeLines = (interfaceName: string, indent: string): string[] => [
113
104
  `${indent}id: string;`,
114
105
  `${indent}path: string | null;`,
115
- `${indent}keys: { [P in keyof ${interfaceName}]: string };`,
116
- `${indent}__keys__?: ${interfaceName};`,
106
+ `${indent}keys: ${interfaceName};`,
117
107
  ];
118
108
 
119
109
  const renderSchemaConst = (schema: ParsedSchema, interfaceName: string): string[] => {
120
- const exportName = exportNameFor(schema.id);
110
+ const exportName = getExportName(schema.id);
111
+
121
112
  if (schema.path !== null) {
122
113
  return [
123
114
  ` const ${exportName}: {`,
124
115
  ` id: ${toJsStringLiteral(schema.id)};`,
125
- ...boundRefTypeLines(interfaceName, " ").slice(1),
116
+ ...boundRefTypeLines(interfaceName, " ".repeat(8)).slice(1),
126
117
  " };",
127
118
  ];
128
119
  }
120
+
129
121
  return [
130
122
  ` const ${exportName}: {`,
131
123
  ` id: ${JSON.stringify(schema.id)};`,
132
- ` keys: { [P in keyof ${interfaceName}]: string };`,
133
- ` __keys__?: ${interfaceName};`,
124
+ ` keys: ${interfaceName};`,
134
125
  " at(path: string): {",
135
- ...boundRefTypeLines(interfaceName, " "),
126
+ ...boundRefTypeLines(interfaceName, " ".repeat(12)),
136
127
  " };",
137
128
  " };",
138
129
  ];
@@ -141,27 +132,36 @@ const renderSchemaConst = (schema: ParsedSchema, interfaceName: string): string[
141
132
  const renderFileModule = (file: ParsedSchemaFile, usedNames: Set<string>): string[] => {
142
133
  const lines = [`declare module "${file.fileName}" {`];
143
134
  const exportNames: string[] = [];
144
- file.schemas.forEach((schema, index) => {
145
- if (index > 0) lines.push("");
146
- const interfaceName = interfaceNameFor(schema.id, usedNames);
147
- lines.push(...renderKeysType(interfaceName, schema, file));
148
- lines.push("");
149
- lines.push(...renderSchemaConst(schema, interfaceName));
150
- exportNames.push(exportNameFor(schema.id));
151
- });
152
- if (exportNames.length > 0) {
153
- lines.push("");
154
- lines.push(` export { ${exportNames.join(", ")} };`);
155
- lines.push(` export default ${exportNames[0]};`);
135
+
136
+ for (const [index, schema] of file.schemas.entries()) {
137
+ if (index > 0) {
138
+ lines.push("");
139
+ }
140
+
141
+ const interfaceName = allocateInterfaceName(schema.id, usedNames);
142
+ lines.push(...renderKeysType(interfaceName, schema), "", ...renderSchemaConst(schema, interfaceName));
143
+ exportNames.push(getExportName(schema.id));
156
144
  }
145
+
146
+ const [firstExport] = exportNames;
147
+
148
+ if (firstExport !== undefined) {
149
+ lines.push("", ` export { ${exportNames.join(", ")} };`, ` export default ${firstExport};`);
150
+ }
151
+
157
152
  lines.push("}");
153
+
158
154
  return lines;
159
155
  };
160
156
 
161
- export const renderEnvModule = (files: ParsedSchemaFile[]): string => {
162
- const usedNames = new Set<string>();
157
+ const renderEnvModule = (files: ParsedSchemaFile[]): string => {
158
+ const usedNames: Set<string> = new Set();
159
+
163
160
  const blocks = files
164
161
  .filter((file) => file.schemas.length > 0)
165
162
  .map((file) => renderFileModule(file, usedNames).join("\n"));
163
+
166
164
  return `${[GTKX_ENV_MODULE_HEADER, ...blocks].join("\n\n")}\n`;
167
165
  };
166
+
167
+ export { renderRuntimeModule, renderEnvModule };
@@ -1,41 +1,46 @@
1
+ import { sortStrings, warn } from "@gtkx/utils";
1
2
  import { createHash } from "node:crypto";
2
3
  import { copyFileSync, type Dirent, mkdirSync, mkdtempSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
3
4
  import { tmpdir } from "node:os";
4
5
  import { dirname, join, relative } from "node:path";
5
- import { sortStrings, warn } from "@gtkx/utils";
6
6
  import { DATA_IMPORT_PREFIX } from "../internal/data-dir.js";
7
7
  import { removeTempDir } from "../internal/staging-dir.js";
8
8
  import { compileSchemas } from "./compile.js";
9
9
  import { type ParsedSchemaFile, parseSchemaXml, SchemaParseError } from "./parser.js";
10
10
  import { renderEnvModule } from "./render.js";
11
11
 
12
- export const SCHEMA_SUFFIX = ".gschema.xml";
13
-
14
- export const prependSchemaDir = (dir: string, existing: string | undefined): string => {
15
- if (existing === undefined || existing.length === 0) return dir;
16
- if (existing.split(":").includes(dir)) return existing;
17
- return `${dir}:${existing}`;
12
+ type SchemaEnvResult = {
13
+ path: string;
14
+ written: boolean;
18
15
  };
19
16
 
17
+ const SCHEMA_SUFFIX = ".gschema.xml";
20
18
  const STAGED_NAME_LENGTH = 16;
21
19
 
20
+ const prependSchemaDir = (dir: string, existing: string | undefined): string => {
21
+ if (existing === undefined || existing.length === 0) {
22
+ return dir;
23
+ }
24
+
25
+ if (existing.split(":").includes(dir)) {
26
+ return existing;
27
+ }
28
+
29
+ return `${dir}:${existing}`;
30
+ };
31
+
22
32
  const stagedSchemaName = (filePath: string): string =>
23
33
  `${createHash("sha1").update(filePath).digest("hex").slice(0, STAGED_NAME_LENGTH)}${SCHEMA_SUFFIX}`;
24
34
 
25
- export const stageSchema = (dir: string, filePath: string): void => {
35
+ const stageSchema = (dir: string, filePath: string): void => {
26
36
  copyFileSync(filePath, join(dir, stagedSchemaName(filePath)));
27
37
  };
28
38
 
29
39
  const toForwardSlashes = (value: string): string => value.replaceAll(/[/\\]/g, "/");
30
40
 
31
- const moduleSpecifierFor = (dataDirAbs: string, filePath: string): string =>
41
+ const getModuleSpecifier = (dataDirAbs: string, filePath: string): string =>
32
42
  `${DATA_IMPORT_PREFIX}/${toForwardSlashes(relative(dataDirAbs, filePath))}`;
33
43
 
34
- type SchemaEnvResult = {
35
- path: string;
36
- written: boolean;
37
- };
38
-
39
44
  const readVisibleEntries = (dir: string): Dirent[] => {
40
45
  try {
41
46
  return readdirSync(dir, { withFileTypes: true }).filter((entry) => !entry.name.startsWith("."));
@@ -46,70 +51,119 @@ const readVisibleEntries = (dir: string): Dirent[] => {
46
51
 
47
52
  const isSchemaFile = (entry: Dirent): boolean => entry.isFile() && entry.name.endsWith(SCHEMA_SUFFIX);
48
53
 
49
- export const findSchemaFiles = (dataDir: string): string[] => {
50
- const found: string[] = [];
51
- const walk = (dir: string): void => {
52
- for (const entry of readVisibleEntries(dir)) {
53
- const full = join(dir, entry.name);
54
- if (entry.isDirectory()) {
55
- walk(full);
56
- } else if (isSchemaFile(entry)) {
57
- found.push(full);
58
- }
54
+ const collectSchemaFiles = (dir: string, found: string[]): void => {
55
+ for (const entry of readVisibleEntries(dir)) {
56
+ const full = join(dir, entry.name);
57
+
58
+ if (entry.isDirectory()) {
59
+ collectSchemaFiles(full, found);
60
+ } else if (isSchemaFile(entry)) {
61
+ found.push(full);
59
62
  }
60
- };
61
- walk(dataDir);
63
+ }
64
+ };
65
+
66
+ const findSchemaFiles = (dataDir: string): string[] => {
67
+ const found: string[] = [];
68
+ collectSchemaFiles(dataDir, found);
69
+
62
70
  return sortStrings(found);
63
71
  };
64
72
 
65
- export const stageAndCompileProjectSchemas = (root: string, dataDir: string | null): string | null => {
66
- if (dataDir === null) return null;
73
+ const stageAndCompileProjectSchemas = (root: string, dataDir: string | null): string | null => {
74
+ if (dataDir === null) {
75
+ return null;
76
+ }
77
+
67
78
  const schemaFiles = findSchemaFiles(join(root, dataDir));
68
- if (schemaFiles.length === 0) return null;
79
+
80
+ if (schemaFiles.length === 0) {
81
+ return null;
82
+ }
69
83
 
70
84
  const dir = mkdtempSync(join(tmpdir(), "gtkx-schemas-"));
71
- for (const filePath of schemaFiles) stageSchema(dir, filePath);
85
+
86
+ for (const filePath of schemaFiles) {
87
+ stageSchema(dir, filePath);
88
+ }
89
+
72
90
  compileSchemas(dir);
73
- process.once("exit", () => removeTempDir(dir));
91
+
92
+ process.once("exit", () => {
93
+ removeTempDir(dir);
94
+ });
74
95
 
75
96
  return dir;
76
97
  };
77
98
 
78
- export const schemaEnvPath = (rootDir: string): string => join(rootDir, "node_modules", ".gtkx", "env.d.ts");
99
+ const schemaEnvPath = (rootDir: string): string => join(rootDir, "node_modules", ".gtkx", "env.d.ts");
100
+
101
+ const parseSchemaFileOrWarn = (filePath: string, dataDirAbs: string): ParsedSchemaFile | null => {
102
+ const specifier = getModuleSpecifier(dataDirAbs, filePath);
103
+
104
+ try {
105
+ return parseSchemaXml(readFileSync(filePath, "utf8"), specifier);
106
+ } catch (error) {
107
+ if (!(error instanceof SchemaParseError)) {
108
+ throw error;
109
+ }
110
+
111
+ warn(`Skipping ${filePath} in schema type generation: ${error.message}`);
112
+
113
+ return null;
114
+ }
115
+ };
79
116
 
80
117
  const parseProjectSchemas = (schemaFiles: string[], dataDirAbs: string): ParsedSchemaFile[] => {
81
118
  const parsed: ParsedSchemaFile[] = [];
119
+
82
120
  for (const filePath of schemaFiles) {
83
- const specifier = moduleSpecifierFor(dataDirAbs, filePath);
84
- try {
85
- parsed.push(parseSchemaXml(readFileSync(filePath, "utf-8"), specifier));
86
- } catch (error) {
87
- if (!(error instanceof SchemaParseError)) throw error;
88
- warn(`Skipping ${filePath} in schema type generation: ${error.message}`);
121
+ const result = parseSchemaFileOrWarn(filePath, dataDirAbs);
122
+
123
+ if (result !== null) {
124
+ parsed.push(result);
89
125
  }
90
126
  }
127
+
91
128
  return parsed;
92
129
  };
93
130
 
94
- const writeIfChanged = (path: string, content: string): boolean => {
95
- let existing: string | null = null;
131
+ const readFileOrNull = (path: string): string | null => {
96
132
  try {
97
- existing = readFileSync(path, "utf-8");
133
+ return readFileSync(path, "utf8");
98
134
  } catch {
99
- existing = null;
135
+ return null;
100
136
  }
101
- if (existing === content) return false;
137
+ };
138
+
139
+ const didWriteChanges = (path: string, content: string): boolean => {
140
+ if (readFileOrNull(path) === content) {
141
+ return false;
142
+ }
143
+
102
144
  mkdirSync(dirname(path), { recursive: true });
103
145
  writeFileSync(path, content);
146
+
104
147
  return true;
105
148
  };
106
149
 
107
- export const emitSchemaEnv = (rootDir: string, dataDir: string | null): SchemaEnvResult => {
150
+ const emitSchemaEnv = (rootDir: string, dataDir: string | null): SchemaEnvResult => {
108
151
  const dataDirAbs = dataDir === null ? null : join(rootDir, dataDir);
109
152
  const schemaFiles = dataDirAbs === null ? [] : findSchemaFiles(dataDirAbs);
110
153
  const parsed = dataDirAbs === null ? [] : parseProjectSchemas(schemaFiles, dataDirAbs);
111
154
  const content = renderEnvModule(parsed);
112
155
  const path = schemaEnvPath(rootDir);
113
- const written = writeIfChanged(path, content);
114
- return { path, written };
156
+ const isWritten = didWriteChanges(path, content);
157
+
158
+ return { path, written: isWritten };
159
+ };
160
+
161
+ export {
162
+ SCHEMA_SUFFIX,
163
+ prependSchemaDir,
164
+ stageSchema,
165
+ findSchemaFiles,
166
+ stageAndCompileProjectSchemas,
167
+ schemaEnvPath,
168
+ emitSchemaEnv,
115
169
  };
@@ -1,4 +1,4 @@
1
- export const ASSET_EXTENSIONS = [
1
+ const ASSET_EXTENSIONS = [
2
2
  "png",
3
3
  "jpg",
4
4
  "jpeg",
@@ -23,6 +23,7 @@ export const ASSET_EXTENSIONS = [
23
23
  "gpa",
24
24
  ];
25
25
 
26
- export const ASSET_RE = new RegExp(String.raw`\.(?:${ASSET_EXTENSIONS.join("|")})$`, "i");
26
+ const ASSET_RE = new RegExp(String.raw`\.(?:${ASSET_EXTENSIONS.join("|")})$`, "i");
27
+ const ASSET_PATH_RE = new RegExp(String.raw`\.(?:${ASSET_EXTENSIONS.join("|")})(?:\?.*)?$`, "i");
27
28
 
28
- export const ASSET_PATH_RE = new RegExp(String.raw`\.(?:${ASSET_EXTENSIONS.join("|")})(?:\?.*)?$`, "i");
29
+ export { ASSET_EXTENSIONS, ASSET_RE, ASSET_PATH_RE };