@gtkx/codegen 1.5.0 → 1.6.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 (175) hide show
  1. package/dist/analysis/descriptor-render.d.ts +1 -0
  2. package/dist/analysis/descriptor-render.d.ts.map +1 -1
  3. package/dist/analysis/descriptor-render.js +56 -28
  4. package/dist/analysis/descriptor-render.js.map +1 -1
  5. package/dist/analysis/descriptor.d.ts +3 -1
  6. package/dist/analysis/descriptor.d.ts.map +1 -1
  7. package/dist/analysis/descriptor.js +10 -9
  8. package/dist/analysis/descriptor.js.map +1 -1
  9. package/dist/compile.d.ts.map +1 -1
  10. package/dist/compile.js +24 -3
  11. package/dist/compile.js.map +1 -1
  12. package/dist/docs/api-reference.d.ts +2 -0
  13. package/dist/docs/api-reference.d.ts.map +1 -1
  14. package/dist/docs/api-reference.js +2 -1
  15. package/dist/docs/api-reference.js.map +1 -1
  16. package/dist/docs/pipeline.d.ts +3 -0
  17. package/dist/docs/pipeline.d.ts.map +1 -1
  18. package/dist/docs/pipeline.js +44 -12
  19. package/dist/docs/pipeline.js.map +1 -1
  20. package/dist/fingerprint.d.ts +3 -0
  21. package/dist/fingerprint.d.ts.map +1 -1
  22. package/dist/fingerprint.js +4 -0
  23. package/dist/fingerprint.js.map +1 -1
  24. package/dist/gi.d.ts +1 -0
  25. package/dist/gi.d.ts.map +1 -1
  26. package/dist/gi.js +4 -1
  27. package/dist/gi.js.map +1 -1
  28. package/dist/gir/libraries.d.ts +12 -5
  29. package/dist/gir/libraries.d.ts.map +1 -1
  30. package/dist/gir/libraries.js +50 -14
  31. package/dist/gir/libraries.js.map +1 -1
  32. package/dist/gir/library.d.ts +5 -0
  33. package/dist/gir/library.d.ts.map +1 -1
  34. package/dist/gir/library.js +6 -0
  35. package/dist/gir/library.js.map +1 -1
  36. package/dist/internal.d.ts +1 -0
  37. package/dist/internal.d.ts.map +1 -1
  38. package/dist/internal.js +1 -0
  39. package/dist/internal.js.map +1 -1
  40. package/dist/jsx.d.ts +1 -0
  41. package/dist/jsx.d.ts.map +1 -1
  42. package/dist/jsx.js +2 -0
  43. package/dist/jsx.js.map +1 -1
  44. package/dist/runner.d.ts +2 -0
  45. package/dist/runner.d.ts.map +1 -1
  46. package/dist/runner.js +3 -1
  47. package/dist/runner.js.map +1 -1
  48. package/dist/store/gi/bootstrap.d.ts +4 -0
  49. package/dist/store/gi/bootstrap.d.ts.map +1 -0
  50. package/dist/store/gi/bootstrap.js +48 -0
  51. package/dist/store/gi/bootstrap.js.map +1 -0
  52. package/dist/store/gi/class.d.ts.map +1 -1
  53. package/dist/store/gi/class.js +75 -28
  54. package/dist/store/gi/class.js.map +1 -1
  55. package/dist/store/gi/constructor-props.d.ts.map +1 -1
  56. package/dist/store/gi/constructor-props.js +2 -1
  57. package/dist/store/gi/constructor-props.js.map +1 -1
  58. package/dist/store/gi/enum.js +3 -3
  59. package/dist/store/gi/enum.js.map +1 -1
  60. package/dist/store/gi/folded.d.ts +14 -0
  61. package/dist/store/gi/folded.d.ts.map +1 -0
  62. package/dist/store/gi/folded.js +35 -0
  63. package/dist/store/gi/folded.js.map +1 -0
  64. package/dist/store/gi/function.d.ts.map +1 -1
  65. package/dist/store/gi/function.js +22 -3
  66. package/dist/store/gi/function.js.map +1 -1
  67. package/dist/store/gi/gtype-binding.d.ts.map +1 -1
  68. package/dist/store/gi/gtype-binding.js +11 -1
  69. package/dist/store/gi/gtype-binding.js.map +1 -1
  70. package/dist/store/gi/interface.d.ts.map +1 -1
  71. package/dist/store/gi/interface.js +43 -4
  72. package/dist/store/gi/interface.js.map +1 -1
  73. package/dist/store/gi/method.d.ts.map +1 -1
  74. package/dist/store/gi/method.js +2 -0
  75. package/dist/store/gi/method.js.map +1 -1
  76. package/dist/store/gi/pipeline.d.ts +5 -1
  77. package/dist/store/gi/pipeline.d.ts.map +1 -1
  78. package/dist/store/gi/pipeline.js +13 -1
  79. package/dist/store/gi/pipeline.js.map +1 -1
  80. package/dist/store/gi/property-accessor.js +1 -1
  81. package/dist/store/gi/property-accessor.js.map +1 -1
  82. package/dist/store/gi/record-field-accessor.d.ts.map +1 -1
  83. package/dist/store/gi/record-field-accessor.js +3 -3
  84. package/dist/store/gi/record-field-accessor.js.map +1 -1
  85. package/dist/store/gi/record.d.ts.map +1 -1
  86. package/dist/store/gi/record.js +44 -12
  87. package/dist/store/gi/record.js.map +1 -1
  88. package/dist/store/gi/registration.js +2 -2
  89. package/dist/store/gi/registration.js.map +1 -1
  90. package/dist/store/gi/return-wrap.js +1 -1
  91. package/dist/store/gi/return-wrap.js.map +1 -1
  92. package/dist/store/gi/signal.d.ts.map +1 -1
  93. package/dist/store/gi/signal.js +7 -3
  94. package/dist/store/gi/signal.js.map +1 -1
  95. package/dist/store/gi/vtable.d.ts.map +1 -1
  96. package/dist/store/gi/vtable.js +1 -1
  97. package/dist/store/gi/vtable.js.map +1 -1
  98. package/dist/store/gi-store.d.ts +1 -0
  99. package/dist/store/gi-store.d.ts.map +1 -1
  100. package/dist/store/gi-store.js +16 -7
  101. package/dist/store/gi-store.js.map +1 -1
  102. package/dist/store/jsx/element-components.d.ts.map +1 -1
  103. package/dist/store/jsx/element-components.js +64 -10
  104. package/dist/store/jsx/element-components.js.map +1 -1
  105. package/dist/store/jsx/element-prop-types.d.ts +2 -0
  106. package/dist/store/jsx/element-prop-types.d.ts.map +1 -1
  107. package/dist/store/jsx/element-prop-types.js +2 -0
  108. package/dist/store/jsx/element-prop-types.js.map +1 -1
  109. package/dist/store/jsx/metadata.d.ts.map +1 -1
  110. package/dist/store/jsx/metadata.js +28 -1
  111. package/dist/store/jsx/metadata.js.map +1 -1
  112. package/dist/store/jsx-store.d.ts +1 -0
  113. package/dist/store/jsx-store.d.ts.map +1 -1
  114. package/dist/store/jsx-store.js +7 -2
  115. package/dist/store/jsx-store.js.map +1 -1
  116. package/dist/store/store-fs.d.ts +2 -1
  117. package/dist/store/store-fs.d.ts.map +1 -1
  118. package/dist/store/store-fs.js +1 -1
  119. package/dist/store/store-fs.js.map +1 -1
  120. package/dist/writer/context.d.ts +17 -0
  121. package/dist/writer/context.d.ts.map +1 -1
  122. package/dist/writer/context.js +83 -11
  123. package/dist/writer/context.js.map +1 -1
  124. package/dist/writer/emit.d.ts +2 -1
  125. package/dist/writer/emit.d.ts.map +1 -1
  126. package/dist/writer/emit.js +2 -1
  127. package/dist/writer/emit.js.map +1 -1
  128. package/dist/writer/imports.d.ts.map +1 -1
  129. package/dist/writer/imports.js +6 -1
  130. package/dist/writer/imports.js.map +1 -1
  131. package/dist/writer/module.d.ts +1 -0
  132. package/dist/writer/module.d.ts.map +1 -1
  133. package/dist/writer/module.js +6 -1
  134. package/dist/writer/module.js.map +1 -1
  135. package/overrides/glib/regex.ts.ejs +2 -0
  136. package/package.json +4 -4
  137. package/src/analysis/descriptor-render.ts +79 -30
  138. package/src/analysis/descriptor.ts +19 -11
  139. package/src/compile.ts +42 -3
  140. package/src/docs/api-reference.ts +4 -1
  141. package/src/docs/pipeline.ts +64 -13
  142. package/src/fingerprint.ts +7 -0
  143. package/src/gi.ts +6 -1
  144. package/src/gir/libraries.ts +64 -16
  145. package/src/gir/library.ts +9 -0
  146. package/src/internal.ts +1 -0
  147. package/src/jsx.ts +3 -0
  148. package/src/runner.ts +5 -1
  149. package/src/store/gi/bootstrap.ts +64 -0
  150. package/src/store/gi/class.ts +100 -29
  151. package/src/store/gi/constructor-props.ts +2 -1
  152. package/src/store/gi/enum.ts +4 -4
  153. package/src/store/gi/folded.ts +57 -0
  154. package/src/store/gi/function.ts +22 -3
  155. package/src/store/gi/gtype-binding.ts +15 -2
  156. package/src/store/gi/interface.ts +64 -7
  157. package/src/store/gi/method.ts +2 -0
  158. package/src/store/gi/pipeline.ts +21 -2
  159. package/src/store/gi/property-accessor.ts +1 -1
  160. package/src/store/gi/record-field-accessor.ts +5 -3
  161. package/src/store/gi/record.ts +61 -14
  162. package/src/store/gi/registration.ts +2 -2
  163. package/src/store/gi/return-wrap.ts +1 -1
  164. package/src/store/gi/signal.ts +8 -3
  165. package/src/store/gi/vtable.ts +1 -0
  166. package/src/store/gi-store.ts +19 -7
  167. package/src/store/jsx/element-components.ts +107 -10
  168. package/src/store/jsx/element-prop-types.ts +4 -0
  169. package/src/store/jsx/metadata.ts +41 -0
  170. package/src/store/jsx-store.ts +10 -4
  171. package/src/store/store-fs.ts +3 -2
  172. package/src/writer/context.ts +109 -16
  173. package/src/writer/emit.ts +3 -1
  174. package/src/writer/imports.ts +9 -1
  175. package/src/writer/module.ts +9 -1
@@ -21,6 +21,7 @@ import {
21
21
  renderStaticHead,
22
22
  } from "./callables.js";
23
23
  import { annotationSpec, getDoc } from "./doc-spec.js";
24
+ import { declareFoldedClass, localClassName } from "./folded.js";
24
25
  import { gtypeMemberDeclaration, renderSourceGtype } from "./gtype-binding.js";
25
26
  import { methodExportName } from "./method.js";
26
27
  import {
@@ -89,8 +90,15 @@ const generateInterface = (context: ModuleContext, iface: GirClass): void => {
89
90
  generateBindings(context, callables);
90
91
  const gtypeExpr = renderSourceGtype(context, iface);
91
92
  const implRef = appendInterfaceTypes(context, iface, className, callables) ?? "unknown";
93
+
94
+ if (gtypeExpr !== undefined && context.isTreeShaken) {
95
+ declareTreeShakenInterface(context, { iface, className, callables, gtypeExpr, implRef });
96
+
97
+ return;
98
+ }
99
+
92
100
  const classCode = renderInterfaceClass(context, { iface, className, callables, gtypeExpr, implRef });
93
- context.declare({ name: className, code: classCode, owner: iface.name });
101
+ context.declare({ name: className, code: `${getDoc(iface)}${classCode}`, owner: iface.name });
94
102
 
95
103
  for (const declaration of renderSignalDeclarations(context, iface, className, true)) {
96
104
  context.declare(declaration);
@@ -109,6 +117,53 @@ const generateInterface = (context: ModuleContext, iface: GirClass): void => {
109
117
  });
110
118
  };
111
119
 
120
+ const declareTreeShakenInterface = (
121
+ context: ModuleContext,
122
+ options: Omit<InterfaceClassOptions, "gtypeExpr"> & { gtypeExpr: string },
123
+ ): void => {
124
+ const { iface, className, callables } = options;
125
+
126
+ for (const declaration of renderSignalDeclarations(context, iface, className, true)) {
127
+ context.declare(declaration);
128
+ }
129
+
130
+ context.declare({
131
+ name: makerName(className),
132
+ code: renderInterfaceMaker(context, iface, className, callables),
133
+ });
134
+
135
+ generateFoldedInterface(context, options);
136
+ };
137
+
138
+ const generateFoldedInterface = (
139
+ context: ModuleContext,
140
+ options: Omit<InterfaceClassOptions, "gtypeExpr"> & { gtypeExpr: string },
141
+ ): void => {
142
+ const { iface, className, callables, gtypeExpr, implRef } = options;
143
+ const localName = localClassName(className);
144
+ context.beginRegistrations();
145
+
146
+ appendInterfaceRegistration(context, {
147
+ className: localName,
148
+ makerName: makerName(className),
149
+ gtypeExpr,
150
+ layout: renderInterfaceLayout(context, iface, callables),
151
+ });
152
+
153
+ const registrations = context.takeRegistrations();
154
+ const classCode = renderInterfaceClass(context, { iface, className, callables, gtypeExpr, implRef }, localName);
155
+
156
+ declareFoldedClass({
157
+ context,
158
+ className,
159
+ doc: getDoc(iface),
160
+ owner: iface.name,
161
+ localDeclaration: classCode,
162
+ registrations,
163
+ hasInstanceInterface: false,
164
+ });
165
+ };
166
+
112
167
  const makerName = (className: string): string => `make${className}`;
113
168
 
114
169
  const renderInterfaceLayout = (
@@ -373,8 +428,12 @@ const renderInterfaceTypeMembers = (context: ModuleContext, iface: GirClass, cal
373
428
  ...interfaceVfuncMembers(context, iface, "signature"),
374
429
  ];
375
430
 
376
- const renderInterfaceClass = (context: ModuleContext, options: InterfaceClassOptions): string => {
377
- const { iface, className, callables, gtypeExpr, implRef } = options;
431
+ const renderInterfaceClass = (
432
+ context: ModuleContext,
433
+ options: InterfaceClassOptions,
434
+ localName?: string,
435
+ ): string => {
436
+ const { className, callables, gtypeExpr, implRef } = options;
378
437
  const members: string[] = [];
379
438
 
380
439
  if (gtypeExpr !== undefined) {
@@ -382,11 +441,9 @@ const renderInterfaceClass = (context: ModuleContext, options: InterfaceClassOpt
382
441
  }
383
442
 
384
443
  members.push(...renderStaticHead(context, callables, className), renderInterfaceBrand(context, implRef));
444
+ const head = localName === undefined ? `export abstract class ${className}` : `abstract class ${localName}`;
385
445
 
386
- return `${getDoc(iface)}${renderBracedOrEmpty(
387
- `export abstract class ${className}`,
388
- members.join("\n\n"),
389
- )}`;
446
+ return renderBracedOrEmpty(head, members.join("\n\n"));
390
447
  };
391
448
 
392
449
  const renderInterfaceBrand = (context: ModuleContext, implRef: string): string => {
@@ -438,6 +438,7 @@ const renderReturnDescriptor = (context: ModuleContext, fn: GirFunction): Return
438
438
  argIndexOffset: instanceOffset,
439
439
  argIndexMap: emittedArgIndices(fn, instanceOffset),
440
440
  isNewlyCreated: fn.instance === undefined,
441
+ isReceived: true,
441
442
  });
442
443
 
443
444
  return {
@@ -568,6 +569,7 @@ const planOutParam = (
568
569
  ...argIndex,
569
570
  cursor: parameter.cursor,
570
571
  hasOutIndirection: true,
572
+ isReceived: true,
571
573
  });
572
574
 
573
575
  return {
@@ -7,6 +7,7 @@ import { getParentRef } from "../../gir/ancestry.js";
7
7
  import { isEmittableEntity } from "../../gir/emittable.js";
8
8
  import { PRIMITIVE_TS_TYPE, primitiveCategory } from "../../gir/primitives.js";
9
9
  import { ModuleContext } from "../../writer/context.js";
10
+ import { renderBootstrapModule } from "./bootstrap.js";
10
11
  import { generateCallback } from "./callback.js";
11
12
  import { generateClass } from "./class.js";
12
13
  import { generateConstant } from "./constant.js";
@@ -24,7 +25,12 @@ type TopologicalState = {
24
25
  result: GirClass[];
25
26
  };
26
27
 
27
- const generateNamespaceModule = (namespace: GirNamespace, library: Library): string => {
28
+ type NamespaceModule = {
29
+ source: string;
30
+ bootstrapSource: string | undefined;
31
+ };
32
+
33
+ const generateNamespaceModule = (namespace: GirNamespace, library: Library): NamespaceModule => {
28
34
  const context = new ModuleContext(namespace, library);
29
35
  context.addGObjectBootstrapImports();
30
36
  generateNamespaceTypes(context, namespace);
@@ -38,7 +44,10 @@ const generateNamespaceModule = (namespace: GirNamespace, library: Library): str
38
44
  );
39
45
  }
40
46
 
41
- return context.module.toSource();
47
+ return {
48
+ source: context.module.toSource(),
49
+ bootstrapSource: library.isTreeShaken ? renderBootstrapModule(context) : undefined,
50
+ };
42
51
  };
43
52
 
44
53
  const generateNamespaceTypes = (context: ModuleContext, namespace: GirNamespace): void => {
@@ -50,10 +59,20 @@ const generateNamespaceTypes = (context: ModuleContext, namespace: GirNamespace)
50
59
  generateRecord(context, record);
51
60
  }
52
61
 
62
+ if (context.isTreeShaken) {
63
+ generateInterfaces(context, namespace);
64
+ }
65
+
53
66
  for (const klass of topologicalClassOrder(namespace.classes, namespace.name)) {
54
67
  generateClass(context, klass);
55
68
  }
56
69
 
70
+ if (!context.isTreeShaken) {
71
+ generateInterfaces(context, namespace);
72
+ }
73
+ };
74
+
75
+ const generateInterfaces = (context: ModuleContext, namespace: GirNamespace): void => {
57
76
  for (const iface of namespace.interfaces) {
58
77
  generateInterface(context, iface);
59
78
  }
@@ -185,7 +185,7 @@ const renderPropertyAccessorSignature = (args: PropertyAccessorArgs): string | u
185
185
  withAccessor(args, (accessor) => `${propertyDoc(args.property)}${renderAccessorPairSignature(accessor)}`);
186
186
 
187
187
  const renderPropertyDescriptor = (context: ModuleContext, property: GirProperty): string =>
188
- renderDescriptor(context, property.type, property.transferOwnership);
188
+ renderDescriptor(context, property.type, property.transferOwnership, { isReceived: true });
189
189
 
190
190
  const renderGenericGetBody = (context: ModuleContext, property: GirProperty, tsType: string): string => {
191
191
  context.addRuntimeImport("getObjectProperty");
@@ -285,7 +285,7 @@ const renderRecordFieldAccessor = (
285
285
  }
286
286
 
287
287
  const descriptor = context.hoistDescriptor(
288
- renderDescriptor(context, field.type, "none", { isInline: isInlineField(context, field) }),
288
+ renderDescriptor(context, field.type, "none", { isInline: isInlineField(context, field), isReceived: true }),
289
289
  );
290
290
 
291
291
  const tsType = fieldTsType(context, field.type);
@@ -501,7 +501,7 @@ const visitInlineStructSlot = (
501
501
  return;
502
502
  }
503
503
 
504
- const descriptor = context.hoistDescriptor(renderDescriptor(context, field.type, "none"));
504
+ const descriptor = context.hoistDescriptor(renderDescriptor(context, field.type, "none", { isReceived: true }));
505
505
  visitors.leaf({ jsName, descriptor, offset, slot, type: field.type });
506
506
  };
507
507
 
@@ -778,7 +778,9 @@ const renderInlineArrayAccessor = (context: ModuleContext, target: StructArrayTa
778
778
  context,
779
779
  jsName,
780
780
  tsType: renderTsType(context, field.type, false),
781
- descriptor: context.hoistDescriptor(renderDescriptor(context, resolution.element, "none", { isInline: true })),
781
+ descriptor: context.hoistDescriptor(
782
+ renderDescriptor(context, resolution.element, "none", { isInline: true, isReceived: true }),
783
+ ),
782
784
  offset: slot.byteOffset,
783
785
  resolution,
784
786
  };
@@ -5,6 +5,7 @@ import { isEmittableEntity } from "../../gir/emittable.js";
5
5
  import { indentMembers } from "../../writer/emit.js";
6
6
  import { type Callables, dedupeCallables, generateBindings, renderPlainTypeMembers } from "./callables.js";
7
7
  import { getDoc } from "./doc-spec.js";
8
+ import { declareFoldedClass, localClassName } from "./folded.js";
8
9
  import { renderSourceGtype } from "./gtype-binding.js";
9
10
  import { renderRecordConstructor, renderRecordConstructorPropsInterface } from "./record-constructor.js";
10
11
  import { renderRecordFieldAccessor } from "./record-field-accessor.js";
@@ -12,9 +13,52 @@ import { computeRecordFieldSlots } from "./record-layout.js";
12
13
  import { appendWrapperClassRegistration } from "./registration.js";
13
14
  import { isConstructibleRecord } from "./value-marshalable.js";
14
15
 
16
+ type FoldedRecordOptions = {
17
+ record: GirRecord;
18
+ className: string;
19
+ doc: string;
20
+ modifier: string;
21
+ heritage: string;
22
+ body: string;
23
+ gtypeExpr: string;
24
+ };
25
+
15
26
  const isGErrorRecord = (context: ModuleContext, record: GirRecord): boolean =>
16
27
  context.namespace.name === "GLib" && record.glibGetType === "g_error_get_type";
17
28
 
29
+ const recordHeritage = (context: ModuleContext, isErrorSubclass: boolean): string => {
30
+ if (!isErrorSubclass) {
31
+ return "";
32
+ }
33
+
34
+ const base = context.isTreeShaken ? context.hoistBaseRef("globalThis.Error") : "globalThis.Error";
35
+
36
+ return ` extends ${base}`;
37
+ };
38
+
39
+ const recordModifier = (isConstructible: boolean): string => (isConstructible ? "" : "abstract ");
40
+
41
+ const declareFoldedRecord = (context: ModuleContext, options: FoldedRecordOptions): void => {
42
+ const { record, className, doc, modifier, heritage, body, gtypeExpr } = options;
43
+ const localName = localClassName(className);
44
+ context.beginRegistrations();
45
+
46
+ appendWrapperClassRegistration(context, {
47
+ className: localName,
48
+ gtypeExpr,
49
+ });
50
+
51
+ declareFoldedClass({
52
+ context,
53
+ className,
54
+ doc,
55
+ owner: record.name,
56
+ localDeclaration: `${modifier}class ${localName}${heritage} {\n${body}\n}`,
57
+ registrations: context.takeRegistrations(),
58
+ hasInstanceInterface: true,
59
+ });
60
+ };
61
+
18
62
  const generateRecord = (context: ModuleContext, record: GirRecord): void => {
19
63
  if (!isEmittableEntity(record)) {
20
64
  return;
@@ -32,16 +76,26 @@ const generateRecord = (context: ModuleContext, record: GirRecord): void => {
32
76
  generateBindings(context, callables);
33
77
  const members = renderRecordMembers(context, record, className, callables);
34
78
  const body = indentMembers(members);
35
- const heritage = isErrorSubclass ? " extends globalThis.Error" : "";
79
+ const heritage = recordHeritage(context, isErrorSubclass);
36
80
  const doc = getDoc(record);
37
81
  const isConstructible = isConstructibleRecord(context, context.namespace.name, record);
38
- const modifier = isConstructible ? "" : "abstract ";
82
+ const modifier = recordModifier(isConstructible);
83
+ const gtypeExpr = renderSourceGtype(context, record);
39
84
 
40
- context.declare({
41
- name: className,
42
- code: `${doc}export ${modifier}class ${className}${heritage} {\n${body}\n}`,
43
- owner: record.name,
44
- });
85
+ if (gtypeExpr !== undefined && context.isTreeShaken) {
86
+ declareFoldedRecord(context, { record, className, doc, modifier, heritage, body, gtypeExpr });
87
+ } else {
88
+ context.declare({
89
+ name: className,
90
+ code: `${doc}export ${modifier}class ${className}${heritage} {\n${body}\n}`,
91
+ owner: record.name,
92
+ });
93
+
94
+ appendWrapperClassRegistration(context, {
95
+ className,
96
+ gtypeExpr,
97
+ });
98
+ }
45
99
 
46
100
  if (isConstructible) {
47
101
  context.declare({
@@ -49,13 +103,6 @@ const generateRecord = (context: ModuleContext, record: GirRecord): void => {
49
103
  code: renderRecordConstructorPropsInterface(context, record, className),
50
104
  });
51
105
  }
52
-
53
- const gtypeExpr = renderSourceGtype(context, record);
54
-
55
- appendWrapperClassRegistration(context, {
56
- className,
57
- gtypeExpr,
58
- });
59
106
  };
60
107
 
61
108
  const renderRecordMembers = (
@@ -25,7 +25,7 @@ const appendWrapperClassRegistration = (
25
25
 
26
26
  context.addRuntimeImport("registerWrapperClass");
27
27
  const args = vfuncs === undefined ? gtypeExpr : `${gtypeExpr}, ${vfuncs}`;
28
- context.module.appendRegistration(`registerWrapperClass(${className}, ${args});`, [className]);
28
+ context.collectRegistration(`registerWrapperClass(${className}, ${args});`, [className]);
29
29
  };
30
30
 
31
31
  const appendInterfaceRegistration = (context: ModuleContext, registration: InterfaceRegistration): void => {
@@ -38,7 +38,7 @@ const appendInterfaceRegistration = (context: ModuleContext, registration: Inter
38
38
  context.addRuntimeImport("registerInterface");
39
39
  const base = `${className}, ${gtypeExpr}, ${makerName}`;
40
40
  const args = layout === undefined ? base : `${base}, ${layout}`;
41
- context.module.appendRegistration(`registerInterface(${args});`, [className, makerName]);
41
+ context.collectRegistration(`registerInterface(${args});`, [className, makerName]);
42
42
  };
43
43
 
44
44
  export { appendWrapperClassRegistration, appendInterfaceRegistration };
@@ -69,7 +69,7 @@ const wrapAlias = (
69
69
 
70
70
  const wrapValue = (context: ModuleContext, ref: TypeId, valueExpression: string): string => {
71
71
  context.addRuntimeImport("fromNative");
72
- const descriptor = context.hoistDescriptor(renderDescriptor(context, ref, "none"));
72
+ const descriptor = context.hoistDescriptor(renderDescriptor(context, ref, "none", { isReceived: true }));
73
73
 
74
74
  return `(fromNative(${descriptor}, ${valueExpression}) as ${renderTsType(context, ref, false)})`;
75
75
  };
@@ -318,7 +318,7 @@ const renderEmitReturnArg = (context: ModuleContext, returnValue: GirReturnValue
318
318
  return "";
319
319
  }
320
320
 
321
- return `, ${renderDescriptor(context, returnValue.type, returnValue.transferOwnership)}`;
321
+ return `, ${renderDescriptor(context, returnValue.type, returnValue.transferOwnership, { isReceived: true })}`;
322
322
  };
323
323
 
324
324
  const renderEmitCase = (context: ModuleContext, signal: GirCallable): string => {
@@ -332,7 +332,10 @@ const renderEmitCase = (context: ModuleContext, signal: GirCallable): string =>
332
332
  let argIndex = 0;
333
333
 
334
334
  const argLiterals = params.map((parameter) => {
335
- const descriptor = renderDescriptor(context, parameter.type, parameter.transferOwnership);
335
+ const descriptor = renderDescriptor(context, parameter.type, parameter.transferOwnership, {
336
+ isReceived: true,
337
+ });
338
+
336
339
  const rendered = renderEmitArgLiteral({ context, parameter, descriptor, argIndex });
337
340
  argIndex = rendered.nextArgIndex;
338
341
 
@@ -366,7 +369,9 @@ const renderCallback = (context: ModuleContext, signal: GirCallable): string =>
366
369
 
367
370
  return tCallback({
368
371
  argTypes: callbackArgs,
369
- returns: renderDescriptor(context, signal.returnValue.type, signal.returnValue.transferOwnership),
372
+ returns: renderDescriptor(context, signal.returnValue.type, signal.returnValue.transferOwnership, {
373
+ isReceived: true,
374
+ }),
370
375
  options: [
371
376
  "hasDestroy: true",
372
377
  "destroyKind: \"closureNotify\"",
@@ -627,6 +627,7 @@ const renderVtableSlotDescriptor = (context: ModuleContext, vtable: Vtable, slot
627
627
  context,
628
628
  callback.returnValue.type,
629
629
  callback.returnValue.transferOwnership,
630
+ { isReceived: true },
630
631
  );
631
632
 
632
633
  const lines = [
@@ -14,6 +14,7 @@ import { buildManifest, namespaceBarrel, type StoreOptions, subpathExport, write
14
14
  type GiNamespaceInput = {
15
15
  directory: string;
16
16
  rawSource: string;
17
+ rawBootstrapSource?: string | undefined;
17
18
  girFile: string;
18
19
  };
19
20
 
@@ -29,6 +30,7 @@ type GiStoreRecords = {
29
30
  };
30
31
 
31
32
  const OVERRIDES_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "overrides");
33
+ const TREE_SHAKEN_SIDE_EFFECTS = ["**/bootstrap.js", "**/overrides/*.js", "**/index.js"];
32
34
 
33
35
  const overrideFiles = (directory: string): SourceModule[] => {
34
36
  const dir = join(OVERRIDES_ROOT, directory);
@@ -48,21 +50,25 @@ const overrideModule = (fileName: string, overridePath: string): SourceModule =>
48
50
  origin: overridePath,
49
51
  });
50
52
 
51
- const barrelFile = (directory: string, girFile: string): SourceModule => {
53
+ const barrelFile = (directory: string, girFile: string, hasBootstrap: boolean): SourceModule => {
52
54
  const barrel = namespaceBarrel(directory);
53
55
  const overrideIndex = join(OVERRIDES_ROOT, directory, "index.ts.ejs");
54
56
 
55
- if (!existsSync(overrideIndex)) {
56
- return { ...barrel, origin: girFile };
57
+ const file = existsSync(overrideIndex)
58
+ ? overrideModule(barrel.fileName, overrideIndex)
59
+ : { ...barrel, origin: girFile };
60
+
61
+ if (!hasBootstrap) {
62
+ return file;
57
63
  }
58
64
 
59
- return overrideModule(barrel.fileName, overrideIndex);
65
+ return { ...file, source: `import "./bootstrap.js";\n${file.source}` };
60
66
  };
61
67
 
62
68
  const externalShimModule = ({ directory, packageName, girFile }: GiExternalNamespaceInput): SourceModule => ({
63
69
  fileName: `${directory}/index.ts`,
64
70
  source:
65
- `/** @deprecated Import from "${packageName}" instead; this alias is removed in GTKX 2.0. */\n` +
71
+ `/** @deprecated Since 1.3. Import from "${packageName}" instead. Removed in v2. */\n` +
66
72
  `export * from "${packageName}";\n`,
67
73
  origin: girFile,
68
74
  });
@@ -74,13 +80,17 @@ const collectStoreSources = (
74
80
  const exportsMap: Record<string, unknown> = {};
75
81
  const collected: SourceModule[] = [];
76
82
 
77
- for (const { directory, rawSource, girFile } of namespaces) {
83
+ for (const { directory, rawSource, rawBootstrapSource, girFile } of namespaces) {
78
84
  collected.push(
79
85
  { fileName: `${directory}/${directory}.ts`, source: rawSource, origin: girFile },
80
86
  ...overrideFiles(directory),
81
- barrelFile(directory, girFile),
87
+ barrelFile(directory, girFile, rawBootstrapSource !== undefined),
82
88
  );
83
89
 
90
+ if (rawBootstrapSource !== undefined) {
91
+ collected.push({ fileName: `${directory}/bootstrap.ts`, source: rawBootstrapSource, origin: girFile });
92
+ }
93
+
84
94
  exportsMap[`./${directory}`] = subpathExport(`${directory}/index`);
85
95
  }
86
96
 
@@ -107,6 +117,7 @@ const writeGiStore = (
107
117
  records: GiStoreRecords,
108
118
  ): void => {
109
119
  const { collected, exportsMap } = collectStoreSources(namespaces, externalNamespaces);
120
+ const isTreeShaken = namespaces.some((namespace) => namespace.rawBootstrapSource !== undefined);
110
121
 
111
122
  writeStore({
112
123
  storeDir: options.storeDir,
@@ -116,6 +127,7 @@ const writeGiStore = (
116
127
  name: "@gtkx/gi",
117
128
  version: options.version,
118
129
  exports: exportsMap,
130
+ sideEffects: isTreeShaken ? TREE_SHAKEN_SIDE_EFFECTS : true,
119
131
  peerDependencies: storePeerDependencies(externalNamespaces),
120
132
  }),
121
133
  rawFiles: [
@@ -1,8 +1,9 @@
1
- import { sourceStringLiteral } from "@gtkx/utils";
1
+ import { sanitizeTypeIdentifier, sourceStringLiteral } from "@gtkx/utils";
2
2
  import type { Library } from "../../gir/library.js";
3
3
  import type { GirNamespace } from "../../gir/namespace.js";
4
4
  import type { ImportsBuilder } from "../../writer/imports.js";
5
5
  import type { LazyElementSpec } from "./element-prop-types.js";
6
+ import { externalPackageFor } from "../../gir/external-namespaces.js";
6
7
  import { getDoc } from "../gi/doc-spec.js";
7
8
  import { ancestorGlibNames, type GlibNamedClass } from "./intrinsic-elements.js";
8
9
 
@@ -23,6 +24,13 @@ type CandidateExportOptions = {
23
24
  components: Record<string, ElementComponent>;
24
25
  };
25
26
 
27
+ type LazyMetadataOptions = {
28
+ isTreeShaken: boolean;
29
+ intrinsicNames: Set<string>;
30
+ };
31
+
32
+ const METADATA_ALIAS = "Metadata$";
33
+
26
34
  const generateElementComponentsSection = (
27
35
  targetNamespace: GirNamespace,
28
36
  library: Library,
@@ -45,7 +53,11 @@ const generateElementComponentsSection = (
45
53
  components: options.components,
46
54
  });
47
55
 
48
- collectLazyElementExports(collector, lazyElements);
56
+ collectLazyElementExports(collector, lazyElements, {
57
+ isTreeShaken: library.isTreeShaken,
58
+ intrinsicNames: new Set(options.intrinsicElements.map((entry) => entry.glibName)),
59
+ });
60
+
49
61
  const source = collector.exportLines.join("\n\n");
50
62
 
51
63
  return { source, exportedNames: collector.exportedNames };
@@ -81,18 +93,75 @@ const collectCandidateExports = (collector: ExportCollector, options: CandidateE
81
93
  }
82
94
  };
83
95
 
84
- const collectLazyElementExports = (collector: ExportCollector, lazyElements: LazyElementSpec[]): void => {
96
+ const collectLazyElementExports = (
97
+ collector: ExportCollector,
98
+ lazyElements: LazyElementSpec[],
99
+ options: LazyMetadataOptions,
100
+ ): void => {
85
101
  for (const spec of lazyElements) {
86
102
  collector.imports.addNamed("@gtkx/react/internal", "createElementComponent", false);
87
103
  collector.imports.addNamed("react", "ReactNode", true);
88
- collector.exportLines.push(renderLazyElementExport(spec));
104
+
105
+ const refs = {
106
+ classRef: lazyClassRef(collector, spec, options),
107
+ metadataRef: lazyMetadataRef(collector, spec, options),
108
+ };
109
+
110
+ collector.exportLines.push(renderLazyElementExport(spec, refs, options));
89
111
  collector.exportedNames.add(spec.element);
90
112
  }
91
113
  };
92
114
 
93
- const renderLazyElementExport = (spec: LazyElementSpec): string => {
115
+ const lazyMetadataRef = (
116
+ collector: ExportCollector,
117
+ spec: LazyElementSpec,
118
+ options: LazyMetadataOptions,
119
+ ): string | undefined => {
120
+ if (!options.isTreeShaken || !options.intrinsicNames.has(spec.element)) {
121
+ return undefined;
122
+ }
123
+
124
+ collector.imports.addNamespace("../metadata.js", METADATA_ALIAS, false);
125
+
126
+ return `${METADATA_ALIAS}.${spec.element}`;
127
+ };
128
+
129
+ const lazyClassRef = (
130
+ collector: ExportCollector,
131
+ spec: LazyElementSpec,
132
+ options: LazyMetadataOptions,
133
+ ): string | undefined => {
134
+ if (!options.isTreeShaken) {
135
+ return undefined;
136
+ }
137
+
138
+ const alias = `${spec.namespaceName}$`;
139
+ const specifier = externalPackageFor(spec.namespaceName) ?? `@gtkx/gi/${spec.namespaceName.toLowerCase()}`;
140
+ collector.imports.addNamespace(specifier, alias, false);
141
+
142
+ return `${alias}.${sanitizeTypeIdentifier(spec.className)}`;
143
+ };
144
+
145
+ const renderLazyElementExport = (
146
+ spec: LazyElementSpec,
147
+ refs: { classRef: string | undefined; metadataRef: string | undefined },
148
+ options: LazyMetadataOptions,
149
+ ): string => {
94
150
  const doc = getDoc(spec);
95
- const factory = `createElementComponent(${sourceStringLiteral(spec.element)})`;
151
+ const args = [sourceStringLiteral(spec.element)];
152
+
153
+ if (refs.classRef !== undefined) {
154
+ args.push(refs.classRef);
155
+ } else if (refs.metadataRef !== undefined) {
156
+ args.push("undefined");
157
+ }
158
+
159
+ if (refs.metadataRef !== undefined) {
160
+ args.push(refs.metadataRef);
161
+ }
162
+
163
+ const pure = options.isTreeShaken ? "/* @__PURE__ */ " : "";
164
+ const factory = `${pure}createElementComponent(${args.join(", ")})`;
96
165
  const component = `${doc}export const ${spec.element}: (props: ${spec.typeName}) => ReactNode = ${factory};`;
97
166
 
98
167
  return `${doc}${spec.typeSource}\n\n${component}`;
@@ -114,9 +183,21 @@ const renderCandidateExport = (
114
183
  imports.addNamed(component.module, component.export, false);
115
184
  }
116
185
 
186
+ let classRef: string | undefined;
187
+ let metadataRef: string | undefined;
188
+
189
+ if (library.isTreeShaken) {
190
+ const alias = `${namespace.name}$`;
191
+ const specifier = externalPackageFor(namespace.name) ?? `@gtkx/gi/${namespace.name.toLowerCase()}`;
192
+ imports.addNamespace(specifier, alias, false);
193
+ imports.addNamespace("../metadata.js", METADATA_ALIAS, false);
194
+ classRef = `${alias}.${sanitizeTypeIdentifier(klass.name)}`;
195
+ metadataRef = `${METADATA_ALIAS}.${glibName}`;
196
+ }
197
+
117
198
  const doc = getDoc(klass);
118
199
 
119
- return `${doc}${renderElementComponentExport(glibName, component)}`;
200
+ return `${doc}${renderElementComponentExport(glibName, component, classRef, metadataRef)}`;
120
201
  };
121
202
 
122
203
  const resolveElementComponent = (
@@ -134,16 +215,32 @@ const resolveElementComponent = (
134
215
  return undefined;
135
216
  };
136
217
 
137
- const renderElementComponentExport = (glibName: string, component: ElementComponent | undefined): string => {
218
+ const renderElementComponentExport = (
219
+ glibName: string,
220
+ component: ElementComponent | undefined,
221
+ classRef: string | undefined,
222
+ metadataRef: string | undefined,
223
+ ): string => {
138
224
  const propsType = `${glibName}Props`;
139
225
  const annotation = `(props: ${propsType}) => ReactNode`;
140
- const factoryCall = `createElementComponent(${sourceStringLiteral(glibName)})`;
226
+ const args = [sourceStringLiteral(glibName)];
227
+
228
+ if (classRef !== undefined) {
229
+ args.push(classRef);
230
+ }
231
+
232
+ if (metadataRef !== undefined) {
233
+ args.push(metadataRef);
234
+ }
235
+
236
+ const pure = classRef === undefined ? "" : "/* @__PURE__ */ ";
237
+ const factoryCall = `${pure}createElementComponent(${args.join(", ")})`;
141
238
 
142
239
  if (component === undefined) {
143
240
  return `export const ${glibName}: ${annotation} = ${factoryCall};`;
144
241
  }
145
242
 
146
- return `export const ${glibName}: ${annotation} = ${component.export}(${factoryCall});`;
243
+ return `export const ${glibName}: ${annotation} = ${pure}${component.export}(${factoryCall});`;
147
244
  };
148
245
 
149
246
  export { generateElementComponentsSection, type ElementComponentOverrides };
@@ -10,6 +10,8 @@ type LazyElementSpec = {
10
10
  typeSource: string;
11
11
  doc: string | undefined;
12
12
  annotations: GirAnnotations;
13
+ namespaceName: string;
14
+ className: string;
13
15
  };
14
16
 
15
17
  const appendConstructOnlyNames = (klass: GirClass, names: string[]): void => {
@@ -43,6 +45,8 @@ const createLazyElementSpec = (context: GirIndex, element: string, entry: GirTyp
43
45
  typeSource: `export type ${typeName} = ${base} & { children?: ReactNode };`,
44
46
  doc: entry.klass.doc,
45
47
  annotations: entry.klass.annotations,
48
+ namespaceName: entry.namespace.name,
49
+ className: entry.klass.name,
46
50
  };
47
51
  };
48
52