@gtkx/codegen 1.2.2 → 1.3.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 (199) hide show
  1. package/README.md +2 -1
  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 +36 -15
  5. package/dist/analysis/descriptor-render.js.map +1 -1
  6. package/dist/analysis/descriptor.d.ts +4 -1
  7. package/dist/analysis/descriptor.d.ts.map +1 -1
  8. package/dist/analysis/descriptor.js +11 -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 +107 -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 +29 -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/jsx.d.ts.map +1 -1
  64. package/dist/jsx.js +27 -8
  65. package/dist/jsx.js.map +1 -1
  66. package/dist/runner.d.ts +8 -0
  67. package/dist/runner.d.ts.map +1 -1
  68. package/dist/runner.js +9 -3
  69. package/dist/runner.js.map +1 -1
  70. package/dist/store/external-package-links.d.ts +6 -0
  71. package/dist/store/external-package-links.d.ts.map +1 -0
  72. package/dist/store/external-package-links.js +158 -0
  73. package/dist/store/external-package-links.js.map +1 -0
  74. package/dist/store/gi/callable-doc.d.ts.map +1 -1
  75. package/dist/store/gi/callable-doc.js +4 -4
  76. package/dist/store/gi/callable-doc.js.map +1 -1
  77. package/dist/store/gi/callables.js +1 -1
  78. package/dist/store/gi/callables.js.map +1 -1
  79. package/dist/store/gi/class.d.ts.map +1 -1
  80. package/dist/store/gi/class.js +17 -0
  81. package/dist/store/gi/class.js.map +1 -1
  82. package/dist/store/gi/constructor-props.js +1 -1
  83. package/dist/store/gi/constructor-props.js.map +1 -1
  84. package/dist/store/gi/function.d.ts.map +1 -1
  85. package/dist/store/gi/function.js +2 -1
  86. package/dist/store/gi/function.js.map +1 -1
  87. package/dist/store/gi/gtype-binding.d.ts +2 -1
  88. package/dist/store/gi/gtype-binding.d.ts.map +1 -1
  89. package/dist/store/gi/gtype-binding.js +6 -1
  90. package/dist/store/gi/gtype-binding.js.map +1 -1
  91. package/dist/store/gi/method.d.ts +7 -2
  92. package/dist/store/gi/method.d.ts.map +1 -1
  93. package/dist/store/gi/method.js +126 -30
  94. package/dist/store/gi/method.js.map +1 -1
  95. package/dist/store/gi/param-marshal.d.ts +8 -1
  96. package/dist/store/gi/param-marshal.d.ts.map +1 -1
  97. package/dist/store/gi/param-marshal.js +52 -4
  98. package/dist/store/gi/param-marshal.js.map +1 -1
  99. package/dist/store/gi/record-constructor.d.ts.map +1 -1
  100. package/dist/store/gi/record-constructor.js +2 -3
  101. package/dist/store/gi/record-constructor.js.map +1 -1
  102. package/dist/store/gi/record-field-accessor.d.ts +2 -1
  103. package/dist/store/gi/record-field-accessor.d.ts.map +1 -1
  104. package/dist/store/gi/record-field-accessor.js +9 -3
  105. package/dist/store/gi/record-field-accessor.js.map +1 -1
  106. package/dist/store/gi/runtime-override.js +1 -1
  107. package/dist/store/gi/runtime-override.js.map +1 -1
  108. package/dist/store/gi/signal.js +2 -2
  109. package/dist/store/gi/signal.js.map +1 -1
  110. package/dist/store/gi/vtable.d.ts.map +1 -1
  111. package/dist/store/gi/vtable.js +82 -12
  112. package/dist/store/gi/vtable.js.map +1 -1
  113. package/dist/store/gi-store.d.ts +7 -2
  114. package/dist/store/gi-store.d.ts.map +1 -1
  115. package/dist/store/gi-store.js +19 -4
  116. package/dist/store/gi-store.js.map +1 -1
  117. package/dist/store/jsx/jsx.d.ts.map +1 -1
  118. package/dist/store/jsx/jsx.js +3 -1
  119. package/dist/store/jsx/jsx.js.map +1 -1
  120. package/dist/store/jsx-store.d.ts +8 -1
  121. package/dist/store/jsx-store.d.ts.map +1 -1
  122. package/dist/store/jsx-store.js +9 -2
  123. package/dist/store/jsx-store.js.map +1 -1
  124. package/dist/store/resolve-store.d.ts +2 -1
  125. package/dist/store/resolve-store.d.ts.map +1 -1
  126. package/dist/store/resolve-store.js +2 -1
  127. package/dist/store/resolve-store.js.map +1 -1
  128. package/dist/store/store-fs.d.ts +2 -1
  129. package/dist/store/store-fs.d.ts.map +1 -1
  130. package/dist/store/store-fs.js +8 -3
  131. package/dist/store/store-fs.js.map +1 -1
  132. package/dist/writer/context.d.ts.map +1 -1
  133. package/dist/writer/context.js +8 -0
  134. package/dist/writer/context.js.map +1 -1
  135. package/dist/writer/gtk-doc.d.ts.map +1 -1
  136. package/dist/writer/gtk-doc.js +27 -1
  137. package/dist/writer/gtk-doc.js.map +1 -1
  138. package/overrides/gdk/rgba.ts.ejs +15 -1
  139. package/overrides/glib/index.ts.ejs +3 -0
  140. package/overrides/glib/regex.ts.ejs +66 -0
  141. package/overrides/gobject/index.ts.ejs +3 -0
  142. package/overrides/gobject/object-class.ts.ejs +27 -0
  143. package/overrides/gobject/param-spec-getters.ts.ejs +57 -0
  144. package/overrides/gobject/param-spec.ts.ejs +21 -0
  145. package/overrides/gobject/value.ts.ejs +6 -1
  146. package/overrides/graphene/structs.ts.ejs +38 -3
  147. package/overrides/gtk/index.ts.ejs +2 -0
  148. package/overrides/gtk/widget-class.ts.ejs +28 -0
  149. package/package.json +6 -5
  150. package/src/analysis/descriptor-render.ts +59 -16
  151. package/src/analysis/descriptor.ts +15 -3
  152. package/src/analysis/param-capability.ts +68 -5
  153. package/src/analysis/param-structure.ts +1 -1
  154. package/src/analysis/ts-type.ts +45 -11
  155. package/src/compile.ts +1 -1
  156. package/src/docs/api-reference.ts +20 -2
  157. package/src/docs/pipeline.ts +15 -1
  158. package/src/fingerprint.ts +144 -16
  159. package/src/gi.ts +44 -9
  160. package/src/gir/external-namespaces.ts +8 -0
  161. package/src/gir/finish-overrides.ts +5 -0
  162. package/src/gir/function.ts +52 -14
  163. package/src/gir/hidden-symbols.ts +39 -0
  164. package/src/gir/library.ts +36 -3
  165. package/src/gir/ucs4-overrides.ts +7 -0
  166. package/src/jsx.ts +33 -8
  167. package/src/runner.ts +17 -3
  168. package/src/store/external-package-links.ts +226 -0
  169. package/src/store/gi/callable-doc.ts +8 -4
  170. package/src/store/gi/callables.ts +1 -1
  171. package/src/store/gi/class.ts +29 -0
  172. package/src/store/gi/constructor-props.ts +1 -1
  173. package/src/store/gi/function.ts +2 -1
  174. package/src/store/gi/gtype-binding.ts +8 -1
  175. package/src/store/gi/method.ts +189 -36
  176. package/src/store/gi/param-marshal.ts +67 -4
  177. package/src/store/gi/record-constructor.ts +8 -3
  178. package/src/store/gi/record-field-accessor.ts +11 -2
  179. package/src/store/gi/runtime-override.ts +1 -1
  180. package/src/store/gi/signal.ts +2 -2
  181. package/src/store/gi/vtable.ts +118 -11
  182. package/src/store/gi-store.ts +33 -3
  183. package/src/store/jsx/jsx.ts +3 -1
  184. package/src/store/jsx-store.ts +23 -7
  185. package/src/store/resolve-store.ts +2 -1
  186. package/src/store/store-fs.ts +10 -2
  187. package/src/writer/context.ts +11 -0
  188. package/src/writer/gtk-doc.ts +38 -1
  189. package/overrides/cairo/context.ts.ejs +0 -1269
  190. package/overrides/cairo/font-face.ts.ejs +0 -151
  191. package/overrides/cairo/font-options.ts.ejs +0 -199
  192. package/overrides/cairo/image-surface.ts.ejs +0 -132
  193. package/overrides/cairo/index.ts.ejs +0 -22
  194. package/overrides/cairo/matrix.ts.ejs +0 -171
  195. package/overrides/cairo/pattern.ts.ejs +0 -476
  196. package/overrides/cairo/recording-surface.ts.ejs +0 -102
  197. package/overrides/cairo/region.ts.ejs +0 -237
  198. package/overrides/cairo/scaled-font.ts.ejs +0 -330
  199. 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,3 +1,4 @@
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";
@@ -11,6 +12,12 @@ type GiNamespaceInput = {
11
12
  girFile: string;
12
13
  };
13
14
 
15
+ type GiExternalNamespaceInput = {
16
+ directory: string;
17
+ packageName: string;
18
+ girFile: string;
19
+ };
20
+
14
21
  const OVERRIDES_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "overrides");
15
22
 
16
23
  const overrideFiles = (directory: string): SourceModule[] => {
@@ -42,8 +49,17 @@ const barrelFile = (directory: string, girFile: string): SourceModule => {
42
49
  return overrideModule(barrel.fileName, overrideIndex);
43
50
  };
44
51
 
52
+ const externalShimModule = ({ directory, packageName, girFile }: GiExternalNamespaceInput): SourceModule => ({
53
+ fileName: `${directory}/index.ts`,
54
+ source:
55
+ `/** @deprecated Import from "${packageName}" instead; this alias is removed in GTKX 2.0. */\n` +
56
+ `export * from "${packageName}";\n`,
57
+ origin: girFile,
58
+ });
59
+
45
60
  const collectStoreSources = (
46
61
  namespaces: GiNamespaceInput[],
62
+ externalNamespaces: GiExternalNamespaceInput[],
47
63
  ): { collected: SourceModule[]; exportsMap: Record<string, unknown> } => {
48
64
  const exportsMap: Record<string, unknown> = {};
49
65
  const collected: SourceModule[] = [];
@@ -58,15 +74,29 @@ const collectStoreSources = (
58
74
  exportsMap[`./${directory}`] = subpathExport(`${directory}/index`);
59
75
  }
60
76
 
77
+ for (const external of externalNamespaces) {
78
+ collected.push(externalShimModule(external));
79
+ exportsMap[`./${external.directory}`] = subpathExport(`${external.directory}/index`);
80
+ }
81
+
61
82
  return { collected, exportsMap };
62
83
  };
63
84
 
85
+ const storePeerDependencies = (externalNamespaces: GiExternalNamespaceInput[]): Record<string, string> =>
86
+ Object.fromEntries(
87
+ sortStrings(["@gtkx/runtime", ...externalNamespaces.map((entry) => entry.packageName)]).map((name) => [
88
+ name,
89
+ "*",
90
+ ]),
91
+ );
92
+
64
93
  const writeGiStore = (
65
94
  options: StoreOptions,
66
95
  namespaces: GiNamespaceInput[],
96
+ externalNamespaces: GiExternalNamespaceInput[],
67
97
  fingerprint: GiFingerprint,
68
98
  ): void => {
69
- const { collected, exportsMap } = collectStoreSources(namespaces);
99
+ const { collected, exportsMap } = collectStoreSources(namespaces, externalNamespaces);
70
100
 
71
101
  writeStore({
72
102
  storeDir: options.storeDir,
@@ -76,10 +106,10 @@ const writeGiStore = (
76
106
  name: "@gtkx/gi",
77
107
  version: options.version,
78
108
  exports: exportsMap,
79
- peerDependencies: { "@gtkx/runtime": "*" },
109
+ peerDependencies: storePeerDependencies(externalNamespaces),
80
110
  }),
81
111
  rawFiles: [{ relativePath: FINGERPRINT_FILENAME, content: `${JSON.stringify(fingerprint, null, 2)}\n` }],
82
112
  });
83
113
  };
84
114
 
85
- export { writeGiStore, type GiNamespaceInput };
115
+ 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 => {
@@ -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,6 +24,7 @@ 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",
@@ -213,4 +214,4 @@ const resolveStore = (projectRoot: string): ResolvedStore => {
213
214
  };
214
215
  };
215
216
 
216
- export { getShadowingStorePaths, resolveStore, sweepProjectStaging, type ResolvedStore };
217
+ 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
  };
@@ -1,5 +1,6 @@
1
1
  import type { Library } from "../gir/library.js";
2
2
  import type { GirNamespace } from "../gir/namespace.js";
3
+ import { externalPackageFor } from "../gir/external-namespaces.js";
3
4
  import { type Declaration, ModuleBuilder } from "./module.js";
4
5
 
5
6
  class ModuleContext {
@@ -34,6 +35,7 @@ class ModuleContext {
34
35
  }
35
36
 
36
37
  this.module.imports.addSideEffect("../gobject/overrides/object.js");
38
+ this.module.imports.addSideEffect("../gobject/overrides/param-spec-getters.js");
37
39
  this.module.imports.addSideEffect("../gobject/overrides/value.js");
38
40
  }
39
41
 
@@ -42,6 +44,15 @@ class ModuleContext {
42
44
  return namespaceName;
43
45
  }
44
46
 
47
+ const packageName = externalPackageFor(namespaceName);
48
+
49
+ if (packageName !== undefined) {
50
+ this.module.imports.addSideEffect(packageName);
51
+ this.module.imports.addNamespace(packageName, namespaceName);
52
+
53
+ return namespaceName;
54
+ }
55
+
45
56
  const directory = namespaceName.toLowerCase();
46
57
  const isFoundational = directory === "gobject" || directory === "glib";
47
58
  const path = isFoundational ? `../${directory}/${directory}.js` : `../${directory}/index.js`;
@@ -1,6 +1,14 @@
1
- import { camelCase } from "@gtkx/utils";
1
+ import { camelCase, kebabCase } from "@gtkx/utils";
2
+ import { HIDDEN_SYMBOLS } from "../gir/hidden-symbols.js";
2
3
 
3
4
  const CONSTANT_MAP: Record<string, string> = { TRUE: "true", FALSE: "false", NULL: "null" };
5
+
6
+ const HIDDEN_LINK_PREFIXES: Map<string, string> = new Map([
7
+ ["GLib", "g"],
8
+ ["GObject", "g"],
9
+ ["Gio", "g"],
10
+ ]);
11
+
4
12
  const CALLABLE_LINK_KINDS: Set<string> = new Set(["method", "func", "ctor", "vfunc", "id"]);
5
13
  const SENTINEL = String.fromCodePoint(0xE0_00);
6
14
  const CODE_BLOCK_OPEN = "|[";
@@ -27,8 +35,37 @@ const DOCBOOK_LIST_PATTERN = /<\/?itemizedlist>\s*/g;
27
35
  const DOCBOOK_PARA_PATTERN = /<\/?para>\s*/g;
28
36
  const BLANK_LINE_RUN_PATTERN = /\n{3,}/g;
29
37
 
38
+ const cSymbolSegment = (segment: string): string => kebabCase(segment).replaceAll("-", "_");
39
+
40
+ const hiddenLinkCandidate = (segments: string[]): string | undefined => {
41
+ const [namespace, ...rest] = segments;
42
+ const prefix = namespace === undefined ? undefined : HIDDEN_LINK_PREFIXES.get(namespace);
43
+
44
+ if (prefix === undefined || rest.length === 0) {
45
+ return undefined;
46
+ }
47
+
48
+ return [prefix, ...rest.map((segment) => cSymbolSegment(segment))].join("_");
49
+ };
50
+
51
+ const hiddenCIdentifier = (kind: string, target: string, segments: string[]): string | undefined => {
52
+ if (!CALLABLE_LINK_KINDS.has(kind)) {
53
+ return undefined;
54
+ }
55
+
56
+ const candidate = kind === "id" ? target : hiddenLinkCandidate(segments);
57
+
58
+ return candidate !== undefined && HIDDEN_SYMBOLS.has(candidate) ? candidate : undefined;
59
+ };
60
+
30
61
  const renderLink = (kind: string, target: string): string => {
31
62
  const segments = target.split(/::|[.:]/);
63
+ const hidden = hiddenCIdentifier(kind, target, segments);
64
+
65
+ if (hidden !== undefined) {
66
+ return `\`${hidden}()\``;
67
+ }
68
+
32
69
  const lastIndex = segments.length - 1;
33
70
 
34
71
  if (kind === "property" || CALLABLE_LINK_KINDS.has(kind)) {