@gtkx/codegen 1.4.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 (180) hide show
  1. package/README.md +1 -1
  2. package/dist/analysis/descriptor-render.d.ts +1 -0
  3. package/dist/analysis/descriptor-render.d.ts.map +1 -1
  4. package/dist/analysis/descriptor-render.js +56 -28
  5. package/dist/analysis/descriptor-render.js.map +1 -1
  6. package/dist/analysis/descriptor.d.ts +3 -1
  7. package/dist/analysis/descriptor.d.ts.map +1 -1
  8. package/dist/analysis/descriptor.js +10 -9
  9. package/dist/analysis/descriptor.js.map +1 -1
  10. package/dist/compile.d.ts.map +1 -1
  11. package/dist/compile.js +24 -3
  12. package/dist/compile.js.map +1 -1
  13. package/dist/docs/api-reference.d.ts +2 -0
  14. package/dist/docs/api-reference.d.ts.map +1 -1
  15. package/dist/docs/api-reference.js +2 -1
  16. package/dist/docs/api-reference.js.map +1 -1
  17. package/dist/docs/pipeline.d.ts +3 -0
  18. package/dist/docs/pipeline.d.ts.map +1 -1
  19. package/dist/docs/pipeline.js +44 -12
  20. package/dist/docs/pipeline.js.map +1 -1
  21. package/dist/fingerprint.d.ts +3 -0
  22. package/dist/fingerprint.d.ts.map +1 -1
  23. package/dist/fingerprint.js +4 -0
  24. package/dist/fingerprint.js.map +1 -1
  25. package/dist/gi.d.ts +1 -0
  26. package/dist/gi.d.ts.map +1 -1
  27. package/dist/gi.js +4 -1
  28. package/dist/gi.js.map +1 -1
  29. package/dist/gir/libraries.d.ts +12 -5
  30. package/dist/gir/libraries.d.ts.map +1 -1
  31. package/dist/gir/libraries.js +50 -14
  32. package/dist/gir/libraries.js.map +1 -1
  33. package/dist/gir/library.d.ts +5 -0
  34. package/dist/gir/library.d.ts.map +1 -1
  35. package/dist/gir/library.js +6 -0
  36. package/dist/gir/library.js.map +1 -1
  37. package/dist/internal.d.ts +1 -0
  38. package/dist/internal.d.ts.map +1 -1
  39. package/dist/internal.js +1 -0
  40. package/dist/internal.js.map +1 -1
  41. package/dist/jsx.d.ts +1 -0
  42. package/dist/jsx.d.ts.map +1 -1
  43. package/dist/jsx.js +2 -0
  44. package/dist/jsx.js.map +1 -1
  45. package/dist/runner.d.ts +2 -0
  46. package/dist/runner.d.ts.map +1 -1
  47. package/dist/runner.js +3 -1
  48. package/dist/runner.js.map +1 -1
  49. package/dist/store/gi/bootstrap.d.ts +4 -0
  50. package/dist/store/gi/bootstrap.d.ts.map +1 -0
  51. package/dist/store/gi/bootstrap.js +48 -0
  52. package/dist/store/gi/bootstrap.js.map +1 -0
  53. package/dist/store/gi/class.d.ts.map +1 -1
  54. package/dist/store/gi/class.js +75 -28
  55. package/dist/store/gi/class.js.map +1 -1
  56. package/dist/store/gi/constructor-props.d.ts.map +1 -1
  57. package/dist/store/gi/constructor-props.js +2 -1
  58. package/dist/store/gi/constructor-props.js.map +1 -1
  59. package/dist/store/gi/enum.js +3 -3
  60. package/dist/store/gi/enum.js.map +1 -1
  61. package/dist/store/gi/folded.d.ts +14 -0
  62. package/dist/store/gi/folded.d.ts.map +1 -0
  63. package/dist/store/gi/folded.js +35 -0
  64. package/dist/store/gi/folded.js.map +1 -0
  65. package/dist/store/gi/function.d.ts.map +1 -1
  66. package/dist/store/gi/function.js +22 -3
  67. package/dist/store/gi/function.js.map +1 -1
  68. package/dist/store/gi/gtype-binding.d.ts.map +1 -1
  69. package/dist/store/gi/gtype-binding.js +11 -1
  70. package/dist/store/gi/gtype-binding.js.map +1 -1
  71. package/dist/store/gi/interface.d.ts.map +1 -1
  72. package/dist/store/gi/interface.js +43 -4
  73. package/dist/store/gi/interface.js.map +1 -1
  74. package/dist/store/gi/method.d.ts.map +1 -1
  75. package/dist/store/gi/method.js +2 -0
  76. package/dist/store/gi/method.js.map +1 -1
  77. package/dist/store/gi/pipeline.d.ts +5 -1
  78. package/dist/store/gi/pipeline.d.ts.map +1 -1
  79. package/dist/store/gi/pipeline.js +13 -1
  80. package/dist/store/gi/pipeline.js.map +1 -1
  81. package/dist/store/gi/property-accessor.js +1 -1
  82. package/dist/store/gi/property-accessor.js.map +1 -1
  83. package/dist/store/gi/record-field-accessor.d.ts.map +1 -1
  84. package/dist/store/gi/record-field-accessor.js +3 -3
  85. package/dist/store/gi/record-field-accessor.js.map +1 -1
  86. package/dist/store/gi/record.d.ts.map +1 -1
  87. package/dist/store/gi/record.js +44 -12
  88. package/dist/store/gi/record.js.map +1 -1
  89. package/dist/store/gi/registration.js +2 -2
  90. package/dist/store/gi/registration.js.map +1 -1
  91. package/dist/store/gi/return-wrap.js +1 -1
  92. package/dist/store/gi/return-wrap.js.map +1 -1
  93. package/dist/store/gi/signal.d.ts.map +1 -1
  94. package/dist/store/gi/signal.js +7 -3
  95. package/dist/store/gi/signal.js.map +1 -1
  96. package/dist/store/gi/vtable.d.ts.map +1 -1
  97. package/dist/store/gi/vtable.js +1 -1
  98. package/dist/store/gi/vtable.js.map +1 -1
  99. package/dist/store/gi-store.d.ts +1 -0
  100. package/dist/store/gi-store.d.ts.map +1 -1
  101. package/dist/store/gi-store.js +16 -7
  102. package/dist/store/gi-store.js.map +1 -1
  103. package/dist/store/jsx/element-components.d.ts.map +1 -1
  104. package/dist/store/jsx/element-components.js +64 -10
  105. package/dist/store/jsx/element-components.js.map +1 -1
  106. package/dist/store/jsx/element-prop-types.d.ts +2 -0
  107. package/dist/store/jsx/element-prop-types.d.ts.map +1 -1
  108. package/dist/store/jsx/element-prop-types.js +2 -0
  109. package/dist/store/jsx/element-prop-types.js.map +1 -1
  110. package/dist/store/jsx/metadata.d.ts.map +1 -1
  111. package/dist/store/jsx/metadata.js +28 -1
  112. package/dist/store/jsx/metadata.js.map +1 -1
  113. package/dist/store/jsx-store.d.ts +1 -0
  114. package/dist/store/jsx-store.d.ts.map +1 -1
  115. package/dist/store/jsx-store.js +7 -2
  116. package/dist/store/jsx-store.js.map +1 -1
  117. package/dist/store/resolve-store.d.ts.map +1 -1
  118. package/dist/store/resolve-store.js +2 -0
  119. package/dist/store/resolve-store.js.map +1 -1
  120. package/dist/store/store-fs.d.ts +2 -1
  121. package/dist/store/store-fs.d.ts.map +1 -1
  122. package/dist/store/store-fs.js +1 -1
  123. package/dist/store/store-fs.js.map +1 -1
  124. package/dist/writer/context.d.ts +17 -0
  125. package/dist/writer/context.d.ts.map +1 -1
  126. package/dist/writer/context.js +83 -11
  127. package/dist/writer/context.js.map +1 -1
  128. package/dist/writer/emit.d.ts +2 -1
  129. package/dist/writer/emit.d.ts.map +1 -1
  130. package/dist/writer/emit.js +2 -1
  131. package/dist/writer/emit.js.map +1 -1
  132. package/dist/writer/imports.d.ts.map +1 -1
  133. package/dist/writer/imports.js +6 -1
  134. package/dist/writer/imports.js.map +1 -1
  135. package/dist/writer/module.d.ts +1 -0
  136. package/dist/writer/module.d.ts.map +1 -1
  137. package/dist/writer/module.js +6 -1
  138. package/dist/writer/module.js.map +1 -1
  139. package/overrides/glib/regex.ts.ejs +2 -0
  140. package/package.json +4 -4
  141. package/src/analysis/descriptor-render.ts +79 -30
  142. package/src/analysis/descriptor.ts +19 -11
  143. package/src/compile.ts +42 -3
  144. package/src/docs/api-reference.ts +4 -1
  145. package/src/docs/pipeline.ts +64 -13
  146. package/src/fingerprint.ts +7 -0
  147. package/src/gi.ts +6 -1
  148. package/src/gir/libraries.ts +64 -16
  149. package/src/gir/library.ts +9 -0
  150. package/src/internal.ts +1 -0
  151. package/src/jsx.ts +3 -0
  152. package/src/runner.ts +5 -1
  153. package/src/store/gi/bootstrap.ts +64 -0
  154. package/src/store/gi/class.ts +100 -29
  155. package/src/store/gi/constructor-props.ts +2 -1
  156. package/src/store/gi/enum.ts +4 -4
  157. package/src/store/gi/folded.ts +57 -0
  158. package/src/store/gi/function.ts +22 -3
  159. package/src/store/gi/gtype-binding.ts +15 -2
  160. package/src/store/gi/interface.ts +64 -7
  161. package/src/store/gi/method.ts +2 -0
  162. package/src/store/gi/pipeline.ts +21 -2
  163. package/src/store/gi/property-accessor.ts +1 -1
  164. package/src/store/gi/record-field-accessor.ts +5 -3
  165. package/src/store/gi/record.ts +61 -14
  166. package/src/store/gi/registration.ts +2 -2
  167. package/src/store/gi/return-wrap.ts +1 -1
  168. package/src/store/gi/signal.ts +8 -3
  169. package/src/store/gi/vtable.ts +1 -0
  170. package/src/store/gi-store.ts +19 -7
  171. package/src/store/jsx/element-components.ts +107 -10
  172. package/src/store/jsx/element-prop-types.ts +4 -0
  173. package/src/store/jsx/metadata.ts +41 -0
  174. package/src/store/jsx-store.ts +10 -4
  175. package/src/store/resolve-store.ts +2 -0
  176. package/src/store/store-fs.ts +3 -2
  177. package/src/writer/context.ts +109 -16
  178. package/src/writer/emit.ts +3 -1
  179. package/src/writer/imports.ts +9 -1
  180. package/src/writer/module.ts +9 -1
@@ -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
 
@@ -8,6 +8,7 @@ import type { GirType } from "../../gir/type.js";
8
8
  import { inputParameters } from "../../analysis/param-structure.js";
9
9
  import { type GirProperty, isConstructableProperty } from "../../gir/property.js";
10
10
  import {
11
+ ancestorGlibNames,
11
12
  implementedInterfaces,
12
13
  isIntrinsicElementClass,
13
14
  iterateClassesWithGlibName,
@@ -18,6 +19,7 @@ type IntrinsicElementEntry = {
18
19
  glibName: string;
19
20
  signals: [string, string][];
20
21
  properties: [string, string][];
22
+ ancestors: string[];
21
23
  };
22
24
 
23
25
  const READABLE = 1;
@@ -35,6 +37,10 @@ const FLOAT_PATTERN = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
35
37
  const generateMetadata = (library: Library): string => {
36
38
  const intrinsicElements = collectIntrinsicElements(library);
37
39
 
40
+ if (library.isTreeShaken) {
41
+ return generateEntryMetadata(intrinsicElements);
42
+ }
43
+
38
44
  const signalsEntries = intrinsicElements.map(
39
45
  ({ glibName, signals }) => ` "${glibName}": ${renderSignalsObject(signals)},`,
40
46
  );
@@ -51,6 +57,40 @@ const generateMetadata = (library: Library): string => {
51
57
  ].join("\n\n")}\n`;
52
58
  };
53
59
 
60
+ const entryAncestorsInSet = (entry: IntrinsicElementEntry, names: Set<string>): string[] =>
61
+ entry.ancestors.filter((name) => name !== entry.glibName && names.has(name));
62
+
63
+ const compareEntriesByDepth = (
64
+ names: Set<string>,
65
+ ): ((first: IntrinsicElementEntry, second: IntrinsicElementEntry) => number) =>
66
+ (first, second) =>
67
+ entryAncestorsInSet(first, names).length - entryAncestorsInSet(second, names).length ||
68
+ first.glibName.localeCompare(second.glibName);
69
+
70
+ const renderEntryExport = (entry: IntrinsicElementEntry, names: Set<string>): string => {
71
+ const parent = entryAncestorsInSet(entry, names)[0] ?? "undefined";
72
+ const literal = sourceStringLiteral(entry.glibName);
73
+ const ownSignals = renderSignalsObject(entry.signals);
74
+ const ownProperties = renderPropertiesObject(entry.properties);
75
+ const call = `registerElementMetadata(${literal}, ${parent}, ${ownSignals}, ${ownProperties})`;
76
+
77
+ return `export const ${entry.glibName}: string = /* @__PURE__ */ ${call};`;
78
+ };
79
+
80
+ const generateEntryMetadata = (intrinsicElements: IntrinsicElementEntry[]): string => {
81
+ const names = new Set(intrinsicElements.map((entry) => entry.glibName));
82
+ const ordered = intrinsicElements.toSorted(compareEntriesByDepth(names));
83
+ const lines = ordered.map((entry) => renderEntryExport(entry, names));
84
+
85
+ return `${[
86
+ "import { registerElementMetadata } from \"@gtkx/runtime/internal\";",
87
+ PROPERTY_ENTRY_TYPE,
88
+ "export const signals: Record<string, Record<string, string>> = {};",
89
+ "export const properties: Record<string, Record<string, PropertyEntry>> = {};",
90
+ lines.join("\n\n"),
91
+ ].join("\n\n")}\n`;
92
+ };
93
+
54
94
  const collectIntrinsicElements = (library: Library): IntrinsicElementEntry[] => {
55
95
  const entries: IntrinsicElementEntry[] = [];
56
96
  const seen: Set<string> = new Set();
@@ -71,6 +111,7 @@ const collectIntrinsicElements = (library: Library): IntrinsicElementEntry[] =>
71
111
  glibName,
72
112
  signals: collectSignals(sources),
73
113
  properties: collectProperties(library, sources),
114
+ ancestors: ancestorGlibNames(klass, namespace, library),
74
115
  });
75
116
  }
76
117
 
@@ -9,12 +9,17 @@ type WriteJsxStoreParams = {
9
9
  metadata: string;
10
10
  externalPackages: string[];
11
11
  rawFiles: RawFile[];
12
+ isTreeShaken?: boolean;
12
13
  };
13
14
 
14
- const jsxPeerDependencies = (externalPackages: string[]): Record<string, string> =>
15
- Object.fromEntries(
16
- sortStrings(["@gtkx/gi", "@gtkx/react", "react", ...externalPackages]).map((name) => [name, "*"]),
15
+ const jsxPeerDependencies = (externalPackages: string[], isTreeShaken: boolean): Record<string, string> => {
16
+ const runtime = isTreeShaken ? ["@gtkx/runtime"] : [];
17
+
18
+ return Object.fromEntries(
19
+ sortStrings(["@gtkx/gi", "@gtkx/react", "react", ...runtime, ...externalPackages])
20
+ .map((name) => [name, "*"]),
17
21
  );
22
+ };
18
23
 
19
24
  const writeJsxStore = (params: WriteJsxStoreParams): void => {
20
25
  const { options, namespaces, metadata, externalPackages, rawFiles } = params;
@@ -43,7 +48,8 @@ const writeJsxStore = (params: WriteJsxStoreParams): void => {
43
48
  name: "@gtkx/jsx",
44
49
  version: options.version,
45
50
  exports: exportsMap,
46
- peerDependencies: jsxPeerDependencies(externalPackages),
51
+ sideEffects: params.isTreeShaken !== true,
52
+ peerDependencies: jsxPeerDependencies(externalPackages, params.isTreeShaken === true),
47
53
  }),
48
54
  rawFiles,
49
55
  });
@@ -28,6 +28,8 @@ const STORE_CONSUMERS: string[] = [
28
28
  "@gtkx/cli",
29
29
  "@gtkx/components",
30
30
  "@gtkx/css",
31
+ "@gtkx/forms",
32
+ "@gtkx/i18n",
31
33
  "@gtkx/navigation",
32
34
  "@gtkx/react",
33
35
  "@gtkx/runtime",
@@ -31,7 +31,7 @@ type Manifest = {
31
31
  name: string;
32
32
  type: "module";
33
33
  version: string;
34
- sideEffects: true;
34
+ sideEffects: boolean | string[];
35
35
  exports: Record<string, unknown>;
36
36
  peerDependencies?: Record<string, string>;
37
37
  };
@@ -40,6 +40,7 @@ type ManifestInput = {
40
40
  name: string;
41
41
  version: string;
42
42
  exports: Record<string, unknown>;
43
+ sideEffects?: boolean | string[];
43
44
  peerDependencies?: Record<string, string>;
44
45
  };
45
46
 
@@ -71,7 +72,7 @@ const buildManifest = (input: ManifestInput): Manifest => {
71
72
  name: input.name,
72
73
  type: "module",
73
74
  version: input.version,
74
- sideEffects: true,
75
+ sideEffects: input.sideEffects ?? true,
75
76
  exports: { "./package.json": "./package.json", ...input.exports },
76
77
  };
77
78
 
@@ -3,16 +3,56 @@ import type { GirNamespace } from "../gir/namespace.js";
3
3
  import { externalPackageFor } from "../gir/external-namespaces.js";
4
4
  import { type Declaration, ModuleBuilder } from "./module.js";
5
5
 
6
+ type BootstrapCallOptions = {
7
+ moduleExports?: string[];
8
+ runtimeImports?: string[];
9
+ };
10
+
11
+ const addExternalNamespaceImport = (context: ModuleContext, packageName: string, namespaceName: string): void => {
12
+ if (context.isTreeShaken) {
13
+ context.externalDependencies.add(packageName);
14
+ } else {
15
+ context.module.imports.addSideEffect(packageName);
16
+ }
17
+
18
+ context.module.imports.addNamespace(packageName, namespaceName);
19
+ };
20
+
21
+ const addInternalNamespaceImport = (context: ModuleContext, namespaceName: string): void => {
22
+ const directory = namespaceName.toLowerCase();
23
+ const isFoundational = directory === "gobject" || directory === "glib";
24
+ const path = isFoundational ? `../${directory}/${directory}.js` : `../${directory}/index.js`;
25
+
26
+ if (context.isTreeShaken) {
27
+ context.dependencies.add(directory);
28
+ } else if (!isFoundational) {
29
+ context.module.imports.addSideEffect(path);
30
+ }
31
+
32
+ context.module.imports.addNamespace(path, namespaceName);
33
+ };
34
+
6
35
  class ModuleContext {
36
+ private registrationSink: string[] | undefined;
37
+ private readonly hoistedBases: Map<string, string> = new Map();
7
38
  public module: ModuleBuilder = new ModuleBuilder();
8
39
  public namespace: GirNamespace;
9
40
  public library: Library;
41
+ public dependencies: Set<string> = new Set();
42
+ public externalDependencies: Set<string> = new Set();
43
+ public bootstrapCalls: string[] = [];
44
+ public bootstrapModuleExports: Set<string> = new Set();
45
+ public bootstrapRuntimeImports: Set<string> = new Set();
10
46
 
11
47
  constructor(namespace: GirNamespace, library: Library) {
12
48
  this.namespace = namespace;
13
49
  this.library = library;
14
50
  }
15
51
 
52
+ get isTreeShaken(): boolean {
53
+ return this.library.isTreeShaken;
54
+ }
55
+
16
56
  addRuntimeImport(name: string): void {
17
57
  this.module.imports.addNamed("@gtkx/runtime", name);
18
58
  }
@@ -25,7 +65,47 @@ class ModuleContext {
25
65
  return this.module.hoistDescriptor(expression);
26
66
  }
27
67
 
68
+ beginRegistrations(): void {
69
+ this.registrationSink = [];
70
+ }
71
+
72
+ takeRegistrations(): string[] {
73
+ const collected = this.registrationSink ?? [];
74
+ this.registrationSink = undefined;
75
+
76
+ return collected;
77
+ }
78
+
79
+ collectRegistration(code: string, requires: string[] = []): void {
80
+ if (this.registrationSink === undefined) {
81
+ this.module.appendRegistration(code, requires);
82
+
83
+ return;
84
+ }
85
+
86
+ this.registrationSink.push(code);
87
+ }
88
+
89
+ addBootstrapCall(code: string, options: BootstrapCallOptions = {}): void {
90
+ const moduleExports = options.moduleExports ?? [];
91
+ const runtimeImports = options.runtimeImports ?? [];
92
+
93
+ for (const name of moduleExports) {
94
+ this.bootstrapModuleExports.add(name);
95
+ }
96
+
97
+ for (const name of runtimeImports) {
98
+ this.bootstrapRuntimeImports.add(name);
99
+ }
100
+
101
+ this.bootstrapCalls.push(code);
102
+ }
103
+
28
104
  addGObjectBootstrapImports(): void {
105
+ if (this.isTreeShaken) {
106
+ return;
107
+ }
108
+
29
109
  if (this.namespace.name === "GObject") {
30
110
  return;
31
111
  }
@@ -46,33 +126,23 @@ class ModuleContext {
46
126
 
47
127
  const packageName = externalPackageFor(namespaceName);
48
128
 
49
- if (packageName !== undefined) {
50
- this.module.imports.addSideEffect(packageName);
51
- this.module.imports.addNamespace(packageName, namespaceName);
52
-
53
- return namespaceName;
54
- }
55
-
56
- const directory = namespaceName.toLowerCase();
57
- const isFoundational = directory === "gobject" || directory === "glib";
58
- const path = isFoundational ? `../${directory}/${directory}.js` : `../${directory}/index.js`;
59
-
60
- if (!isFoundational) {
61
- this.module.imports.addSideEffect(path);
129
+ if (packageName === undefined) {
130
+ addInternalNamespaceImport(this, namespaceName);
131
+ } else {
132
+ addExternalNamespaceImport(this, packageName, namespaceName);
62
133
  }
63
134
 
64
- this.module.imports.addNamespace(path, namespaceName);
65
-
66
135
  return namespaceName;
67
136
  }
68
137
 
69
138
  declare(declaration: Declaration): void {
70
- const { name, code, owner } = declaration;
139
+ const { name, code, owner, isLocal } = declaration;
71
140
 
72
141
  this.module.appendDeclaration({
73
142
  name,
74
143
  code,
75
144
  owner: owner === undefined ? undefined : `${this.namespace.name}.${owner}`,
145
+ isLocal,
76
146
  });
77
147
  }
78
148
 
@@ -83,6 +153,29 @@ class ModuleContext {
83
153
 
84
154
  return `${this.addCrossNamespaceImport(namespaceName)}.${name}`;
85
155
  }
156
+
157
+ hoistBaseRef(expression: string): string {
158
+ if (!expression.includes(".")) {
159
+ return expression;
160
+ }
161
+
162
+ const existing = this.hoistedBases.get(expression);
163
+
164
+ if (existing !== undefined) {
165
+ return existing;
166
+ }
167
+
168
+ const local = `_${expression.replaceAll(".", "_")}`;
169
+ this.hoistedBases.set(expression, local);
170
+
171
+ this.module.appendDeclaration({
172
+ name: local,
173
+ code: `const ${local}: typeof ${expression} = ${expression};`,
174
+ isLocal: true,
175
+ });
176
+
177
+ return local;
178
+ }
86
179
  }
87
180
 
88
181
  export { ModuleContext };
@@ -32,4 +32,6 @@ const arrayLiteral = (elements: string[]): string => {
32
32
  return `[\n${lines.join("\n")}\n]`;
33
33
  };
34
34
 
35
- export { indent, renderBlock, renderBraced, renderBracedOrEmpty, indentMembers, joinArgs, arrayLiteral };
35
+ const pure = (expression: string): string => `/* @__PURE__ */ ${expression}`;
36
+
37
+ export { indent, renderBlock, renderBraced, renderBracedOrEmpty, indentMembers, joinArgs, arrayLiteral, pure };
@@ -62,8 +62,16 @@ class ImportsBuilder {
62
62
  }
63
63
 
64
64
  addNamespace(specifier: string, alias: string, isType = false): void {
65
- if (!this.namespaces.has(specifier)) {
65
+ const existing = this.namespaces.get(specifier);
66
+
67
+ if (existing === undefined) {
66
68
  this.namespaces.set(specifier, { alias, isType });
69
+
70
+ return;
71
+ }
72
+
73
+ if (!isType && existing.isType) {
74
+ existing.isType = false;
67
75
  }
68
76
  }
69
77
 
@@ -1,9 +1,11 @@
1
+ import { pure } from "./emit.js";
1
2
  import { ImportsBuilder } from "./imports.js";
2
3
 
3
4
  type Declaration = {
4
5
  name: string;
5
6
  code: string;
6
7
  owner?: string | undefined;
8
+ isLocal?: boolean | undefined;
7
9
  };
8
10
 
9
11
  type ExportedDeclaration = {
@@ -127,12 +129,18 @@ class ModuleBuilder {
127
129
 
128
130
  const name = `_desc${String(this.hoistedDescriptors.size)}`;
129
131
  this.hoistedDescriptors.set(expression, name);
130
- this.appendBinding(`const ${name} = ${expression};`, name);
132
+ this.appendBinding(`const ${name} = ${pure(expression)};`, name);
131
133
 
132
134
  return name;
133
135
  }
134
136
 
135
137
  appendDeclaration(declaration: Declaration): void {
138
+ if (declaration.isLocal === true) {
139
+ this.declarations.push(declaration.code);
140
+
141
+ return;
142
+ }
143
+
136
144
  this.claimExportedName(declaration);
137
145
  this.claimTypeName(declaration);
138
146
  this.declaredNames.add(declaration.name);