@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,5 +1,6 @@
1
1
  import type { GirAnnotations } from "./annotations.js";
2
2
  import type { ParseContext } from "./type-id.js";
3
+ import { type CursorParameterNames, PARAMETERS_MISSING_ARRAY_EXTENT } from "./cursor-overrides.js";
3
4
  import { PARAMETERS_MISSING_NULLABLE_ANNOTATION } from "./nullable-overrides.js";
4
5
  import { type GirParameter, type GirReturnValue, parameterFromNode, parseCallable } from "./parameter.js";
5
6
  import { attr, getChild, type RawNode } from "./parse.js";
@@ -50,18 +51,53 @@ const relaxMissingNullable = (fn: GirFunction): GirFunction => {
50
51
  return fn;
51
52
  };
52
53
 
54
+ const parameterIndexFor = (parameters: GirParameter[], name: string): number => {
55
+ const index = parameters.findIndex((parameter) => parameter.name === name);
56
+
57
+ if (index === -1) {
58
+ throw new Error(`A cursor correction names a parameter the GIR does not declare: ${name}`);
59
+ }
60
+
61
+ return index;
62
+ };
63
+
64
+ const bindCursorParameters = (parameters: GirParameter[], corrections: CursorParameterNames[]): void => {
65
+ for (const names of corrections) {
66
+ const parameter = parameters[parameterIndexFor(parameters, names.cursor)];
67
+
68
+ if (parameter !== undefined) {
69
+ parameter.cursor = {
70
+ baseIndex: parameterIndexFor(parameters, names.base),
71
+ lengthIndex: parameterIndexFor(parameters, names.length),
72
+ };
73
+ }
74
+ }
75
+ };
76
+
77
+ const bindMissingArrayExtent = (fn: GirFunction): GirFunction => {
78
+ const corrections = fn.cIdentifier === undefined ? undefined : PARAMETERS_MISSING_ARRAY_EXTENT.get(fn.cIdentifier);
79
+
80
+ if (corrections !== undefined) {
81
+ bindCursorParameters(fn.parameters, corrections);
82
+ }
83
+
84
+ return fn;
85
+ };
86
+
53
87
  const functionFromNode = (node: RawNode, context: ParseContext): GirFunction => {
54
88
  const instanceNode = getChild(getChild(node, "parameters"), "instance-parameter");
55
89
 
56
- return relaxMissingNullable({
57
- ...parseCallable(node, context),
58
- name: attr(node, "shadows") ?? attr(node, "name") ?? "",
59
- cIdentifier: attr(node, "c:identifier"),
60
- movedTo: attr(node, "moved-to"),
61
- shadowedBy: attr(node, "shadowed-by"),
62
- finishFunc: attr(node, "glib:finish-func"),
63
- instance: instanceNode === undefined ? undefined : parameterFromNode(instanceNode, context),
64
- });
90
+ return bindMissingArrayExtent(
91
+ relaxMissingNullable({
92
+ ...parseCallable(node, context),
93
+ name: attr(node, "shadows") ?? attr(node, "name") ?? "",
94
+ cIdentifier: attr(node, "c:identifier"),
95
+ movedTo: attr(node, "moved-to"),
96
+ shadowedBy: attr(node, "shadowed-by"),
97
+ finishFunc: attr(node, "glib:finish-func"),
98
+ instance: instanceNode === undefined ? undefined : parameterFromNode(instanceNode, context),
99
+ }),
100
+ );
65
101
  };
66
102
 
67
103
  export { functionFromNode, type GirFunction };
@@ -7,7 +7,7 @@ const SYSTEM_GIR_PATH = "/usr/share/gir-1.0";
7
7
  /**
8
8
  * The directories to search for `.gir` files, in precedence order: the config's own `girPath`, then
9
9
  * `GTKX_GIR_PATH`, then `/usr/share/gir-1.0`, then the girdir pkg-config reports for
10
- * gobject-introspection-1.0. Duplicates are dropped, and the last two are included only when they exist.
10
+ * gobject-introspection-1.0. Duplicates are dropped, and the system directories are included only when they exist.
11
11
  *
12
12
  * @throws If pkg-config is installed but fails while being queried.
13
13
  */
@@ -84,10 +84,12 @@ class Library {
84
84
  *
85
85
  * @param libraries GIR identifiers to load, such as `Gtk-4.0`.
86
86
  * @param girPath Directories searched, in order, for each `.gir` file.
87
+ * @param isByteArrayTyped Whether byte sequences render as `Uint8Array` rather than `number[]`.
87
88
  * @returns A library holding every namespace that was parsed.
88
89
  */
89
- static load(libraries: string[], girPath: string[]): Library {
90
+ static load(libraries: string[], girPath: string[], isByteArrayTyped = false): Library {
90
91
  const library = new Library();
92
+ library.isByteArrayTypedValue = isByteArrayTyped;
91
93
  this.drive(library, libraries, girPath);
92
94
 
93
95
  return library;
@@ -99,6 +101,7 @@ class Library {
99
101
  private nsIdByName: Map<string, number> = new Map();
100
102
  private typeTables: TypeTable[] = [];
101
103
  private girFilesValue: string[] = [];
104
+ private isByteArrayTypedValue = false;
102
105
 
103
106
  /** Creates a library with no namespaces loaded; {@link Library.load} builds a populated one. */
104
107
  constructor() {
@@ -294,7 +297,7 @@ class Library {
294
297
  seen.add(namespaceName);
295
298
  const path = locateGirFile(identifier, girPath);
296
299
  girFiles.push(path);
297
- const header = parseNamespaceHeader(readRepositoryNode(path));
300
+ const header = parseNamespaceHeader(readRepositoryNode(path), path);
298
301
  const shell = this.registerNamespace(header);
299
302
  discovered.push({ header, shell });
300
303
 
@@ -328,6 +331,11 @@ class Library {
328
331
  return this.namespacesByName;
329
332
  }
330
333
 
334
+ /** Whether byte sequences render as `Uint8Array` rather than `number[]`. */
335
+ public get isByteArrayTyped(): boolean {
336
+ return this.isByteArrayTypedValue;
337
+ }
338
+
331
339
  /** Paths of the `.gir` files that were read, in the order they were discovered. */
332
340
  public get girFiles(): string[] {
333
341
  return this.girFilesValue;
@@ -44,6 +44,8 @@ type GirNamespace = {
44
44
  id: number;
45
45
  /** Namespace name as it appears in the GIR, such as `Gtk`. */
46
46
  name: string;
47
+ /** Path of the `.gir` file the namespace was parsed from, named by every diagnostic the namespace causes. */
48
+ girFile: string;
47
49
  /** The `shared-library` attribute verbatim: the objects to load symbols from, separated by commas. */
48
50
  sharedLibrary: string | undefined;
49
51
  /** Prefixes stripped off C identifiers to derive export names, such as `gtk`. */
@@ -78,6 +80,8 @@ type NamespaceInclude = {
78
80
  type NamespaceHeader = {
79
81
  /** Namespace name as it appears in the GIR, such as `Gtk`. */
80
82
  name: string;
83
+ /** Path of the `.gir` file the header was read from. */
84
+ girFile: string;
81
85
  /** The `shared-library` attribute verbatim: the objects to load symbols from, separated by commas. */
82
86
  sharedLibrary: string | undefined;
83
87
  /** Prefixes stripped off C identifiers to derive export names, such as `gtk`. */
@@ -90,7 +94,7 @@ type NamespaceHeader = {
90
94
 
91
95
  const namespaceDirectory = (namespace: Pick<GirNamespace, "name">): string => namespace.name.toLowerCase();
92
96
 
93
- const parseNamespaceHeader = (repositoryNode: RawNode): NamespaceHeader => {
97
+ const parseNamespaceHeader = (repositoryNode: RawNode, path: string): NamespaceHeader => {
94
98
  const includes = getChildren(repositoryNode, "include").map<NamespaceInclude>((include) => ({
95
99
  name: nameAttr(include),
96
100
  version: attr(include, "version") ?? "",
@@ -99,11 +103,12 @@ const parseNamespaceHeader = (repositoryNode: RawNode): NamespaceHeader => {
99
103
  const namespaceNode = getChildren(repositoryNode, "namespace")[0];
100
104
 
101
105
  if (namespaceNode === undefined) {
102
- throw new Error("GIR repository has no <namespace> child");
106
+ throw new Error(`GIR file at ${path} has no <namespace> child in its <repository>`);
103
107
  }
104
108
 
105
109
  return {
106
110
  name: nameAttr(namespaceNode),
111
+ girFile: path,
107
112
  sharedLibrary: attr(namespaceNode, "shared-library"),
108
113
  cSymbolPrefixes: splitPrefixes(attr(namespaceNode, "c:symbol-prefixes")),
109
114
  includes,
@@ -114,6 +119,7 @@ const parseNamespaceHeader = (repositoryNode: RawNode): NamespaceHeader => {
114
119
  const createNamespaceShell = (header: NamespaceHeader, id: number): GirNamespace => ({
115
120
  id,
116
121
  name: header.name,
122
+ girFile: header.girFile,
117
123
  sharedLibrary: header.sharedLibrary,
118
124
  cSymbolPrefixes: header.cSymbolPrefixes,
119
125
  classes: [],
@@ -23,6 +23,17 @@ type ParameterTransfer = "none" | "full" | "container";
23
23
  */
24
24
  type CallbackScope = "call" | "notified" | "async" | "forever";
25
25
 
26
+ /**
27
+ * Bounds of an out parameter that points into the buffer of a sibling parameter instead of to an
28
+ * array of its own, which GIR describes as an array with no extent of any kind.
29
+ */
30
+ type GirCursorBounds = {
31
+ /** Position of the parameter holding the buffer the pointer points into. */
32
+ baseIndex: number;
33
+ /** Position of the parameter carrying that buffer's element count. */
34
+ lengthIndex: number;
35
+ };
36
+
26
37
  /** A parameter of a {@link GirCallable}, with the annotations that decide how it is marshalled. */
27
38
  type GirParameter = {
28
39
  /** The name GIR gives the parameter. */
@@ -51,6 +62,8 @@ type GirParameter = {
51
62
  destroyIndex: number | undefined;
52
63
  /** Whether the parameter is the `...` varargs marker. */
53
64
  isVarargs: boolean;
65
+ /** Where the buffer is that the parameter points into, when it carries a cursor rather than an array. */
66
+ cursor: GirCursorBounds | undefined;
54
67
  };
55
68
 
56
69
  /** What a {@link GirCallable} hands back, with the annotations that decide how it is marshalled. */
@@ -118,6 +131,7 @@ const parameterFromNode = (node: RawNode, context: ParseContext): GirParameter =
118
131
  closureIndex: intAttr(node, "closure"),
119
132
  destroyIndex: intAttr(node, "destroy"),
120
133
  isVarargs: getChild(node, "varargs") !== undefined,
134
+ cursor: undefined,
121
135
  });
122
136
 
123
137
  const isOutParameter = (parameter: GirParameter): boolean =>
@@ -163,6 +177,7 @@ export {
163
177
  isInoutParameter,
164
178
  parseCallable,
165
179
  type ParameterTransfer,
180
+ type GirCursorBounds,
166
181
  type GirParameter,
167
182
  type GirReturnValue,
168
183
  type GirCallable,
package/src/gir/parse.ts CHANGED
@@ -1,9 +1,15 @@
1
- import { readFileSync } from "node:fs";
2
- import { createXmlParser } from "../xml.js";
1
+ import { createXmlParser, parseXmlFile } from "../xml.js";
3
2
 
4
3
  /** An element of the parsed GIR XML: attributes under `@_`-prefixed keys, children under their tag names. */
5
4
  type RawNode = Record<string, unknown>;
6
5
 
6
+ const GIR_LABEL = "GIR file";
7
+
8
+ const GIR_MALFORMED_REMEDY =
9
+ "No XML parser can read this file, so report it to whoever ships it. To get past it now, name the " +
10
+ "libraries you need explicitly in `libraries` and leave this one out, or put a corrected copy of the " +
11
+ "file in a directory listed in `girPath`, which is searched ahead of the system location.";
12
+
7
13
  const MULTI_TAGS: Set<string> = new Set([
8
14
  "include",
9
15
  "class",
@@ -42,11 +48,8 @@ function renameReservedTag(tag: string): string {
42
48
  return RESERVED_TAG_RENAMES.get(tag) ?? tag;
43
49
  }
44
50
 
45
- const parseGirFile = (path: string): RawNode => {
46
- const xml = readFileSync(path, "utf8");
47
-
48
- return PARSER.parse(xml) as RawNode;
49
- };
51
+ const parseGirFile = (path: string): RawNode =>
52
+ parseXmlFile({ parser: PARSER, label: GIR_LABEL, path, malformedRemedy: GIR_MALFORMED_REMEDY }) as RawNode;
50
53
 
51
54
  const attr = (node: RawNode | undefined, name: string): string | undefined => {
52
55
  if (node === undefined) {
@@ -110,7 +110,7 @@ const PRIMITIVE_TS_TYPE: Record<PrimitiveCategory, string> = {
110
110
  float64: "number",
111
111
  string: "string",
112
112
  unichar: "string",
113
- pointer: "number",
113
+ pointer: "bigint",
114
114
  };
115
115
 
116
116
  const primitiveCategory = (name: string): PrimitiveCategory | undefined => PRIMITIVE_BY_NAME.get(name);
package/src/guards.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { isRecord } from "@gtkx/utils";
2
+
3
+ type Guard<T> = (value: unknown) => value is T;
4
+ type OptionalGuard<T> = Guard<T | undefined> & { __optional__: true };
5
+ type FieldGuard<T> = [undefined] extends [T] ? OptionalGuard<NonNullable<T>> : Guard<T>;
6
+ type FieldGuards<T> = { [K in keyof Required<T>]: FieldGuard<T[K]> };
7
+
8
+ const isBoolean = (value: unknown): value is boolean => typeof value === "boolean";
9
+ const isNumber = (value: unknown): value is number => typeof value === "number";
10
+ const isString = (value: unknown): value is string => typeof value === "string";
11
+
12
+ const arrayGuard = <T>(isEntry: Guard<T>): Guard<T[]> =>
13
+ (value: unknown): value is T[] => Array.isArray(value) && value.every((entry: unknown) => isEntry(entry));
14
+
15
+ const optionalGuard = <T>(isPresent: Guard<T>): OptionalGuard<T> =>
16
+ Object.assign((value: unknown): value is T | undefined => value === undefined || isPresent(value), {
17
+ __optional__: true as const,
18
+ });
19
+
20
+ const hasFields = <T extends object>(value: unknown, guards: FieldGuards<T>): value is T =>
21
+ isRecord(value) && Object.entries(guards).every(([name, guard]) => (guard as Guard<unknown>)(value[name]));
22
+
23
+ export { arrayGuard, hasFields, isBoolean, isNumber, isString, optionalGuard };
package/src/internal.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export { writeDocs } from "./docs/pipeline.js";
2
2
  export { runGlCodegen } from "./runner.js";
3
+ export { getShadowingStorePaths, sweepProjectStaging } from "./store/resolve-store.js";
4
+ export { ensureStoreLinks } from "./store/store-links.js";
package/src/json.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { readFileSync } from "node:fs";
2
+
3
+ const readJsonFile = (path: string): unknown => {
4
+ try {
5
+ return JSON.parse(readFileSync(path, "utf8")) as unknown;
6
+ } catch {
7
+ return undefined;
8
+ }
9
+ };
10
+
11
+ export { readJsonFile };
@@ -2,6 +2,7 @@ import { toCamelIdentifier } from "@gtkx/utils";
2
2
  import type { GlCommand, GlParam } from "./model.js";
3
3
  import type { CommandPlan, GlScalar, ParamPlan, ReturnPlan } from "./plan.js";
4
4
  import {
5
+ type ArrayLayout,
5
6
  tArray,
6
7
  tBoolean,
7
8
  tBuffer,
@@ -50,6 +51,7 @@ type BuildArgOptions = {
50
51
  };
51
52
 
52
53
  const OUT_PLAN_KINDS: Set<string> = new Set(["ref-out", "ref-array-out", "ref-fixed-out", "string-out"]);
54
+ const NUMERIC_LAYOUT: ArrayLayout = { isBytes: false };
53
55
 
54
56
  const scalarAliasOrGroup = (scalar: GlScalar, group: string | undefined): string =>
55
57
  group !== undefined && scalar.isGroupBearing === true ? group : scalar.tsAlias;
@@ -122,12 +124,13 @@ const buildInArg = (options: BuildArgOptions, name: string, track: (alias: strin
122
124
  return inArg(name, "string", tString("borrowed"));
123
125
  }
124
126
  case "string-array-in": {
125
- return inArg(name, "string[]", tArray(tString("borrowed")));
127
+ return inArg(name, "string[]", tArray(tString("borrowed"), undefined, NUMERIC_LAYOUT));
126
128
  }
127
129
  case "array-in": {
128
130
  track(scalarAliasOrGroup(plan.scalar, param.group));
131
+ const descriptor = tArray(plan.scalar.descriptor, undefined, NUMERIC_LAYOUT);
129
132
 
130
- return inArg(name, arrayInTsType(plan.scalar, param.group), tArray(plan.scalar.descriptor));
133
+ return inArg(name, arrayInTsType(plan.scalar, param.group), descriptor);
131
134
  }
132
135
  case "buffer": {
133
136
  return inArg(name, `ArrayBufferView | ${track("GLintptr")} | null`, tBuffer);
@@ -136,7 +139,7 @@ const buildInArg = (options: BuildArgOptions, name: string, track: (alias: strin
136
139
  return inArg(name, track("GLintptr"), tUint64);
137
140
  }
138
141
  case "byte-offset-array": {
139
- return inArg(name, `${track("GLintptr")}[]`, tArray(tUint64));
142
+ return inArg(name, `${track("GLintptr")}[]`, tArray(tUint64, undefined, NUMERIC_LAYOUT));
140
143
  }
141
144
  case "ref-array-out":
142
145
  case "ref-fixed-out":
@@ -165,14 +168,14 @@ const outArgFields = (options: OutArgFieldsOptions): OutArgFields => {
165
168
  return {
166
169
  seed: `const ${cellName} = { value: new Array<number>(${lenIdentifier}).fill(0) };`,
167
170
  tsType: `${track(scalarAliasOrGroup(plan.scalar, param.group))}[]`,
168
- descriptor: tRef(tSizedArray(plan.scalar.descriptor, sizeIndex)),
171
+ descriptor: tRef(tSizedArray(plan.scalar.descriptor, sizeIndex, undefined, NUMERIC_LAYOUT)),
169
172
  };
170
173
  }
171
174
  case "ref-fixed-out": {
172
175
  return {
173
176
  seed: `const ${cellName} = { value: new Array<number>(${String(plan.length)}).fill(0) };`,
174
177
  tsType: `${track(plan.scalar.tsAlias)}[]`,
175
- descriptor: tRef(tFixedArray(plan.scalar.descriptor, plan.length)),
178
+ descriptor: tRef(tFixedArray(plan.scalar.descriptor, plan.length, undefined, NUMERIC_LAYOUT)),
176
179
  };
177
180
  }
178
181
  case "string-out": {
@@ -204,7 +207,7 @@ const buildOutArg = (options: BuildArgOptions, track: (alias: string) => string)
204
207
  throw new Error(`Parameter index ${String(index)} out of range on ${command.name}`);
205
208
  }
206
209
 
207
- const cellName = `out${String(outIndex)}`;
210
+ const cellName = `__out${String(outIndex)}`;
208
211
  const cell: OutArgCell = { isOut: true, cellName, paramIndex: index };
209
212
 
210
213
  return { ...cell, ...outArgFields({ command, plan, param, cellName, track }) };
@@ -76,7 +76,10 @@ const renderEnumsModule = (tokens: EnumRow[], docs: GlDocContext): string => {
76
76
  const builder = new ModuleBuilder();
77
77
 
78
78
  for (const row of tokens) {
79
- builder.appendDeclaration(`${enumJsDoc(row, docs)}\nexport const ${row.exportName} = ${row.literal};`);
79
+ builder.appendDeclaration({
80
+ name: row.exportName,
81
+ code: `${enumJsDoc(row, docs)}\nexport const ${row.exportName} = ${row.literal};`,
82
+ });
80
83
  }
81
84
 
82
85
  return `${generatedHeader(docs)}\n\n${builder.toSource()}`;
@@ -135,7 +138,11 @@ const appendScalarAliases = (builder: ModuleBuilder, docs: GlDocContext): void =
135
138
  }
136
139
 
137
140
  seen.add(scalar.tsAlias);
138
- builder.appendDeclaration(`${scalarJsDoc(scalar.tsAlias, docs)}\nexport type ${scalar.tsAlias} = number;`);
141
+
142
+ builder.appendDeclaration({
143
+ name: scalar.tsAlias,
144
+ code: `${scalarJsDoc(scalar.tsAlias, docs)}\nexport type ${scalar.tsAlias} = number;`,
145
+ });
139
146
  }
140
147
  };
141
148
 
@@ -143,7 +150,10 @@ const appendGroupAliases = (builder: ModuleBuilder, groupAliases: Map<string, st
143
150
  const sorted = sortStringsBy(groupAliases.entries(), ([key]) => key);
144
151
 
145
152
  for (const [group, base] of sorted) {
146
- builder.appendDeclaration(`${groupAliasJsDoc(group, base, docs)}\nexport type ${group} = ${base};`);
153
+ builder.appendDeclaration({
154
+ name: group,
155
+ code: `${groupAliasJsDoc(group, base, docs)}\nexport type ${group} = ${base};`,
156
+ });
147
157
  }
148
158
  };
149
159
 
@@ -151,12 +161,18 @@ const renderTypesModule = (groupAliases: Map<string, string>, docs: GlDocContext
151
161
  const builder = new ModuleBuilder();
152
162
  builder.imports.addNamed("@gtkx/native", "ExternalObject", true);
153
163
  builder.imports.addNamed("@gtkx/native", "Handle", true);
154
- builder.appendDeclaration(`${syncJsDoc(docs)}\nexport type GLsync = ExternalObject<Handle>;`);
155
164
 
156
- builder.appendDeclaration(
157
- "/** An opaque native pointer handle (e.g. a `glMapBufferRange` mapping). */\n" +
158
- "export type GLpointer = ExternalObject<Handle>;",
159
- );
165
+ builder.appendDeclaration({
166
+ name: "GLsync",
167
+ code: `${syncJsDoc(docs)}\nexport type GLsync = ExternalObject<Handle>;`,
168
+ });
169
+
170
+ builder.appendDeclaration({
171
+ name: "GLpointer",
172
+ code:
173
+ "/** An opaque native pointer handle (e.g. a `glMapBufferRange` mapping). */\n" +
174
+ "export type GLpointer = ExternalObject<Handle>;",
175
+ });
160
176
 
161
177
  appendScalarAliases(builder, docs);
162
178
  appendGroupAliases(builder, groupAliases, docs);
@@ -184,7 +200,7 @@ const appendCommandBindings = (builder: ModuleBuilder, commands: RenderedCommand
184
200
 
185
201
  const appendCommandDeclarations = (builder: ModuleBuilder, commands: RenderedCommand[]): void => {
186
202
  for (const command of commands) {
187
- builder.appendDeclaration(command.declaration);
203
+ builder.appendDeclaration({ name: command.exportName, code: command.declaration });
188
204
  }
189
205
  };
190
206
 
@@ -1,10 +1,10 @@
1
- import { readFileSync } from "node:fs";
2
- import { createXmlParser } from "../xml.js";
1
+ import { createXmlParser, parseXmlFile } from "../xml.js";
3
2
 
4
3
  type OrderedNode = Record<string, unknown>;
5
4
 
6
5
  const ATTRIBUTES_KEY = ":@";
7
6
  const TEXT_KEY = "#text";
7
+ const REGISTRY_LABEL = "OpenGL registry";
8
8
 
9
9
  const PARSER = createXmlParser({
10
10
  trimValues: false,
@@ -12,8 +12,7 @@ const PARSER = createXmlParser({
12
12
  });
13
13
 
14
14
  const parseRegistryFile = (path: string): OrderedNode[] => {
15
- const xml = readFileSync(path, "utf8");
16
- const documentNodes = PARSER.parse(xml) as OrderedNode[];
15
+ const documentNodes = parseXmlFile({ parser: PARSER, label: REGISTRY_LABEL, path }) as OrderedNode[];
17
16
 
18
17
  for (const node of documentNodes) {
19
18
  if (nodeTag(node) === "registry") {
@@ -387,4 +387,4 @@ const generateGlModules = (options: GlGenerationOptions): GlGenerationResult =>
387
387
  };
388
388
  };
389
389
 
390
- export { generateGlModules, type GlGenerationReport, type GlGenerationResult };
390
+ export { generateGlModules, type GlGenerationReport };
@@ -328,7 +328,6 @@ const planCommand = (command: GlCommand, policy: GlPlanPolicy): CommandPlan => {
328
328
 
329
329
  export {
330
330
  GL_SCALARS,
331
- parseCType,
332
331
  planCommand,
333
332
  type GlScalar,
334
333
  type ParamPlan,
@@ -114,7 +114,7 @@ const returnStatements = (call: string, returned: EmittedReturn, outs: OutArg[])
114
114
  return [`${call};`, tail];
115
115
  }
116
116
 
117
- return [`const result = ${call};`, `return [${returned.expr("result")}, ${outValues.join(", ")}];`];
117
+ return [`const __result = ${call};`, `return [${returned.expr("__result")}, ${outValues.join(", ")}];`];
118
118
  };
119
119
 
120
120
  const renderCommand = (
@@ -135,7 +135,7 @@ const renderCommand = (
135
135
  const seeds = outs.map((out) => out.seed);
136
136
  const bindExpression = glBind(command.name, descriptors, returned.descriptor);
137
137
  const isInline = plan.params.some((paramPlan) => paramPlan.kind === "string-out");
138
- const bindingName = isInline ? "binding" : toCamelIdentifier(command.name);
138
+ const bindingName = isInline ? "__binding" : toCamelIdentifier(command.name);
139
139
 
140
140
  const body = [
141
141
  ...seeds,
@@ -252,12 +252,14 @@ const deriveGenSingular = (
252
252
  const descriptors = [...prefix.map((arg) => arg.descriptor), countScalar.descriptor, tRef(outScalar.descriptor)];
253
253
  usedTypes.add(outScalar.tsAlias);
254
254
  const signature = prefix.map((arg) => `${arg.name}: ${arg.tsType}`).join(", ");
255
- const callArgs = [...prefix.map((arg) => arg.name), "1", "out"].join(", ");
255
+ const callArgs = [...prefix.map((arg) => arg.name), "1", "__out"].join(", ");
256
256
  const jsDoc = genSingularJsDoc({ plan, provenance, docs }, prefix, shape);
257
257
 
258
- const body = [" const out = { value: 0 };", ` ${bindingName}(${callArgs});`, " return out.value;"].join(
259
- "\n",
260
- );
258
+ const body = [
259
+ " const __out = { value: 0 };",
260
+ ` ${bindingName}(${callArgs});`,
261
+ " return __out.value;",
262
+ ].join("\n");
261
263
 
262
264
  const binding = glBind(plan.command.name, renderDescriptorList(descriptors), tVoid);
263
265
 
package/src/runner.ts CHANGED
@@ -2,12 +2,13 @@ import { mkdirSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import type { ModuleExport } from "./react/element-config.js";
4
4
  import type { OmittedProps } from "./store/jsx/omitted-props.js";
5
- import type { StoreOptions } from "./store/store-fs.js";
6
5
  import { checkModules } from "./compile.js";
7
6
  import { isGiStoreFresh } from "./fingerprint.js";
8
7
  import { runGiCodegen } from "./gi.js";
9
8
  import { Library } from "./gir/library.js";
10
9
  import { generateGlModules, type GlGenerationReport } from "./khronos/pipeline.js";
10
+ import { sweepStagingDirs } from "./staging.js";
11
+ import { ensureStoreLink, type StoreOptions } from "./store/store-fs.js";
11
12
 
12
13
  type GlCodegenOptions = {
13
14
  registryPath: string;
@@ -38,6 +39,8 @@ type CodegenRunnerOptions = {
38
39
  userOmittedProps?: OmittedProps;
39
40
  /** Regenerates both stores even when their fingerprints are fresh. */
40
41
  isForced?: boolean;
42
+ /** Emits GIR byte sequences as `Uint8Array` rather than `number[]`; defaults to false. */
43
+ isByteArrayTyped?: boolean;
41
44
  };
42
45
 
43
46
  /** What a `runCodegen` run produced. */
@@ -64,6 +67,10 @@ type StoreResult = {
64
67
  * store or the React element config changed, unless `options.isForced` is set. Pass the spread of
65
68
  * `resolveStore(projectRoot)` for everything but `libraries` and `girPath`.
66
69
  *
70
+ * Every store already on disk is linked at its `linkDir` before anything is generated, so a link an install
71
+ * pruned out of `node_modules` is restored without regenerating the store, and the gi store is reachable
72
+ * under its own specifier while the jsx store is type checked.
73
+ *
67
74
  * @param options What to generate and where to write it.
68
75
  * @returns A summary of what was regenerated and how long the run took.
69
76
  * @throws If `girPath` is empty, which would otherwise generate nothing and report success.
@@ -152,12 +159,14 @@ const emitStoresWithConfig = async (config: {
152
159
  girPath: string[];
153
160
  }): Promise<StoreResult> => {
154
161
  const { options, gi, jsx, libraries, girPath } = config;
162
+ const isByteArrayTyped = options.isByteArrayTyped === true;
155
163
  let library: Library | undefined;
156
- const loadLibrary = (): Library => (library ??= Library.load(libraries, girPath));
157
- const isGiRegenerated = options.isForced === true || !isGiStoreFresh(gi.storeDir, libraries, girPath);
164
+ const loadLibrary = (): Library => (library ??= Library.load(libraries, girPath, isByteArrayTyped));
165
+ const giInputs = { girFiles: [] as string[], libraries, girPath, isByteArrayTyped };
166
+ const isGiRegenerated = options.isForced === true || !isGiStoreFresh(gi.storeDir, giInputs);
158
167
 
159
168
  const namespaces = isGiRegenerated
160
- ? runGiCodegen(loadLibrary(), { gi, libraries, girPath })
169
+ ? runGiCodegen(loadLibrary(), { gi, libraries, girPath, isByteArrayTyped })
161
170
  : 0;
162
171
 
163
172
  if (jsx === undefined) {
@@ -167,6 +176,17 @@ const emitStoresWithConfig = async (config: {
167
176
  return emitJsxStore({ options, jsx, gi, loadLibrary, isGiRegenerated, namespaces });
168
177
  };
169
178
 
179
+ const prepareStores = (stores: (StoreOptions | undefined)[]): void => {
180
+ for (const store of stores) {
181
+ if (store === undefined) {
182
+ continue;
183
+ }
184
+
185
+ sweepStagingDirs(store.storeDir);
186
+ ensureStoreLink(store);
187
+ }
188
+ };
189
+
170
190
  const emitStores = async (options: CodegenRunnerOptions): Promise<StoreResult> => {
171
191
  const { gi, jsx, libraries, girPath } = options;
172
192
 
@@ -174,6 +194,8 @@ const emitStores = async (options: CodegenRunnerOptions): Promise<StoreResult> =
174
194
  throw new Error("codegen needs at least one GIR search path; pass the result of resolveGirPath");
175
195
  }
176
196
 
197
+ prepareStores([gi, jsx]);
198
+
177
199
  return emitStoresWithConfig({ options, gi, jsx, libraries, girPath });
178
200
  };
179
201
 
package/src/staging.ts ADDED
@@ -0,0 +1,67 @@
1
+ import { errorCode } from "@gtkx/utils";
2
+ import { mkdirSync, mkdtempSync, readdirSync, rmSync } from "node:fs";
3
+ import { basename, dirname, join } from "node:path";
4
+
5
+ const STAGING_SUFFIX = ".tmp-";
6
+ const OWNER_PATTERN = /^(?<pid>\d+)-/;
7
+
8
+ const isOwnerRunning = (pid: number): boolean => {
9
+ if (!Number.isSafeInteger(pid) || pid <= 0) {
10
+ return false;
11
+ }
12
+
13
+ try {
14
+ process.kill(pid, 0);
15
+
16
+ return true;
17
+ } catch (error) {
18
+ return errorCode(error) === "EPERM";
19
+ }
20
+ };
21
+
22
+ const isStranded = (entry: string, prefix: string): boolean => {
23
+ if (!entry.startsWith(prefix)) {
24
+ return false;
25
+ }
26
+
27
+ const owner = OWNER_PATTERN.exec(entry.slice(prefix.length))?.groups?.pid;
28
+
29
+ return owner === undefined || !isOwnerRunning(Number(owner));
30
+ };
31
+
32
+ const readEntries = (parentDir: string): string[] => {
33
+ try {
34
+ return readdirSync(parentDir);
35
+ } catch {
36
+ return [];
37
+ }
38
+ };
39
+
40
+ const removeStrandedDir = (path: string): void => {
41
+ try {
42
+ rmSync(path, { recursive: true, force: true });
43
+ } catch {
44
+ return;
45
+ }
46
+ };
47
+
48
+ const sweepStrandedDirs = (parentDir: string, prefix: string): void => {
49
+ for (const entry of readEntries(parentDir)) {
50
+ if (isStranded(entry, prefix)) {
51
+ removeStrandedDir(join(parentDir, entry));
52
+ }
53
+ }
54
+ };
55
+
56
+ const sweepStagingDirs = (target: string): void => {
57
+ sweepStrandedDirs(dirname(target), `${basename(target)}${STAGING_SUFFIX}`);
58
+ };
59
+
60
+ const createStagingDir = (target: string): string => {
61
+ mkdirSync(dirname(target), { recursive: true });
62
+ sweepStagingDirs(target);
63
+
64
+ return mkdtempSync(`${target}${STAGING_SUFFIX}${String(process.pid)}-`);
65
+ };
66
+
67
+ export { createStagingDir, sweepStagingDirs, sweepStrandedDirs };