@gtkx/codegen 1.0.0 → 1.2.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 (347) hide show
  1. package/README.md +1 -1
  2. package/dist/analysis/descriptor-render.d.ts +12 -15
  3. package/dist/analysis/descriptor-render.d.ts.map +1 -1
  4. package/dist/analysis/descriptor-render.js +59 -38
  5. package/dist/analysis/descriptor-render.js.map +1 -1
  6. package/dist/analysis/descriptor.d.ts +25 -10
  7. package/dist/analysis/descriptor.d.ts.map +1 -1
  8. package/dist/analysis/descriptor.js +37 -9
  9. package/dist/analysis/descriptor.js.map +1 -1
  10. package/dist/analysis/inheritance.d.ts +4 -2
  11. package/dist/analysis/inheritance.d.ts.map +1 -1
  12. package/dist/analysis/inheritance.js +80 -38
  13. package/dist/analysis/inheritance.js.map +1 -1
  14. package/dist/analysis/interface-conflicts.d.ts +25 -0
  15. package/dist/analysis/interface-conflicts.d.ts.map +1 -0
  16. package/dist/analysis/interface-conflicts.js +139 -0
  17. package/dist/analysis/interface-conflicts.js.map +1 -0
  18. package/dist/analysis/param-capability.d.ts.map +1 -1
  19. package/dist/analysis/param-capability.js +3 -4
  20. package/dist/analysis/param-capability.js.map +1 -1
  21. package/dist/analysis/param-structure.js +2 -2
  22. package/dist/analysis/param-structure.js.map +1 -1
  23. package/dist/analysis/ts-type.d.ts +3 -2
  24. package/dist/analysis/ts-type.d.ts.map +1 -1
  25. package/dist/analysis/ts-type.js +26 -26
  26. package/dist/analysis/ts-type.js.map +1 -1
  27. package/dist/analysis/type-key.d.ts +5 -0
  28. package/dist/analysis/type-key.d.ts.map +1 -0
  29. package/dist/analysis/type-key.js +9 -0
  30. package/dist/analysis/type-key.js.map +1 -0
  31. package/dist/analysis/type-shape.d.ts +3 -2
  32. package/dist/analysis/type-shape.d.ts.map +1 -1
  33. package/dist/analysis/type-shape.js +2 -1
  34. package/dist/analysis/type-shape.js.map +1 -1
  35. package/dist/compile.d.ts +14 -6
  36. package/dist/compile.d.ts.map +1 -1
  37. package/dist/compile.js +125 -24
  38. package/dist/compile.js.map +1 -1
  39. package/dist/docs/api-reference.d.ts +2 -0
  40. package/dist/docs/api-reference.d.ts.map +1 -1
  41. package/dist/docs/api-reference.js +11 -10
  42. package/dist/docs/api-reference.js.map +1 -1
  43. package/dist/docs/element-page.d.ts.map +1 -1
  44. package/dist/docs/element-page.js +5 -3
  45. package/dist/docs/element-page.js.map +1 -1
  46. package/dist/docs/pipeline.d.ts +1 -0
  47. package/dist/docs/pipeline.d.ts.map +1 -1
  48. package/dist/docs/pipeline.js +93 -19
  49. package/dist/docs/pipeline.js.map +1 -1
  50. package/dist/docs/render.d.ts +2 -1
  51. package/dist/docs/render.d.ts.map +1 -1
  52. package/dist/docs/render.js +5 -4
  53. package/dist/docs/render.js.map +1 -1
  54. package/dist/docs/symbol-page.d.ts.map +1 -1
  55. package/dist/docs/symbol-page.js +10 -9
  56. package/dist/docs/symbol-page.js.map +1 -1
  57. package/dist/fingerprint.d.ts +20 -3
  58. package/dist/fingerprint.d.ts.map +1 -1
  59. package/dist/fingerprint.js +56 -37
  60. package/dist/fingerprint.js.map +1 -1
  61. package/dist/gi.d.ts +1 -0
  62. package/dist/gi.d.ts.map +1 -1
  63. package/dist/gi.js +8 -2
  64. package/dist/gi.js.map +1 -1
  65. package/dist/gir/ancestry.d.ts +7 -1
  66. package/dist/gir/ancestry.d.ts.map +1 -1
  67. package/dist/gir/ancestry.js +9 -1
  68. package/dist/gir/ancestry.js.map +1 -1
  69. package/dist/gir/annotations.d.ts +1 -2
  70. package/dist/gir/annotations.d.ts.map +1 -1
  71. package/dist/gir/annotations.js +1 -1
  72. package/dist/gir/annotations.js.map +1 -1
  73. package/dist/gir/cursor-overrides.d.ts +8 -0
  74. package/dist/gir/cursor-overrides.d.ts.map +1 -0
  75. package/dist/gir/cursor-overrides.js +6 -0
  76. package/dist/gir/cursor-overrides.js.map +1 -0
  77. package/dist/gir/emittable.d.ts +7 -0
  78. package/dist/gir/emittable.d.ts.map +1 -0
  79. package/dist/gir/emittable.js +3 -0
  80. package/dist/gir/emittable.js.map +1 -0
  81. package/dist/gir/function.d.ts.map +1 -1
  82. package/dist/gir/function.js +28 -2
  83. package/dist/gir/function.js.map +1 -1
  84. package/dist/gir/gir-path.d.ts +1 -1
  85. package/dist/gir/gir-path.js +1 -1
  86. package/dist/gir/gir-path.js.map +1 -1
  87. package/dist/gir/library.d.ts +5 -1
  88. package/dist/gir/library.d.ts.map +1 -1
  89. package/dist/gir/library.js +9 -2
  90. package/dist/gir/library.js.map +1 -1
  91. package/dist/gir/namespace.d.ts +5 -1
  92. package/dist/gir/namespace.d.ts.map +1 -1
  93. package/dist/gir/namespace.js +4 -2
  94. package/dist/gir/namespace.js.map +1 -1
  95. package/dist/gir/parameter.d.ts +13 -1
  96. package/dist/gir/parameter.d.ts.map +1 -1
  97. package/dist/gir/parameter.js +1 -0
  98. package/dist/gir/parameter.js.map +1 -1
  99. package/dist/gir/parse.d.ts.map +1 -1
  100. package/dist/gir/parse.js +6 -6
  101. package/dist/gir/parse.js.map +1 -1
  102. package/dist/gir/primitives.js +1 -1
  103. package/dist/gir/primitives.js.map +1 -1
  104. package/dist/guards.d.ts +16 -0
  105. package/dist/guards.d.ts.map +1 -0
  106. package/dist/guards.js +11 -0
  107. package/dist/guards.js.map +1 -0
  108. package/dist/internal.d.ts +2 -0
  109. package/dist/internal.d.ts.map +1 -1
  110. package/dist/internal.js +2 -0
  111. package/dist/internal.js.map +1 -1
  112. package/dist/json.d.ts +3 -0
  113. package/dist/json.d.ts.map +1 -0
  114. package/dist/json.js +11 -0
  115. package/dist/json.js.map +1 -0
  116. package/dist/khronos/args.d.ts.map +1 -1
  117. package/dist/khronos/args.js +8 -6
  118. package/dist/khronos/args.js.map +1 -1
  119. package/dist/khronos/modules.d.ts.map +1 -1
  120. package/dist/khronos/modules.js +22 -7
  121. package/dist/khronos/modules.js.map +1 -1
  122. package/dist/khronos/parse.d.ts.map +1 -1
  123. package/dist/khronos/parse.js +3 -4
  124. package/dist/khronos/parse.js.map +1 -1
  125. package/dist/khronos/pipeline.d.ts +1 -1
  126. package/dist/khronos/pipeline.d.ts.map +1 -1
  127. package/dist/khronos/pipeline.js.map +1 -1
  128. package/dist/khronos/plan.d.ts +1 -7
  129. package/dist/khronos/plan.d.ts.map +1 -1
  130. package/dist/khronos/plan.js +1 -1
  131. package/dist/khronos/plan.js.map +1 -1
  132. package/dist/khronos/render.d.ts.map +1 -1
  133. package/dist/khronos/render.js +8 -4
  134. package/dist/khronos/render.js.map +1 -1
  135. package/dist/runner.d.ts +7 -1
  136. package/dist/runner.d.ts.map +1 -1
  137. package/dist/runner.js +21 -3
  138. package/dist/runner.js.map +1 -1
  139. package/dist/staging.d.ts +5 -0
  140. package/dist/staging.d.ts.map +1 -0
  141. package/dist/staging.js +57 -0
  142. package/dist/staging.js.map +1 -0
  143. package/dist/store/compile-store.d.ts +0 -1
  144. package/dist/store/compile-store.d.ts.map +1 -1
  145. package/dist/store/compile-store.js +7 -32
  146. package/dist/store/compile-store.js.map +1 -1
  147. package/dist/store/gi/callables.d.ts +6 -3
  148. package/dist/store/gi/callables.d.ts.map +1 -1
  149. package/dist/store/gi/callables.js +40 -30
  150. package/dist/store/gi/callables.js.map +1 -1
  151. package/dist/store/gi/callback.d.ts.map +1 -1
  152. package/dist/store/gi/callback.js +9 -5
  153. package/dist/store/gi/callback.js.map +1 -1
  154. package/dist/store/gi/class.d.ts.map +1 -1
  155. package/dist/store/gi/class.js +104 -44
  156. package/dist/store/gi/class.js.map +1 -1
  157. package/dist/store/gi/companion.d.ts.map +1 -1
  158. package/dist/store/gi/companion.js +6 -6
  159. package/dist/store/gi/companion.js.map +1 -1
  160. package/dist/store/gi/constant.d.ts.map +1 -1
  161. package/dist/store/gi/constant.js +11 -3
  162. package/dist/store/gi/constant.js.map +1 -1
  163. package/dist/store/gi/constructor-props.js +1 -1
  164. package/dist/store/gi/constructor-props.js.map +1 -1
  165. package/dist/store/gi/enum.d.ts.map +1 -1
  166. package/dist/store/gi/enum.js +29 -12
  167. package/dist/store/gi/enum.js.map +1 -1
  168. package/dist/store/gi/function.d.ts +1 -3
  169. package/dist/store/gi/function.d.ts.map +1 -1
  170. package/dist/store/gi/function.js +24 -18
  171. package/dist/store/gi/function.js.map +1 -1
  172. package/dist/store/gi/gtype-binding.d.ts.map +1 -1
  173. package/dist/store/gi/gtype-binding.js +4 -1
  174. package/dist/store/gi/gtype-binding.js.map +1 -1
  175. package/dist/store/gi/interface.d.ts.map +1 -1
  176. package/dist/store/gi/interface.js +34 -19
  177. package/dist/store/gi/interface.js.map +1 -1
  178. package/dist/store/gi/method.d.ts +7 -6
  179. package/dist/store/gi/method.d.ts.map +1 -1
  180. package/dist/store/gi/method.js +13 -24
  181. package/dist/store/gi/method.js.map +1 -1
  182. package/dist/store/gi/param-marshal.d.ts +3 -2
  183. package/dist/store/gi/param-marshal.d.ts.map +1 -1
  184. package/dist/store/gi/param-marshal.js +50 -5
  185. package/dist/store/gi/param-marshal.js.map +1 -1
  186. package/dist/store/gi/pipeline.d.ts.map +1 -1
  187. package/dist/store/gi/pipeline.js +21 -10
  188. package/dist/store/gi/pipeline.js.map +1 -1
  189. package/dist/store/gi/properties.d.ts +2 -1
  190. package/dist/store/gi/properties.d.ts.map +1 -1
  191. package/dist/store/gi/properties.js +6 -3
  192. package/dist/store/gi/properties.js.map +1 -1
  193. package/dist/store/gi/property-accessor.d.ts +10 -8
  194. package/dist/store/gi/property-accessor.d.ts.map +1 -1
  195. package/dist/store/gi/property-accessor.js +49 -91
  196. package/dist/store/gi/property-accessor.js.map +1 -1
  197. package/dist/store/gi/record-constructor.d.ts +8 -1
  198. package/dist/store/gi/record-constructor.d.ts.map +1 -1
  199. package/dist/store/gi/record-constructor.js +34 -13
  200. package/dist/store/gi/record-constructor.js.map +1 -1
  201. package/dist/store/gi/record-field-accessor.d.ts.map +1 -1
  202. package/dist/store/gi/record-field-accessor.js +20 -4
  203. package/dist/store/gi/record-field-accessor.js.map +1 -1
  204. package/dist/store/gi/record-layout.d.ts +6 -1
  205. package/dist/store/gi/record-layout.d.ts.map +1 -1
  206. package/dist/store/gi/record-layout.js +8 -1
  207. package/dist/store/gi/record-layout.js.map +1 -1
  208. package/dist/store/gi/record.d.ts.map +1 -1
  209. package/dist/store/gi/record.js +19 -8
  210. package/dist/store/gi/record.js.map +1 -1
  211. package/dist/store/gi/registration.js +2 -2
  212. package/dist/store/gi/registration.js.map +1 -1
  213. package/dist/store/gi/return-wrap.js +1 -1
  214. package/dist/store/gi/return-wrap.js.map +1 -1
  215. package/dist/store/gi/signal.d.ts +2 -1
  216. package/dist/store/gi/signal.d.ts.map +1 -1
  217. package/dist/store/gi/signal.js +55 -28
  218. package/dist/store/gi/signal.js.map +1 -1
  219. package/dist/store/gi/value-marshalable.d.ts.map +1 -1
  220. package/dist/store/gi/value-marshalable.js +2 -1
  221. package/dist/store/gi/value-marshalable.js.map +1 -1
  222. package/dist/store/gi/vtable.d.ts +4 -1
  223. package/dist/store/gi/vtable.d.ts.map +1 -1
  224. package/dist/store/gi/vtable.js +60 -17
  225. package/dist/store/gi/vtable.js.map +1 -1
  226. package/dist/store/gi-store.d.ts +1 -0
  227. package/dist/store/gi-store.d.ts.map +1 -1
  228. package/dist/store/gi-store.js +15 -20
  229. package/dist/store/gi-store.js.map +1 -1
  230. package/dist/store/jsx/generated-elements.d.ts +2 -1
  231. package/dist/store/jsx/generated-elements.d.ts.map +1 -1
  232. package/dist/store/jsx/generated-elements.js +13 -7
  233. package/dist/store/jsx/generated-elements.js.map +1 -1
  234. package/dist/store/jsx/jsx.js +3 -3
  235. package/dist/store/jsx/jsx.js.map +1 -1
  236. package/dist/store/jsx/metadata.d.ts.map +1 -1
  237. package/dist/store/jsx/metadata.js +3 -1
  238. package/dist/store/jsx/metadata.js.map +1 -1
  239. package/dist/store/jsx-store.d.ts.map +1 -1
  240. package/dist/store/jsx-store.js +3 -4
  241. package/dist/store/jsx-store.js.map +1 -1
  242. package/dist/store/resolve-store.d.ts +14 -3
  243. package/dist/store/resolve-store.d.ts.map +1 -1
  244. package/dist/store/resolve-store.js +98 -15
  245. package/dist/store/resolve-store.js.map +1 -1
  246. package/dist/store/store-fs.d.ts +9 -7
  247. package/dist/store/store-fs.d.ts.map +1 -1
  248. package/dist/store/store-fs.js +54 -25
  249. package/dist/store/store-fs.js.map +1 -1
  250. package/dist/store/store-links.d.ts +3 -0
  251. package/dist/store/store-links.d.ts.map +1 -0
  252. package/dist/store/store-links.js +39 -0
  253. package/dist/store/store-links.js.map +1 -0
  254. package/dist/writer/comparison-context.d.ts +4 -0
  255. package/dist/writer/comparison-context.d.ts.map +1 -0
  256. package/dist/writer/comparison-context.js +13 -0
  257. package/dist/writer/comparison-context.js.map +1 -0
  258. package/dist/writer/context.d.ts +3 -3
  259. package/dist/writer/context.d.ts.map +1 -1
  260. package/dist/writer/context.js +11 -6
  261. package/dist/writer/context.js.map +1 -1
  262. package/dist/writer/module.d.ts +13 -5
  263. package/dist/writer/module.d.ts.map +1 -1
  264. package/dist/writer/module.js +68 -11
  265. package/dist/writer/module.js.map +1 -1
  266. package/dist/xml.d.ts +8 -1
  267. package/dist/xml.d.ts.map +1 -1
  268. package/dist/xml.js +70 -1
  269. package/dist/xml.js.map +1 -1
  270. package/overrides/gobject/object.ts.ejs +2 -0
  271. package/package.json +14 -3
  272. package/src/analysis/descriptor-render.ts +106 -52
  273. package/src/analysis/descriptor.ts +76 -15
  274. package/src/analysis/inheritance.ts +134 -44
  275. package/src/analysis/interface-conflicts.ts +235 -0
  276. package/src/analysis/param-capability.ts +3 -6
  277. package/src/analysis/param-structure.ts +2 -2
  278. package/src/analysis/ts-type.ts +43 -28
  279. package/src/analysis/type-key.ts +14 -0
  280. package/src/analysis/type-shape.ts +5 -2
  281. package/src/compile.ts +192 -27
  282. package/src/docs/api-reference.ts +13 -10
  283. package/src/docs/element-page.ts +5 -2
  284. package/src/docs/pipeline.ts +140 -24
  285. package/src/docs/render.ts +7 -3
  286. package/src/docs/symbol-page.ts +12 -11
  287. package/src/fingerprint.ts +95 -47
  288. package/src/gi.ts +10 -2
  289. package/src/gir/ancestry.ts +17 -1
  290. package/src/gir/annotations.ts +1 -1
  291. package/src/gir/cursor-overrides.ts +12 -0
  292. package/src/gir/emittable.ts +8 -0
  293. package/src/gir/function.ts +45 -9
  294. package/src/gir/gir-path.ts +1 -1
  295. package/src/gir/library.ts +10 -2
  296. package/src/gir/namespace.ts +8 -2
  297. package/src/gir/parameter.ts +15 -0
  298. package/src/gir/parse.ts +10 -7
  299. package/src/gir/primitives.ts +1 -1
  300. package/src/guards.ts +23 -0
  301. package/src/internal.ts +2 -0
  302. package/src/json.ts +11 -0
  303. package/src/khronos/args.ts +9 -6
  304. package/src/khronos/modules.ts +25 -9
  305. package/src/khronos/parse.ts +3 -4
  306. package/src/khronos/pipeline.ts +1 -1
  307. package/src/khronos/plan.ts +0 -1
  308. package/src/khronos/render.ts +8 -6
  309. package/src/runner.ts +26 -4
  310. package/src/staging.ts +67 -0
  311. package/src/store/compile-store.ts +7 -38
  312. package/src/store/gi/callables.ts +60 -33
  313. package/src/store/gi/callback.ts +9 -9
  314. package/src/store/gi/class.ts +140 -53
  315. package/src/store/gi/companion.ts +7 -6
  316. package/src/store/gi/constant.ts +13 -3
  317. package/src/store/gi/constructor-props.ts +1 -1
  318. package/src/store/gi/enum.ts +36 -26
  319. package/src/store/gi/function.ts +30 -24
  320. package/src/store/gi/gtype-binding.ts +7 -2
  321. package/src/store/gi/interface.ts +39 -20
  322. package/src/store/gi/method.ts +26 -32
  323. package/src/store/gi/param-marshal.ts +77 -6
  324. package/src/store/gi/pipeline.ts +25 -15
  325. package/src/store/gi/properties.ts +8 -4
  326. package/src/store/gi/property-accessor.ts +73 -161
  327. package/src/store/gi/record-constructor.ts +53 -21
  328. package/src/store/gi/record-field-accessor.ts +26 -4
  329. package/src/store/gi/record-layout.ts +11 -1
  330. package/src/store/gi/record.ts +20 -12
  331. package/src/store/gi/registration.ts +2 -2
  332. package/src/store/gi/return-wrap.ts +1 -1
  333. package/src/store/gi/signal.ts +66 -42
  334. package/src/store/gi/value-marshalable.ts +3 -1
  335. package/src/store/gi/vtable.ts +97 -20
  336. package/src/store/gi-store.ts +26 -31
  337. package/src/store/jsx/generated-elements.ts +17 -8
  338. package/src/store/jsx/jsx.ts +3 -3
  339. package/src/store/jsx/metadata.ts +4 -1
  340. package/src/store/jsx-store.ts +3 -4
  341. package/src/store/resolve-store.ts +133 -18
  342. package/src/store/store-fs.ts +82 -35
  343. package/src/store/store-links.ts +49 -0
  344. package/src/writer/comparison-context.ts +18 -0
  345. package/src/writer/context.ts +12 -6
  346. package/src/writer/module.ts +101 -14
  347. package/src/xml.ts +103 -1
@@ -1,30 +1,109 @@
1
1
  import { ImportsBuilder } from "./imports.js";
2
2
 
3
- type DeclaredType = {
3
+ type Declaration = {
4
4
  name: string;
5
- owner: string;
5
+ code: string;
6
+ owner?: string | undefined;
6
7
  };
7
8
 
9
+ type ExportedDeclaration = {
10
+ name: string;
11
+ spaces: string[];
12
+ };
13
+
14
+ const EXPORTED_DECLARATION = /^export (?:abstract )?(class|const|enum|function|interface|type) ([A-Za-z_$][\w$]*)/mu;
15
+
16
+ const SPACES_BY_KEYWORD: Map<string, string[]> = new Map([
17
+ ["class", ["value", "type"]],
18
+ ["const", ["value"]],
19
+ ["enum", ["value", "type"]],
20
+ ["function", ["value"]],
21
+ ["interface", []],
22
+ ["type", ["type"]],
23
+ ]);
24
+
25
+ const getExportedDeclaration = (code: string): ExportedDeclaration | undefined => {
26
+ const match = EXPORTED_DECLARATION.exec(code);
27
+ const spaces = SPACES_BY_KEYWORD.get(match?.[1] ?? "");
28
+ const name = match?.[2];
29
+
30
+ return spaces === undefined || name === undefined ? undefined : { name, spaces };
31
+ };
32
+
33
+ const exportedNameText = (exported: ExportedDeclaration | undefined): string =>
34
+ exported === undefined ? "no name at all" : `'${exported.name}'`;
35
+
8
36
  class ModuleBuilder {
9
37
  private bindings: string[] = [];
10
38
  private bindingNames: Set<string> = new Set();
11
39
  private hoistedDescriptors: Map<string, string> = new Map();
40
+ private claimedSpaces: Set<string> = new Set();
12
41
  private declarations: string[] = [];
42
+ private declaredNames: Set<string> = new Set();
13
43
  private declaredTypes: Map<string, string> = new Map();
14
44
  private registrations: string[] = [];
45
+ private requiredNames: Set<string> = new Set();
15
46
  public imports: ImportsBuilder = new ImportsBuilder();
16
47
 
17
- private claimTypeName(declared: DeclaredType): void {
18
- const owner = this.declaredTypes.get(declared.name);
48
+ private claimSpace(name: string, space: string): void {
49
+ const key = `${space} ${name}`;
50
+
51
+ if (this.claimedSpaces.has(key)) {
52
+ throw new Error(
53
+ `The generated module declares '${name}' twice in its ${space} space. ` +
54
+ "A module may declare each exported name once per declaration space.",
55
+ );
56
+ }
57
+
58
+ this.claimedSpaces.add(key);
59
+ }
60
+
61
+ private claimExportedName(declaration: Declaration): void {
62
+ const exported = getExportedDeclaration(declaration.code);
63
+ const spaces = exported?.name === declaration.name ? exported.spaces : undefined;
64
+
65
+ if (spaces === undefined) {
66
+ throw new Error(
67
+ `The declaration recorded as '${declaration.name}' exports ${exportedNameText(exported)}. ` +
68
+ "A declaration may only be recorded under the name its own code exports.",
69
+ );
70
+ }
71
+
72
+ for (const space of spaces) {
73
+ this.claimSpace(declaration.name, space);
74
+ }
75
+ }
76
+
77
+ private claimTypeName(declaration: Declaration): void {
78
+ const { name, owner } = declaration;
79
+
80
+ if (owner === undefined) {
81
+ return;
82
+ }
83
+
84
+ const previous = this.declaredTypes.get(name);
19
85
 
20
- if (owner !== undefined && owner !== declared.owner) {
86
+ if (previous !== undefined && previous !== owner) {
21
87
  throw new Error(
22
- `The generated type '${declared.name}' is declared for both ${owner} and ${declared.owner}. ` +
88
+ `The generated type '${name}' is declared for both ${previous} and ${owner}. ` +
23
89
  "Rename one of them, or drop the namespace from the configuration.",
24
90
  );
25
91
  }
26
92
 
27
- this.declaredTypes.set(declared.name, declared.owner);
93
+ this.declaredTypes.set(name, owner);
94
+ }
95
+
96
+ private verifyRegistrations(): void {
97
+ const missing = [...this.requiredNames].filter((name) => !this.declaredNames.has(name));
98
+
99
+ if (missing.length === 0) {
100
+ return;
101
+ }
102
+
103
+ throw new Error(
104
+ `The generated module registers ${missing.join(", ")}, which it never declares. ` +
105
+ "A registration may only name a symbol whose declaration the same module emits.",
106
+ );
28
107
  }
29
108
 
30
109
  appendBinding(code: string, name?: string): void {
@@ -53,19 +132,27 @@ class ModuleBuilder {
53
132
  return name;
54
133
  }
55
134
 
56
- appendDeclaration(code: string, declared?: DeclaredType): void {
57
- if (declared !== undefined) {
58
- this.claimTypeName(declared);
59
- }
135
+ appendDeclaration(declaration: Declaration): void {
136
+ this.claimExportedName(declaration);
137
+ this.claimTypeName(declaration);
138
+ this.declaredNames.add(declaration.name);
139
+ this.declarations.push(declaration.code);
140
+ }
60
141
 
61
- this.declarations.push(code);
142
+ hasExports(): boolean {
143
+ return this.declaredNames.size > 0;
62
144
  }
63
145
 
64
- appendRegistration(code: string): void {
146
+ appendRegistration(code: string, requires: string[] = []): void {
147
+ for (const name of requires) {
148
+ this.requiredNames.add(name);
149
+ }
150
+
65
151
  this.registrations.push(code);
66
152
  }
67
153
 
68
154
  toSource(): string {
155
+ this.verifyRegistrations();
69
156
  const sections: string[] = [];
70
157
  const importsBlock = this.imports.toSource();
71
158
 
@@ -89,4 +176,4 @@ class ModuleBuilder {
89
176
  }
90
177
  }
91
178
 
92
- export { type DeclaredType, ModuleBuilder };
179
+ export { type Declaration, ModuleBuilder };
package/src/xml.ts CHANGED
@@ -1,4 +1,17 @@
1
+ import { errorMessage, isRecord } from "@gtkx/utils";
1
2
  import { type X2jOptions, XMLParser } from "fast-xml-parser";
3
+ import { SyntaxValidator } from "fast-xml-validator";
4
+ import { readFileSync } from "node:fs";
5
+
6
+ type XmlFileInput = {
7
+ parser: XMLParser;
8
+ label: string;
9
+ path: string;
10
+ malformedRemedy?: string;
11
+ };
12
+
13
+ const HIGHEST_CONTROL_CODE = 0x1F;
14
+ const LEGAL_CONTROL_CODES: Set<number> = new Set([0x09, 0x0A, 0x0D]);
2
15
 
3
16
  const createXmlParser = (options: Partial<X2jOptions>): XMLParser =>
4
17
  new XMLParser({
@@ -9,4 +22,93 @@ const createXmlParser = (options: Partial<X2jOptions>): XMLParser =>
9
22
  ...options,
10
23
  });
11
24
 
12
- export { createXmlParser };
25
+ const xmlFileError = (input: XmlFileInput, action: string, error: unknown): Error =>
26
+ new Error(`Cannot ${action} the ${input.label} at ${input.path}: ${errorMessage(error)}`, { cause: error });
27
+
28
+ const readXmlFile = (input: XmlFileInput): string => {
29
+ try {
30
+ return readFileSync(input.path, "utf8");
31
+ } catch (error) {
32
+ throw xmlFileError(input, "read", error);
33
+ }
34
+ };
35
+
36
+ const isLocated = (line: unknown, col: unknown): boolean =>
37
+ typeof line === "number" && typeof col === "number" && (line > 1 || col > 1);
38
+
39
+ const positionSuffix = (error: unknown): string => {
40
+ if (!isRecord(error)) {
41
+ return "";
42
+ }
43
+
44
+ const { line, col } = error;
45
+
46
+ if (!isLocated(line, col)) {
47
+ return "";
48
+ }
49
+
50
+ return ` (line ${String(line)}, column ${String(col)})`;
51
+ };
52
+
53
+ const isIllegalControlCode = (code: number): boolean =>
54
+ code <= HIGHEST_CONTROL_CODE && !LEGAL_CONTROL_CODES.has(code);
55
+
56
+ const escapedCode = (code: number): string => `[U+${code.toString(16).toUpperCase().padStart(4, "0")}]`;
57
+
58
+ const escapeControlCodes = (text: string): string => {
59
+ let escaped = "";
60
+
61
+ for (const character of text) {
62
+ const code = character.codePointAt(0) ?? 0;
63
+ escaped += isIllegalControlCode(code) ? escapedCode(code) : character;
64
+ }
65
+
66
+ return escaped;
67
+ };
68
+
69
+ const offendingLine = (xml: string, error: unknown): string => {
70
+ const line = isRecord(error) ? error.line : undefined;
71
+
72
+ if (typeof line !== "number") {
73
+ return "";
74
+ }
75
+
76
+ const text = xml.split("\n")[line - 1];
77
+
78
+ if (text === undefined) {
79
+ return "";
80
+ }
81
+
82
+ return `\n ${escapeControlCodes(text.replace(/^[ \t]+/, "").replace(/\r$/, ""))}`;
83
+ };
84
+
85
+ const remedySuffix = (input: XmlFileInput, xml: string, error: unknown): string => {
86
+ const remedy = input.malformedRemedy;
87
+
88
+ return remedy === undefined ? "" : `${offendingLine(xml, error)}\n${remedy}`;
89
+ };
90
+
91
+ const assertWellFormed = (input: XmlFileInput, xml: string): void => {
92
+ try {
93
+ SyntaxValidator.validate(xml);
94
+ } catch (error) {
95
+ throw new Error(
96
+ `The ${input.label} at ${input.path} is not well-formed XML: ` +
97
+ `${errorMessage(error)}${positionSuffix(error)}${remedySuffix(input, xml, error)}`,
98
+ { cause: error },
99
+ );
100
+ }
101
+ };
102
+
103
+ const parseXmlFile = (input: XmlFileInput): unknown => {
104
+ const xml = readXmlFile(input);
105
+ assertWellFormed(input, xml);
106
+
107
+ try {
108
+ return input.parser.parse(xml);
109
+ } catch (error) {
110
+ throw xmlFileError(input, "parse", error);
111
+ }
112
+ };
113
+
114
+ export { createXmlParser, parseXmlFile };