@gtkx/codegen 1.2.2 → 1.4.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 (218) hide show
  1. package/README.md +6 -4
  2. package/dist/analysis/descriptor-render.d.ts +7 -1
  3. package/dist/analysis/descriptor-render.d.ts.map +1 -1
  4. package/dist/analysis/descriptor-render.js +37 -15
  5. package/dist/analysis/descriptor-render.js.map +1 -1
  6. package/dist/analysis/descriptor.d.ts +5 -1
  7. package/dist/analysis/descriptor.d.ts.map +1 -1
  8. package/dist/analysis/descriptor.js +12 -2
  9. package/dist/analysis/descriptor.js.map +1 -1
  10. package/dist/analysis/param-capability.d.ts +5 -2
  11. package/dist/analysis/param-capability.d.ts.map +1 -1
  12. package/dist/analysis/param-capability.js +36 -4
  13. package/dist/analysis/param-capability.js.map +1 -1
  14. package/dist/analysis/param-structure.d.ts +1 -1
  15. package/dist/analysis/param-structure.d.ts.map +1 -1
  16. package/dist/analysis/param-structure.js.map +1 -1
  17. package/dist/analysis/ts-type.d.ts +1 -1
  18. package/dist/analysis/ts-type.d.ts.map +1 -1
  19. package/dist/analysis/ts-type.js +28 -10
  20. package/dist/analysis/ts-type.js.map +1 -1
  21. package/dist/compile.js +1 -1
  22. package/dist/compile.js.map +1 -1
  23. package/dist/docs/api-reference.d.ts +6 -0
  24. package/dist/docs/api-reference.d.ts.map +1 -1
  25. package/dist/docs/api-reference.js +11 -2
  26. package/dist/docs/api-reference.js.map +1 -1
  27. package/dist/docs/pipeline.d.ts +3 -0
  28. package/dist/docs/pipeline.d.ts.map +1 -1
  29. package/dist/docs/pipeline.js +10 -1
  30. package/dist/docs/pipeline.js.map +1 -1
  31. package/dist/fingerprint.d.ts +5 -0
  32. package/dist/fingerprint.d.ts.map +1 -1
  33. package/dist/fingerprint.js +111 -15
  34. package/dist/fingerprint.js.map +1 -1
  35. package/dist/gi.d.ts +3 -0
  36. package/dist/gi.d.ts.map +1 -1
  37. package/dist/gi.js +33 -7
  38. package/dist/gi.js.map +1 -1
  39. package/dist/gir/external-namespaces.d.ts +8 -0
  40. package/dist/gir/external-namespaces.d.ts.map +1 -0
  41. package/dist/gir/external-namespaces.js +4 -0
  42. package/dist/gir/external-namespaces.js.map +1 -0
  43. package/dist/gir/finish-overrides.d.ts +3 -0
  44. package/dist/gir/finish-overrides.d.ts.map +1 -0
  45. package/dist/gir/finish-overrides.js +5 -0
  46. package/dist/gir/finish-overrides.js.map +1 -0
  47. package/dist/gir/function.d.ts +5 -2
  48. package/dist/gir/function.d.ts.map +1 -1
  49. package/dist/gir/function.js +34 -5
  50. package/dist/gir/function.js.map +1 -1
  51. package/dist/gir/hidden-symbols.d.ts +3 -0
  52. package/dist/gir/hidden-symbols.d.ts.map +1 -0
  53. package/dist/gir/hidden-symbols.js +39 -0
  54. package/dist/gir/hidden-symbols.js.map +1 -0
  55. package/dist/gir/library.d.ts +22 -2
  56. package/dist/gir/library.d.ts.map +1 -1
  57. package/dist/gir/library.js +21 -3
  58. package/dist/gir/library.js.map +1 -1
  59. package/dist/gir/ucs4-overrides.d.ts +3 -0
  60. package/dist/gir/ucs4-overrides.d.ts.map +1 -0
  61. package/dist/gir/ucs4-overrides.js +7 -0
  62. package/dist/gir/ucs4-overrides.js.map +1 -0
  63. package/dist/guards.d.ts +2 -1
  64. package/dist/guards.d.ts.map +1 -1
  65. package/dist/guards.js +3 -1
  66. package/dist/guards.js.map +1 -1
  67. package/dist/index.d.ts +2 -0
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +2 -0
  70. package/dist/index.js.map +1 -1
  71. package/dist/jsx.d.ts.map +1 -1
  72. package/dist/jsx.js +27 -8
  73. package/dist/jsx.js.map +1 -1
  74. package/dist/runner.d.ts +8 -0
  75. package/dist/runner.d.ts.map +1 -1
  76. package/dist/runner.js +9 -3
  77. package/dist/runner.js.map +1 -1
  78. package/dist/store/external-package-links.d.ts +6 -0
  79. package/dist/store/external-package-links.d.ts.map +1 -0
  80. package/dist/store/external-package-links.js +158 -0
  81. package/dist/store/external-package-links.js.map +1 -0
  82. package/dist/store/gi/callable-doc.d.ts.map +1 -1
  83. package/dist/store/gi/callable-doc.js +4 -4
  84. package/dist/store/gi/callable-doc.js.map +1 -1
  85. package/dist/store/gi/callables.js +1 -1
  86. package/dist/store/gi/callables.js.map +1 -1
  87. package/dist/store/gi/class.d.ts.map +1 -1
  88. package/dist/store/gi/class.js +17 -0
  89. package/dist/store/gi/class.js.map +1 -1
  90. package/dist/store/gi/constructor-props.js +1 -1
  91. package/dist/store/gi/constructor-props.js.map +1 -1
  92. package/dist/store/gi/function.d.ts.map +1 -1
  93. package/dist/store/gi/function.js +2 -1
  94. package/dist/store/gi/function.js.map +1 -1
  95. package/dist/store/gi/generated-libraries.d.ts +26 -0
  96. package/dist/store/gi/generated-libraries.d.ts.map +1 -0
  97. package/dist/store/gi/generated-libraries.js +53 -0
  98. package/dist/store/gi/generated-libraries.js.map +1 -0
  99. package/dist/store/gi/gtype-binding.d.ts +2 -1
  100. package/dist/store/gi/gtype-binding.d.ts.map +1 -1
  101. package/dist/store/gi/gtype-binding.js +6 -1
  102. package/dist/store/gi/gtype-binding.js.map +1 -1
  103. package/dist/store/gi/method.d.ts +7 -2
  104. package/dist/store/gi/method.d.ts.map +1 -1
  105. package/dist/store/gi/method.js +126 -30
  106. package/dist/store/gi/method.js.map +1 -1
  107. package/dist/store/gi/param-marshal.d.ts +8 -1
  108. package/dist/store/gi/param-marshal.d.ts.map +1 -1
  109. package/dist/store/gi/param-marshal.js +52 -4
  110. package/dist/store/gi/param-marshal.js.map +1 -1
  111. package/dist/store/gi/record-constructor.d.ts.map +1 -1
  112. package/dist/store/gi/record-constructor.js +2 -3
  113. package/dist/store/gi/record-constructor.js.map +1 -1
  114. package/dist/store/gi/record-field-accessor.d.ts +2 -1
  115. package/dist/store/gi/record-field-accessor.d.ts.map +1 -1
  116. package/dist/store/gi/record-field-accessor.js +9 -3
  117. package/dist/store/gi/record-field-accessor.js.map +1 -1
  118. package/dist/store/gi/runtime-override.js +1 -1
  119. package/dist/store/gi/runtime-override.js.map +1 -1
  120. package/dist/store/gi/signal.js +2 -2
  121. package/dist/store/gi/signal.js.map +1 -1
  122. package/dist/store/gi/vtable.d.ts.map +1 -1
  123. package/dist/store/gi/vtable.js +82 -12
  124. package/dist/store/gi/vtable.js.map +1 -1
  125. package/dist/store/gi-store.d.ts +12 -2
  126. package/dist/store/gi-store.d.ts.map +1 -1
  127. package/dist/store/gi-store.js +24 -5
  128. package/dist/store/gi-store.js.map +1 -1
  129. package/dist/store/jsx/jsx.d.ts.map +1 -1
  130. package/dist/store/jsx/jsx.js +3 -1
  131. package/dist/store/jsx/jsx.js.map +1 -1
  132. package/dist/store/jsx/metadata.d.ts.map +1 -1
  133. package/dist/store/jsx/metadata.js +35 -60
  134. package/dist/store/jsx/metadata.js.map +1 -1
  135. package/dist/store/jsx-store.d.ts +8 -1
  136. package/dist/store/jsx-store.d.ts.map +1 -1
  137. package/dist/store/jsx-store.js +9 -2
  138. package/dist/store/jsx-store.js.map +1 -1
  139. package/dist/store/resolve-store.d.ts +2 -1
  140. package/dist/store/resolve-store.d.ts.map +1 -1
  141. package/dist/store/resolve-store.js +3 -1
  142. package/dist/store/resolve-store.js.map +1 -1
  143. package/dist/store/store-fs.d.ts +2 -1
  144. package/dist/store/store-fs.d.ts.map +1 -1
  145. package/dist/store/store-fs.js +8 -3
  146. package/dist/store/store-fs.js.map +1 -1
  147. package/dist/writer/context.d.ts.map +1 -1
  148. package/dist/writer/context.js +8 -0
  149. package/dist/writer/context.js.map +1 -1
  150. package/dist/writer/gtk-doc.d.ts.map +1 -1
  151. package/dist/writer/gtk-doc.js +27 -1
  152. package/dist/writer/gtk-doc.js.map +1 -1
  153. package/overrides/gdk/rgba.ts.ejs +15 -1
  154. package/overrides/glib/index.ts.ejs +3 -0
  155. package/overrides/glib/regex.ts.ejs +66 -0
  156. package/overrides/gobject/index.ts.ejs +3 -0
  157. package/overrides/gobject/object-class.ts.ejs +27 -0
  158. package/overrides/gobject/param-spec-getters.ts.ejs +57 -0
  159. package/overrides/gobject/param-spec.ts.ejs +21 -0
  160. package/overrides/gobject/value.ts.ejs +6 -1
  161. package/overrides/graphene/structs.ts.ejs +38 -3
  162. package/overrides/gtk/index.ts.ejs +2 -0
  163. package/overrides/gtk/widget-class.ts.ejs +28 -0
  164. package/package.json +7 -6
  165. package/src/analysis/descriptor-render.ts +60 -16
  166. package/src/analysis/descriptor.ts +17 -3
  167. package/src/analysis/param-capability.ts +68 -5
  168. package/src/analysis/param-structure.ts +1 -1
  169. package/src/analysis/ts-type.ts +45 -11
  170. package/src/compile.ts +1 -1
  171. package/src/docs/api-reference.ts +20 -2
  172. package/src/docs/pipeline.ts +15 -1
  173. package/src/fingerprint.ts +151 -16
  174. package/src/gi.ts +48 -9
  175. package/src/gir/external-namespaces.ts +8 -0
  176. package/src/gir/finish-overrides.ts +5 -0
  177. package/src/gir/function.ts +52 -14
  178. package/src/gir/hidden-symbols.ts +39 -0
  179. package/src/gir/library.ts +36 -3
  180. package/src/gir/ucs4-overrides.ts +7 -0
  181. package/src/guards.ts +6 -1
  182. package/src/index.ts +2 -0
  183. package/src/jsx.ts +33 -8
  184. package/src/runner.ts +17 -3
  185. package/src/store/external-package-links.ts +226 -0
  186. package/src/store/gi/callable-doc.ts +8 -4
  187. package/src/store/gi/callables.ts +1 -1
  188. package/src/store/gi/class.ts +29 -0
  189. package/src/store/gi/constructor-props.ts +1 -1
  190. package/src/store/gi/function.ts +2 -1
  191. package/src/store/gi/generated-libraries.ts +91 -0
  192. package/src/store/gi/gtype-binding.ts +8 -1
  193. package/src/store/gi/method.ts +189 -36
  194. package/src/store/gi/param-marshal.ts +67 -4
  195. package/src/store/gi/record-constructor.ts +8 -3
  196. package/src/store/gi/record-field-accessor.ts +11 -2
  197. package/src/store/gi/runtime-override.ts +1 -1
  198. package/src/store/gi/signal.ts +2 -2
  199. package/src/store/gi/vtable.ts +118 -11
  200. package/src/store/gi-store.ts +48 -5
  201. package/src/store/jsx/jsx.ts +3 -1
  202. package/src/store/jsx/metadata.ts +46 -99
  203. package/src/store/jsx-store.ts +23 -7
  204. package/src/store/resolve-store.ts +3 -1
  205. package/src/store/store-fs.ts +10 -2
  206. package/src/writer/context.ts +11 -0
  207. package/src/writer/gtk-doc.ts +38 -1
  208. package/overrides/cairo/context.ts.ejs +0 -1269
  209. package/overrides/cairo/font-face.ts.ejs +0 -151
  210. package/overrides/cairo/font-options.ts.ejs +0 -199
  211. package/overrides/cairo/image-surface.ts.ejs +0 -132
  212. package/overrides/cairo/index.ts.ejs +0 -22
  213. package/overrides/cairo/matrix.ts.ejs +0 -171
  214. package/overrides/cairo/pattern.ts.ejs +0 -476
  215. package/overrides/cairo/recording-surface.ts.ejs +0 -102
  216. package/overrides/cairo/region.ts.ejs +0 -237
  217. package/overrides/cairo/scaled-font.ts.ejs +0 -330
  218. package/overrides/cairo/surface.ts.ejs +0 -296
@@ -11,6 +11,7 @@ import type { JsDocSpec } from "../../writer/doc.js";
11
11
  import {
12
12
  isInlineCallbackRef,
13
13
  isScalarRef,
14
+ renderCallbackType,
14
15
  renderDescriptor,
15
16
  renderParamDescriptor,
16
17
  } from "../../analysis/descriptor-render.js";
@@ -19,7 +20,6 @@ import {
19
20
  handlerParameters,
20
21
  parameterIdentifier,
21
22
  renamesWithInstance,
22
- renderHandlerParameters,
23
23
  renderHandlerResultType,
24
24
  } from "../../analysis/param-structure.js";
25
25
  import { renderTsType } from "../../analysis/ts-type.js";
@@ -74,6 +74,12 @@ type Vtable = {
74
74
  slots: VtableSlot[];
75
75
  };
76
76
 
77
+ type SlotParamPlan = {
78
+ parameters: GirParameter[];
79
+ argIndexMap: Map<number, number>;
80
+ decoded: Set<GirParameter>;
81
+ };
82
+
77
83
  const PADDING_FIELD_NAME = /^(?:reserved|padding)\d*$/i;
78
84
 
79
85
  const UNCALLABLE_SLOT_KEYS: Set<string> = new Set([
@@ -86,6 +92,7 @@ const UNCALLABLE_SLOT_KEYS: Set<string> = new Set([
86
92
  const VTABLE_CACHE: WeakMap<GirClass, Vtable> = new WeakMap();
87
93
  const PROTECTED_SLOT_NOTE = "It is `protected`, so only a subclass chaining up reaches it.";
88
94
  const PUBLIC_SLOT_NOTE = "Calling it from anywhere else re-enters the slot on a live instance.";
95
+ const OPAQUE_CALLBACK_NOTE = "Received as the raw address of the C function pointer.";
89
96
 
90
97
  const vfuncMemberName = (fieldName: string): string => `vfunc${pascalCase(fieldName)}`;
91
98
 
@@ -125,6 +132,79 @@ const vtableCallbackType = (context: ModuleContext, field: GirField): GirCallbac
125
132
  return type.value;
126
133
  };
127
134
 
135
+ const isCallbackParam = (context: ModuleContext, parameter: GirParameter): boolean =>
136
+ parameter.type !== undefined && context.library.typeFor(parameter.type)?.kind === "callback";
137
+
138
+ const isDecodedCallbackParam = (context: ModuleContext, parameter: GirParameter, index: number): boolean =>
139
+ isCallbackParam(context, parameter) &&
140
+ parameter.closureIndex === index + 1 &&
141
+ parameter.destroyIndex === undefined;
142
+
143
+ const decodedSlotParams = (context: ModuleContext, callback: GirCallback): Set<GirParameter> => {
144
+ const decoded: Set<GirParameter> = new Set();
145
+
146
+ for (const [index, parameter] of callback.parameters.entries()) {
147
+ if (isDecodedCallbackParam(context, parameter, index)) {
148
+ decoded.add(parameter);
149
+ }
150
+ }
151
+
152
+ return decoded;
153
+ };
154
+
155
+ const companionIndices = (decoded: Set<GirParameter>): Set<number> => {
156
+ const companions: Set<number> = new Set();
157
+
158
+ for (const parameter of decoded) {
159
+ if (parameter.closureIndex !== undefined) {
160
+ companions.add(parameter.closureIndex);
161
+ }
162
+ }
163
+
164
+ return companions;
165
+ };
166
+
167
+ const slotParamPlan = (context: ModuleContext, callback: GirCallback): SlotParamPlan => {
168
+ const decoded = decodedSlotParams(context, callback);
169
+ const companions = companionIndices(decoded);
170
+ const parameters: GirParameter[] = [];
171
+ const argIndexMap: Map<number, number> = new Map();
172
+
173
+ for (const [index, parameter] of callback.parameters.entries()) {
174
+ if (companions.has(index)) {
175
+ continue;
176
+ }
177
+
178
+ argIndexMap.set(index, parameters.length);
179
+ parameters.push(parameter);
180
+ }
181
+
182
+ return { parameters, argIndexMap, decoded };
183
+ };
184
+
185
+ const isOpaqueCallbackParam = (context: ModuleContext, plan: SlotParamPlan, parameter: GirParameter): boolean =>
186
+ isCallbackParam(context, parameter) && !plan.decoded.has(parameter);
187
+
188
+ const renderSlotParamDescriptor = (context: ModuleContext, parameter: GirParameter, plan: SlotParamPlan): string => {
189
+ if (plan.decoded.has(parameter)) {
190
+ const callbackDescriptor = renderCallbackType(context, parameter.type, parameter);
191
+
192
+ if (callbackDescriptor !== undefined) {
193
+ return callbackDescriptor;
194
+ }
195
+ }
196
+
197
+ return renderParamDescriptor(context, parameter, parameter.type, { argIndexMap: plan.argIndexMap });
198
+ };
199
+
200
+ const renderSlotParamTsType = (context: ModuleContext, plan: SlotParamPlan, parameter: GirParameter): string => {
201
+ if (isOpaqueCallbackParam(context, plan, parameter)) {
202
+ return parameter.nullable ? "bigint | null" : "bigint";
203
+ }
204
+
205
+ return renderTsType(context, parameter.type, parameter.nullable);
206
+ };
207
+
128
208
  const vtableSlotEntry = (
129
209
  context: ModuleContext,
130
210
  field: GirField,
@@ -387,15 +467,35 @@ const hasCallableVfuncSlots = (context: ModuleContext, namespaceName: string, kl
387
467
 
388
468
  const slotDoc = (slot: VtableSlot): string | undefined => slot.vfunc?.doc ?? slot.field.doc ?? slot.callback.doc;
389
469
 
390
- const slotDocParameters = (slot: VtableSlot): GirParameter[] => {
470
+ const slotParamDoc = (
471
+ context: ModuleContext,
472
+ plan: SlotParamPlan,
473
+ parameter: GirParameter,
474
+ doc: string | undefined,
475
+ ): string | undefined => {
476
+ if (!isOpaqueCallbackParam(context, plan, parameter)) {
477
+ return doc;
478
+ }
479
+
480
+ return doc === undefined ? OPAQUE_CALLBACK_NOTE : `${doc} ${OPAQUE_CALLBACK_NOTE}`;
481
+ };
482
+
483
+ const slotDocParameters = (context: ModuleContext, slot: VtableSlot): GirParameter[] => {
484
+ const plan = slotParamPlan(context, slot.callback);
391
485
  const [, ...parameters] = slot.callback.parameters;
392
486
  const vfuncParameters = slot.vfunc?.parameters ?? [];
393
487
 
394
- return parameters.map((parameter, index) => ({ ...parameter, doc: vfuncParameters[index]?.doc ?? parameter.doc }));
488
+ return parameters
489
+ .map((parameter, index) => ({ parameter, index }))
490
+ .filter(({ index }) => plan.argIndexMap.has(index + 1))
491
+ .map(({ parameter, index }) => ({
492
+ ...parameter,
493
+ doc: slotParamDoc(context, plan, parameter, vfuncParameters[index]?.doc ?? parameter.doc),
494
+ }));
395
495
  };
396
496
 
397
- const slotDocSpec = (slot: VtableSlot): JsDocSpec => {
398
- const parameters = slotDocParameters(slot);
497
+ const slotDocSpec = (context: ModuleContext, slot: VtableSlot): JsDocSpec => {
498
+ const parameters = slotDocParameters(context, slot);
399
499
  const source = slot.vfunc ?? slot.callback;
400
500
 
401
501
  return {
@@ -446,12 +546,18 @@ const isCallableSlot = (context: ModuleContext, slot: VtableSlot): boolean =>
446
546
  !UNCALLABLE_SLOT_KEYS.has(slot.key) && !hasUnannotatedPointerParam(context, slot);
447
547
 
448
548
  const vfuncSlotSignature = (context: ModuleContext, slot: VtableSlot, isOptional = false): VfuncSignature => {
449
- const [, ...parameters] = slot.callback.parameters;
549
+ const plan = slotParamPlan(context, slot.callback);
550
+ const [, ...parameters] = plan.parameters;
450
551
 
451
552
  const renderType = (ref: TypeId | undefined, isNullable: boolean): string =>
452
553
  renderTsType(context, ref, isNullable);
453
554
 
454
- const signature = renderHandlerParameters(parameters, renderType).join(", ");
555
+ const signature = handlerParameters(parameters)
556
+ .map(
557
+ (parameter, index) =>
558
+ `${parameterIdentifier(parameter, index)}: ${renderSlotParamTsType(context, plan, parameter)}`)
559
+ .join(", ");
560
+
455
561
  const folded = foldedLengthParameters(context.library, slot.callback);
456
562
 
457
563
  const returnType = renderHandlerResultType({
@@ -470,13 +576,13 @@ const renderVfuncMember = (options: VfuncMemberOptions): string => {
470
576
  const { context, ownerRef, slot, mode, isProtected } = options;
471
577
  const { header, returnType } = vfuncSlotSignature(context, slot, mode === "requirement");
472
578
  const declaration = `${isProtected ? "protected " : ""}${header}`;
473
- const doc = renderJsDoc(slotDoc(slot), vfuncMemberNote(options), slotDocSpec(slot));
579
+ const doc = renderJsDoc(slotDoc(slot), vfuncMemberNote(options), slotDocSpec(context, slot));
474
580
 
475
581
  if (mode !== "implementation") {
476
582
  return `${doc}${declaration};`;
477
583
  }
478
584
 
479
- const [, ...parameters] = slot.callback.parameters;
585
+ const [, ...parameters] = slotParamPlan(context, slot.callback).parameters;
480
586
 
481
587
  const inputs = handlerParameters(parameters).map((parameter, index) =>
482
588
  parameterIdentifier(parameter, index));
@@ -511,9 +617,10 @@ const isVtableSlotEligible = (context: ModuleContext, callback: GirCallback): bo
511
617
 
512
618
  const renderVtableSlotDescriptor = (context: ModuleContext, vtable: Vtable, slot: VtableSlot): string => {
513
619
  const { key, field, callback, byteOffset } = slot;
620
+ const plan = slotParamPlan(context, callback);
514
621
 
515
- const argDescriptors = callback.parameters
516
- .map((param) => renderParamDescriptor(context, param, param.type))
622
+ const argDescriptors = plan.parameters
623
+ .map((param) => renderSlotParamDescriptor(context, param, plan))
517
624
  .join(", ");
518
625
 
519
626
  const returnDescriptor = renderDescriptor(
@@ -1,8 +1,14 @@
1
+ import { sortStrings } from "@gtkx/utils";
1
2
  import { existsSync, readdirSync, readFileSync } from "node:fs";
2
3
  import { dirname, join, resolve } from "node:path";
3
4
  import { fileURLToPath } from "node:url";
4
5
  import type { SourceModule } from "../compile.js";
5
6
  import { FINGERPRINT_FILENAME, type GiFingerprint } from "../fingerprint.js";
7
+ import {
8
+ type GeneratedLibraries,
9
+ LIBRARIES_FILENAME,
10
+ renderGeneratedLibraries,
11
+ } from "./gi/generated-libraries.js";
6
12
  import { buildManifest, namespaceBarrel, type StoreOptions, subpathExport, writeStore } from "./store-fs.js";
7
13
 
8
14
  type GiNamespaceInput = {
@@ -11,6 +17,17 @@ type GiNamespaceInput = {
11
17
  girFile: string;
12
18
  };
13
19
 
20
+ type GiExternalNamespaceInput = {
21
+ directory: string;
22
+ packageName: string;
23
+ girFile: string;
24
+ };
25
+
26
+ type GiStoreRecords = {
27
+ fingerprint: GiFingerprint;
28
+ libraries: GeneratedLibraries;
29
+ };
30
+
14
31
  const OVERRIDES_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "overrides");
15
32
 
16
33
  const overrideFiles = (directory: string): SourceModule[] => {
@@ -42,8 +59,17 @@ const barrelFile = (directory: string, girFile: string): SourceModule => {
42
59
  return overrideModule(barrel.fileName, overrideIndex);
43
60
  };
44
61
 
62
+ const externalShimModule = ({ directory, packageName, girFile }: GiExternalNamespaceInput): SourceModule => ({
63
+ fileName: `${directory}/index.ts`,
64
+ source:
65
+ `/** @deprecated Import from "${packageName}" instead; this alias is removed in GTKX 2.0. */\n` +
66
+ `export * from "${packageName}";\n`,
67
+ origin: girFile,
68
+ });
69
+
45
70
  const collectStoreSources = (
46
71
  namespaces: GiNamespaceInput[],
72
+ externalNamespaces: GiExternalNamespaceInput[],
47
73
  ): { collected: SourceModule[]; exportsMap: Record<string, unknown> } => {
48
74
  const exportsMap: Record<string, unknown> = {};
49
75
  const collected: SourceModule[] = [];
@@ -58,15 +84,29 @@ const collectStoreSources = (
58
84
  exportsMap[`./${directory}`] = subpathExport(`${directory}/index`);
59
85
  }
60
86
 
87
+ for (const external of externalNamespaces) {
88
+ collected.push(externalShimModule(external));
89
+ exportsMap[`./${external.directory}`] = subpathExport(`${external.directory}/index`);
90
+ }
91
+
61
92
  return { collected, exportsMap };
62
93
  };
63
94
 
95
+ const storePeerDependencies = (externalNamespaces: GiExternalNamespaceInput[]): Record<string, string> =>
96
+ Object.fromEntries(
97
+ sortStrings(["@gtkx/runtime", ...externalNamespaces.map((entry) => entry.packageName)]).map((name) => [
98
+ name,
99
+ "*",
100
+ ]),
101
+ );
102
+
64
103
  const writeGiStore = (
65
104
  options: StoreOptions,
66
105
  namespaces: GiNamespaceInput[],
67
- fingerprint: GiFingerprint,
106
+ externalNamespaces: GiExternalNamespaceInput[],
107
+ records: GiStoreRecords,
68
108
  ): void => {
69
- const { collected, exportsMap } = collectStoreSources(namespaces);
109
+ const { collected, exportsMap } = collectStoreSources(namespaces, externalNamespaces);
70
110
 
71
111
  writeStore({
72
112
  storeDir: options.storeDir,
@@ -76,10 +116,13 @@ const writeGiStore = (
76
116
  name: "@gtkx/gi",
77
117
  version: options.version,
78
118
  exports: exportsMap,
79
- peerDependencies: { "@gtkx/runtime": "*" },
119
+ peerDependencies: storePeerDependencies(externalNamespaces),
80
120
  }),
81
- rawFiles: [{ relativePath: FINGERPRINT_FILENAME, content: `${JSON.stringify(fingerprint, null, 2)}\n` }],
121
+ rawFiles: [
122
+ { relativePath: FINGERPRINT_FILENAME, content: `${JSON.stringify(records.fingerprint, null, 2)}\n` },
123
+ { relativePath: LIBRARIES_FILENAME, content: renderGeneratedLibraries(records.libraries) },
124
+ ],
82
125
  });
83
126
  };
84
127
 
85
- export { writeGiStore, type GiNamespaceInput };
128
+ export { writeGiStore, type GiExternalNamespaceInput, type GiNamespaceInput };
@@ -3,6 +3,7 @@ import type { GirClass } from "../../gir/class.js";
3
3
  import type { Library } from "../../gir/library.js";
4
4
  import type { GirNamespace } from "../../gir/namespace.js";
5
5
  import type { ImportsBuilder } from "../../writer/imports.js";
6
+ import { externalPackageFor } from "../../gir/external-namespaces.js";
6
7
  import { renderBlock } from "../../writer/emit.js";
7
8
  import { getDoc } from "../gi/doc-spec.js";
8
9
  import { elementPropTypeFor } from "./element-prop-imports.js";
@@ -48,7 +49,8 @@ const addGiNamespace = (imports: ImportsBuilder, namespaceName: string, alias: s
48
49
  return;
49
50
  }
50
51
 
51
- imports.addNamespace(`@gtkx/gi/${namespaceName.toLowerCase()}`, alias, true);
52
+ const specifier = externalPackageFor(namespaceName) ?? `@gtkx/gi/${namespaceName.toLowerCase()}`;
53
+ imports.addNamespace(specifier, alias, true);
52
54
  };
53
55
 
54
56
  const addReactBuiltin = (imports: ImportsBuilder, name: string): void => {
@@ -17,22 +17,17 @@ import {
17
17
  type IntrinsicElementEntry = {
18
18
  glibName: string;
19
19
  signals: [string, string][];
20
- constructOnly: string[];
21
- constructable: string[];
22
- defaults: [string, string][];
20
+ properties: [string, string][];
23
21
  };
24
22
 
25
- type PropNameCollector = {
26
- shouldKeep: (property: GirProperty) => boolean;
27
- seen: Set<string>;
28
- names: string[];
29
- };
23
+ const READABLE = 1;
24
+ const WRITABLE = 2;
25
+ const CONSTRUCT = 4;
26
+ const CONSTRUCT_ONLY = 8;
30
27
 
31
- type DefaultPropsCollector = {
32
- library: Library;
33
- seen: Set<string>;
34
- defaults: [string, string][];
35
- };
28
+ const PROPERTY_ENTRY_TYPE =
29
+ "/** The GObject name of a property, what a write may do to it, and the value it resets to. */\n" +
30
+ "export type PropertyEntry = [name: string, flags: number, defaultValue?: unknown];";
36
31
 
37
32
  const INTEGER_PATTERN = /^-?\d+$/;
38
33
  const FLOAT_PATTERN = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
@@ -44,23 +39,15 @@ const generateMetadata = (library: Library): string => {
44
39
  ({ glibName, signals }) => ` "${glibName}": ${renderSignalsObject(signals)},`,
45
40
  );
46
41
 
47
- const constructOnlyEntries = intrinsicElements
48
- .filter(({ constructOnly }) => constructOnly.length > 0)
49
- .map(({ glibName, constructOnly }) => ` "${glibName}": ${renderStringSet(constructOnly)},`);
50
-
51
- const constructableEntries = intrinsicElements
52
- .filter(({ constructable }) => constructable.length > 0)
53
- .map(({ glibName, constructable }) => ` "${glibName}": ${renderStringSet(constructable)},`);
54
-
55
- const defaultsEntries = intrinsicElements
56
- .filter(({ defaults }) => defaults.length > 0)
57
- .map(({ glibName, defaults }) => ` "${glibName}": ${renderDefaultsObject(defaults)},`);
42
+ const propertyEntries = intrinsicElements
43
+ .filter(({ properties }) => properties.length > 0)
44
+ .map(({ glibName, properties }) => ` "${glibName}": ${renderPropertiesObject(properties)},`);
58
45
 
59
46
  return `${[
47
+ PROPERTY_ENTRY_TYPE,
60
48
  `export const signals: Record<string, Record<string, string>> = {\n${signalsEntries.join("\n")}\n};`,
61
- `export const constructOnlyProps: Record<string, Set<string>> = {\n${constructOnlyEntries.join("\n")}\n};`,
62
- `export const constructProps: Record<string, Set<string>> = {\n${constructableEntries.join("\n")}\n};`,
63
- `export const defaultProps: Record<string, Record<string, unknown>> = {\n${defaultsEntries.join("\n")}\n};`,
49
+ "export const properties: Record<string, Record<string, PropertyEntry>> = " +
50
+ `{\n${propertyEntries.join("\n")}\n};`,
64
51
  ].join("\n\n")}\n`;
65
52
  };
66
53
 
@@ -83,9 +70,7 @@ const collectIntrinsicElements = (library: Library): IntrinsicElementEntry[] =>
83
70
  entries.push({
84
71
  glibName,
85
72
  signals: collectSignals(sources),
86
- constructOnly: collectConstructOnly(sources),
87
- constructable: collectConstructable(sources),
88
- defaults: collectDefaultProps(library, sources),
73
+ properties: collectProperties(library, sources),
89
74
  });
90
75
  }
91
76
 
@@ -116,41 +101,52 @@ const collectSignals = (sources: GirClass[]): [string, string][] => {
116
101
  return signals;
117
102
  };
118
103
 
119
- const collectPropNamesFromSource = (source: GirClass, collector: PropNameCollector): void => {
120
- const { shouldKeep, seen, names } = collector;
104
+ const propertyFlags = (property: GirProperty): number => {
105
+ const construct = isConstructableProperty(property) && !property.constructOnly ? CONSTRUCT : 0;
106
+ const constructOnly = property.constructOnly ? CONSTRUCT_ONLY : 0;
121
107
 
122
- for (const property of source.properties) {
123
- if (!shouldKeep(property)) {
124
- continue;
125
- }
108
+ return (property.readable ? READABLE : 0) |
109
+ (property.writable ? WRITABLE : 0) |
110
+ construct |
111
+ constructOnly;
112
+ };
113
+
114
+ const propertyEntryLiteral = (library: Library, klass: GirClass, property: GirProperty): string => {
115
+ const head = `${sourceStringLiteral(property.name)},${String(propertyFlags(property))}`;
116
+ const literal = isDefaultCandidate(property) ? renderDefaultLiteral(library, klass, property) : undefined;
117
+
118
+ return literal === undefined ? `[${head}]` : `[${head},${literal}]`;
119
+ };
126
120
 
121
+ const collectPropertiesFromSource = (
122
+ library: Library,
123
+ source: GirClass,
124
+ seen: Set<string>,
125
+ entries: [string, string][],
126
+ ): void => {
127
+ for (const property of source.properties) {
127
128
  const jsName = toCamelIdentifier(property.name);
128
129
 
129
- if (seen.has(jsName)) {
130
+ if (!property.introspectable || seen.has(jsName)) {
130
131
  continue;
131
132
  }
132
133
 
133
134
  seen.add(jsName);
134
- names.push(jsName);
135
+ entries.push([jsName, propertyEntryLiteral(library, source, property)]);
135
136
  }
136
137
  };
137
138
 
138
- const collectPropNames = (sources: GirClass[], shouldKeep: (property: GirProperty) => boolean): string[] => {
139
- const collector: PropNameCollector = { shouldKeep, seen: new Set<string>(), names: [] };
139
+ const collectProperties = (library: Library, sources: GirClass[]): [string, string][] => {
140
+ const seen: Set<string> = new Set();
141
+ const entries: [string, string][] = [];
140
142
 
141
143
  for (const source of sources) {
142
- collectPropNamesFromSource(source, collector);
144
+ collectPropertiesFromSource(library, source, seen, entries);
143
145
  }
144
146
 
145
- return collector.names;
147
+ return entries;
146
148
  };
147
149
 
148
- const collectConstructOnly = (sources: GirClass[]): string[] =>
149
- collectPropNames(sources, (property) => property.constructOnly && property.introspectable);
150
-
151
- const collectConstructable = (sources: GirClass[]): string[] =>
152
- collectPropNames(sources, (property) => isConstructableProperty(property) && property.introspectable);
153
-
154
150
  const renderObjectLiteral = (entries: [string, string][], renderValue: (value: string) => string): string => {
155
151
  if (entries.length === 0) {
156
152
  return "{}";
@@ -161,60 +157,14 @@ const renderObjectLiteral = (entries: [string, string][], renderValue: (value: s
161
157
  return `{\n${lines.join(",\n")}\n }`;
162
158
  };
163
159
 
164
- const renderStringSet = (names: string[]): string =>
165
- `new Set([${names.map((name) => sourceStringLiteral(name)).join(",")}])`;
160
+ const renderPropertiesObject = (entries: [string, string][]): string =>
161
+ renderObjectLiteral(entries, (value) => value);
166
162
 
167
163
  const renderSignalsObject = (entries: [string, string][]): string => renderObjectLiteral(entries, sourceStringLiteral);
168
164
 
169
- const collectDefaultsFromClass = (klass: GirClass, collector: DefaultPropsCollector): void => {
170
- for (const property of klass.properties) {
171
- const entry = defaultPropEntry(collector.library, klass, property, collector.seen);
172
-
173
- if (entry !== undefined) {
174
- collector.defaults.push(entry);
175
- }
176
- }
177
- };
178
-
179
- const collectDefaultProps = (library: Library, sources: GirClass[]): [string, string][] => {
180
- const collector: DefaultPropsCollector = { library, seen: new Set<string>(), defaults: [] };
181
-
182
- for (const klass of sources) {
183
- collectDefaultsFromClass(klass, collector);
184
- }
185
-
186
- return collector.defaults;
187
- };
188
-
189
165
  const isDefaultCandidate = (property: GirProperty): boolean =>
190
166
  (property.writable || property.construct) && !property.constructOnly && property.introspectable;
191
167
 
192
- const defaultPropEntry = (
193
- library: Library,
194
- klass: GirClass,
195
- property: GirProperty,
196
- seen: Set<string>,
197
- ): [string, string] | undefined => {
198
- if (!isDefaultCandidate(property)) {
199
- return undefined;
200
- }
201
-
202
- const jsName = toCamelIdentifier(property.name);
203
-
204
- if (seen.has(jsName)) {
205
- return undefined;
206
- }
207
-
208
- seen.add(jsName);
209
- const literal = renderDefaultLiteral(library, klass, property);
210
-
211
- if (literal === undefined) {
212
- return undefined;
213
- }
214
-
215
- return [jsName, literal];
216
- };
217
-
218
168
  const willSetterRejectNull = (library: Library, klass: GirClass, property: GirProperty): boolean => {
219
169
  if (property.setter === undefined) {
220
170
  return false;
@@ -382,7 +332,4 @@ const enumDefaultLiteral = (enumType: GirEnum, raw: string): string | undefined
382
332
  return member?.value;
383
333
  };
384
334
 
385
- const renderDefaultsObject = (entries: [string, string][]): string =>
386
- renderObjectLiteral(entries, (literal) => literal);
387
-
388
335
  export { generateMetadata };
@@ -1,24 +1,40 @@
1
+ import { sortStrings } from "@gtkx/utils";
1
2
  import type { JsxNamespaceFile } from "./jsx/pipeline.js";
2
3
  import type { RawFile } from "./store-fs.js";
3
4
  import { buildManifest, namespaceBarrel, type StoreOptions, subpathExport, writeStore } from "./store-fs.js";
4
5
 
5
- const writeJsxStore = (
6
- options: StoreOptions,
7
- namespaces: JsxNamespaceFile[],
8
- metadata: string,
9
- rawFiles: RawFile[],
10
- ): void => {
6
+ type WriteJsxStoreParams = {
7
+ options: StoreOptions;
8
+ namespaces: JsxNamespaceFile[];
9
+ metadata: string;
10
+ externalPackages: string[];
11
+ rawFiles: RawFile[];
12
+ };
13
+
14
+ const jsxPeerDependencies = (externalPackages: string[]): Record<string, string> =>
15
+ Object.fromEntries(
16
+ sortStrings(["@gtkx/gi", "@gtkx/react", "react", ...externalPackages]).map((name) => [name, "*"]),
17
+ );
18
+
19
+ const writeJsxStore = (params: WriteJsxStoreParams): void => {
20
+ const { options, namespaces, metadata, externalPackages, rawFiles } = params;
21
+
11
22
  const exportsMap: Record<string, unknown> = {
23
+ ".": subpathExport("index"),
12
24
  "./metadata": subpathExport("metadata"),
13
25
  };
14
26
 
15
27
  const files = [{ fileName: "metadata.ts", source: metadata }];
28
+ const indexExports: string[] = [];
16
29
 
17
30
  for (const { directory, source } of namespaces) {
18
31
  files.push({ fileName: `${directory}/${directory}.tsx`, source }, namespaceBarrel(directory));
19
32
  exportsMap[`./${directory}`] = subpathExport(`${directory}/index`);
33
+ indexExports.push(`export * from "./${directory}/index.js";`);
20
34
  }
21
35
 
36
+ files.push({ fileName: "index.ts", source: `${indexExports.join("\n")}\n` });
37
+
22
38
  writeStore({
23
39
  storeDir: options.storeDir,
24
40
  linkDir: options.linkDir,
@@ -27,7 +43,7 @@ const writeJsxStore = (
27
43
  name: "@gtkx/jsx",
28
44
  version: options.version,
29
45
  exports: exportsMap,
30
- peerDependencies: { "@gtkx/gi": "*", "@gtkx/react": "*", react: "*" },
46
+ peerDependencies: jsxPeerDependencies(externalPackages),
31
47
  }),
32
48
  rawFiles,
33
49
  });
@@ -24,9 +24,11 @@ const SCOPE = "@gtkx";
24
24
  const STORE_NAMES: string[] = ["gi", "jsx"];
25
25
 
26
26
  const STORE_CONSUMERS: string[] = [
27
+ "@gtkx/animated",
27
28
  "@gtkx/cli",
28
29
  "@gtkx/components",
29
30
  "@gtkx/css",
31
+ "@gtkx/navigation",
30
32
  "@gtkx/react",
31
33
  "@gtkx/runtime",
32
34
  "@gtkx/testing",
@@ -213,4 +215,4 @@ const resolveStore = (projectRoot: string): ResolvedStore => {
213
215
  };
214
216
  };
215
217
 
216
- export { getShadowingStorePaths, resolveStore, sweepProjectStaging, type ResolvedStore };
218
+ export { getShadowingStorePaths, nodeModulesChain, resolveStore, sweepProjectStaging, type ResolvedStore };
@@ -13,6 +13,7 @@ import { dirname, join, relative } from "node:path";
13
13
  import { keepFailedProject, type SourceModule } from "../compile.js";
14
14
  import { createStagingDir } from "../staging.js";
15
15
  import { compileStore } from "./compile-store.js";
16
+ import { ensureExternalPackageLinks, resetExternalPackageLinks } from "./external-package-links.js";
16
17
 
17
18
  /** Where one generated store is written and how it is reached. */
18
19
  type StoreOptions = {
@@ -88,6 +89,7 @@ const writeStore = (params: WriteStoreParams): void => {
88
89
  try {
89
90
  buildTempStore(tmp, params);
90
91
  swapStore(tmp, params.storeDir);
92
+ resetExternalPackageLinks(params.storeDir);
91
93
  ensureStoreLink(params);
92
94
  } catch (error) {
93
95
  throw keepFailedProject({ projectDir: tmp, keepAt, error });
@@ -148,11 +150,15 @@ const isStoreLinked = (link: StoreLink): boolean => {
148
150
  };
149
151
 
150
152
  const ensureStoreLink = (link: StoreLink): void => {
151
- if (!existsSync(join(link.storeDir, "package.json")) || isStoreLinked(link)) {
153
+ if (!existsSync(join(link.storeDir, "package.json"))) {
152
154
  return;
153
155
  }
154
156
 
155
- symlinkRelative(link.linkDir, link.storeDir);
157
+ if (!isStoreLinked(link)) {
158
+ symlinkRelative(link.linkDir, link.storeDir);
159
+ }
160
+
161
+ ensureExternalPackageLinks(link);
156
162
  };
157
163
 
158
164
  const storeWriteMessage = (storeDir: string, error: unknown): string =>
@@ -176,7 +182,9 @@ export {
176
182
  buildManifest,
177
183
  ensureStoreLink,
178
184
  namespaceBarrel,
185
+ symlinkRelative,
179
186
  writeStore,
187
+ type StoreLink,
180
188
  type StoreOptions,
181
189
  type RawFile,
182
190
  };