@gtkx/codegen 1.2.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/README.md +6 -4
  2. package/dist/analysis/descriptor-render.d.ts +7 -1
  3. package/dist/analysis/descriptor-render.d.ts.map +1 -1
  4. package/dist/analysis/descriptor-render.js +37 -15
  5. package/dist/analysis/descriptor-render.js.map +1 -1
  6. package/dist/analysis/descriptor.d.ts +5 -1
  7. package/dist/analysis/descriptor.d.ts.map +1 -1
  8. package/dist/analysis/descriptor.js +12 -2
  9. package/dist/analysis/descriptor.js.map +1 -1
  10. package/dist/analysis/param-capability.d.ts +5 -2
  11. package/dist/analysis/param-capability.d.ts.map +1 -1
  12. package/dist/analysis/param-capability.js +36 -4
  13. package/dist/analysis/param-capability.js.map +1 -1
  14. package/dist/analysis/param-structure.d.ts +1 -1
  15. package/dist/analysis/param-structure.d.ts.map +1 -1
  16. package/dist/analysis/param-structure.js.map +1 -1
  17. package/dist/analysis/ts-type.d.ts +1 -1
  18. package/dist/analysis/ts-type.d.ts.map +1 -1
  19. package/dist/analysis/ts-type.js +28 -10
  20. package/dist/analysis/ts-type.js.map +1 -1
  21. package/dist/compile.js +1 -1
  22. package/dist/compile.js.map +1 -1
  23. package/dist/docs/api-reference.d.ts +6 -0
  24. package/dist/docs/api-reference.d.ts.map +1 -1
  25. package/dist/docs/api-reference.js +11 -2
  26. package/dist/docs/api-reference.js.map +1 -1
  27. package/dist/docs/pipeline.d.ts +3 -0
  28. package/dist/docs/pipeline.d.ts.map +1 -1
  29. package/dist/docs/pipeline.js +10 -1
  30. package/dist/docs/pipeline.js.map +1 -1
  31. package/dist/fingerprint.d.ts +5 -0
  32. package/dist/fingerprint.d.ts.map +1 -1
  33. package/dist/fingerprint.js +111 -15
  34. package/dist/fingerprint.js.map +1 -1
  35. package/dist/gi.d.ts +3 -0
  36. package/dist/gi.d.ts.map +1 -1
  37. package/dist/gi.js +33 -7
  38. package/dist/gi.js.map +1 -1
  39. package/dist/gir/external-namespaces.d.ts +8 -0
  40. package/dist/gir/external-namespaces.d.ts.map +1 -0
  41. package/dist/gir/external-namespaces.js +4 -0
  42. package/dist/gir/external-namespaces.js.map +1 -0
  43. package/dist/gir/finish-overrides.d.ts +3 -0
  44. package/dist/gir/finish-overrides.d.ts.map +1 -0
  45. package/dist/gir/finish-overrides.js +5 -0
  46. package/dist/gir/finish-overrides.js.map +1 -0
  47. package/dist/gir/function.d.ts +5 -2
  48. package/dist/gir/function.d.ts.map +1 -1
  49. package/dist/gir/function.js +34 -5
  50. package/dist/gir/function.js.map +1 -1
  51. package/dist/gir/hidden-symbols.d.ts +3 -0
  52. package/dist/gir/hidden-symbols.d.ts.map +1 -0
  53. package/dist/gir/hidden-symbols.js +39 -0
  54. package/dist/gir/hidden-symbols.js.map +1 -0
  55. package/dist/gir/library.d.ts +22 -2
  56. package/dist/gir/library.d.ts.map +1 -1
  57. package/dist/gir/library.js +21 -3
  58. package/dist/gir/library.js.map +1 -1
  59. package/dist/gir/ucs4-overrides.d.ts +3 -0
  60. package/dist/gir/ucs4-overrides.d.ts.map +1 -0
  61. package/dist/gir/ucs4-overrides.js +7 -0
  62. package/dist/gir/ucs4-overrides.js.map +1 -0
  63. package/dist/guards.d.ts +2 -1
  64. package/dist/guards.d.ts.map +1 -1
  65. package/dist/guards.js +3 -1
  66. package/dist/guards.js.map +1 -1
  67. package/dist/index.d.ts +2 -0
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +2 -0
  70. package/dist/index.js.map +1 -1
  71. package/dist/jsx.d.ts.map +1 -1
  72. package/dist/jsx.js +27 -8
  73. package/dist/jsx.js.map +1 -1
  74. package/dist/runner.d.ts +8 -0
  75. package/dist/runner.d.ts.map +1 -1
  76. package/dist/runner.js +9 -3
  77. package/dist/runner.js.map +1 -1
  78. package/dist/store/external-package-links.d.ts +6 -0
  79. package/dist/store/external-package-links.d.ts.map +1 -0
  80. package/dist/store/external-package-links.js +158 -0
  81. package/dist/store/external-package-links.js.map +1 -0
  82. package/dist/store/gi/callable-doc.d.ts.map +1 -1
  83. package/dist/store/gi/callable-doc.js +4 -4
  84. package/dist/store/gi/callable-doc.js.map +1 -1
  85. package/dist/store/gi/callables.js +1 -1
  86. package/dist/store/gi/callables.js.map +1 -1
  87. package/dist/store/gi/class.d.ts.map +1 -1
  88. package/dist/store/gi/class.js +17 -0
  89. package/dist/store/gi/class.js.map +1 -1
  90. package/dist/store/gi/constructor-props.js +1 -1
  91. package/dist/store/gi/constructor-props.js.map +1 -1
  92. package/dist/store/gi/function.d.ts.map +1 -1
  93. package/dist/store/gi/function.js +2 -1
  94. package/dist/store/gi/function.js.map +1 -1
  95. package/dist/store/gi/generated-libraries.d.ts +26 -0
  96. package/dist/store/gi/generated-libraries.d.ts.map +1 -0
  97. package/dist/store/gi/generated-libraries.js +53 -0
  98. package/dist/store/gi/generated-libraries.js.map +1 -0
  99. package/dist/store/gi/gtype-binding.d.ts +2 -1
  100. package/dist/store/gi/gtype-binding.d.ts.map +1 -1
  101. package/dist/store/gi/gtype-binding.js +6 -1
  102. package/dist/store/gi/gtype-binding.js.map +1 -1
  103. package/dist/store/gi/method.d.ts +7 -2
  104. package/dist/store/gi/method.d.ts.map +1 -1
  105. package/dist/store/gi/method.js +126 -30
  106. package/dist/store/gi/method.js.map +1 -1
  107. package/dist/store/gi/param-marshal.d.ts +8 -1
  108. package/dist/store/gi/param-marshal.d.ts.map +1 -1
  109. package/dist/store/gi/param-marshal.js +52 -4
  110. package/dist/store/gi/param-marshal.js.map +1 -1
  111. package/dist/store/gi/record-constructor.d.ts.map +1 -1
  112. package/dist/store/gi/record-constructor.js +2 -3
  113. package/dist/store/gi/record-constructor.js.map +1 -1
  114. package/dist/store/gi/record-field-accessor.d.ts +2 -1
  115. package/dist/store/gi/record-field-accessor.d.ts.map +1 -1
  116. package/dist/store/gi/record-field-accessor.js +9 -3
  117. package/dist/store/gi/record-field-accessor.js.map +1 -1
  118. package/dist/store/gi/runtime-override.js +1 -1
  119. package/dist/store/gi/runtime-override.js.map +1 -1
  120. package/dist/store/gi/signal.js +2 -2
  121. package/dist/store/gi/signal.js.map +1 -1
  122. package/dist/store/gi/vtable.d.ts.map +1 -1
  123. package/dist/store/gi/vtable.js +82 -12
  124. package/dist/store/gi/vtable.js.map +1 -1
  125. package/dist/store/gi-store.d.ts +12 -2
  126. package/dist/store/gi-store.d.ts.map +1 -1
  127. package/dist/store/gi-store.js +24 -5
  128. package/dist/store/gi-store.js.map +1 -1
  129. package/dist/store/jsx/jsx.d.ts.map +1 -1
  130. package/dist/store/jsx/jsx.js +3 -1
  131. package/dist/store/jsx/jsx.js.map +1 -1
  132. package/dist/store/jsx/metadata.d.ts.map +1 -1
  133. package/dist/store/jsx/metadata.js +35 -60
  134. package/dist/store/jsx/metadata.js.map +1 -1
  135. package/dist/store/jsx-store.d.ts +8 -1
  136. package/dist/store/jsx-store.d.ts.map +1 -1
  137. package/dist/store/jsx-store.js +9 -2
  138. package/dist/store/jsx-store.js.map +1 -1
  139. package/dist/store/resolve-store.d.ts +2 -1
  140. package/dist/store/resolve-store.d.ts.map +1 -1
  141. package/dist/store/resolve-store.js +3 -1
  142. package/dist/store/resolve-store.js.map +1 -1
  143. package/dist/store/store-fs.d.ts +2 -1
  144. package/dist/store/store-fs.d.ts.map +1 -1
  145. package/dist/store/store-fs.js +8 -3
  146. package/dist/store/store-fs.js.map +1 -1
  147. package/dist/writer/context.d.ts.map +1 -1
  148. package/dist/writer/context.js +8 -0
  149. package/dist/writer/context.js.map +1 -1
  150. package/dist/writer/gtk-doc.d.ts.map +1 -1
  151. package/dist/writer/gtk-doc.js +27 -1
  152. package/dist/writer/gtk-doc.js.map +1 -1
  153. package/overrides/gdk/rgba.ts.ejs +15 -1
  154. package/overrides/glib/index.ts.ejs +3 -0
  155. package/overrides/glib/regex.ts.ejs +66 -0
  156. package/overrides/gobject/index.ts.ejs +3 -0
  157. package/overrides/gobject/object-class.ts.ejs +27 -0
  158. package/overrides/gobject/param-spec-getters.ts.ejs +57 -0
  159. package/overrides/gobject/param-spec.ts.ejs +21 -0
  160. package/overrides/gobject/value.ts.ejs +6 -1
  161. package/overrides/graphene/structs.ts.ejs +38 -3
  162. package/overrides/gtk/index.ts.ejs +2 -0
  163. package/overrides/gtk/widget-class.ts.ejs +28 -0
  164. package/package.json +7 -6
  165. package/src/analysis/descriptor-render.ts +60 -16
  166. package/src/analysis/descriptor.ts +17 -3
  167. package/src/analysis/param-capability.ts +68 -5
  168. package/src/analysis/param-structure.ts +1 -1
  169. package/src/analysis/ts-type.ts +45 -11
  170. package/src/compile.ts +1 -1
  171. package/src/docs/api-reference.ts +20 -2
  172. package/src/docs/pipeline.ts +15 -1
  173. package/src/fingerprint.ts +151 -16
  174. package/src/gi.ts +48 -9
  175. package/src/gir/external-namespaces.ts +8 -0
  176. package/src/gir/finish-overrides.ts +5 -0
  177. package/src/gir/function.ts +52 -14
  178. package/src/gir/hidden-symbols.ts +39 -0
  179. package/src/gir/library.ts +36 -3
  180. package/src/gir/ucs4-overrides.ts +7 -0
  181. package/src/guards.ts +6 -1
  182. package/src/index.ts +2 -0
  183. package/src/jsx.ts +33 -8
  184. package/src/runner.ts +17 -3
  185. package/src/store/external-package-links.ts +226 -0
  186. package/src/store/gi/callable-doc.ts +8 -4
  187. package/src/store/gi/callables.ts +1 -1
  188. package/src/store/gi/class.ts +29 -0
  189. package/src/store/gi/constructor-props.ts +1 -1
  190. package/src/store/gi/function.ts +2 -1
  191. package/src/store/gi/generated-libraries.ts +91 -0
  192. package/src/store/gi/gtype-binding.ts +8 -1
  193. package/src/store/gi/method.ts +189 -36
  194. package/src/store/gi/param-marshal.ts +67 -4
  195. package/src/store/gi/record-constructor.ts +8 -3
  196. package/src/store/gi/record-field-accessor.ts +11 -2
  197. package/src/store/gi/runtime-override.ts +1 -1
  198. package/src/store/gi/signal.ts +2 -2
  199. package/src/store/gi/vtable.ts +118 -11
  200. package/src/store/gi-store.ts +48 -5
  201. package/src/store/jsx/jsx.ts +3 -1
  202. package/src/store/jsx/metadata.ts +46 -99
  203. package/src/store/jsx-store.ts +23 -7
  204. package/src/store/resolve-store.ts +3 -1
  205. package/src/store/store-fs.ts +10 -2
  206. package/src/writer/context.ts +11 -0
  207. package/src/writer/gtk-doc.ts +38 -1
  208. package/overrides/cairo/context.ts.ejs +0 -1269
  209. package/overrides/cairo/font-face.ts.ejs +0 -151
  210. package/overrides/cairo/font-options.ts.ejs +0 -199
  211. package/overrides/cairo/image-surface.ts.ejs +0 -132
  212. package/overrides/cairo/index.ts.ejs +0 -22
  213. package/overrides/cairo/matrix.ts.ejs +0 -171
  214. package/overrides/cairo/pattern.ts.ejs +0 -476
  215. package/overrides/cairo/recording-surface.ts.ejs +0 -102
  216. package/overrides/cairo/region.ts.ejs +0 -237
  217. package/overrides/cairo/scaled-font.ts.ejs +0 -330
  218. package/overrides/cairo/surface.ts.ejs +0 -296
@@ -11,7 +11,8 @@ import {
11
11
  type ListType,
12
12
  type TypeId,
13
13
  } from "../gir/type-id.js";
14
- import { gtypeTsType } from "../store/gi/gtype-binding.js";
14
+ import { gtypeParamTsType, gtypeTsType } from "../store/gi/gtype-binding.js";
15
+ import { isValueTypeName } from "../store/gi/param-marshal.js";
15
16
  import { isByteSequence } from "./type-shape.js";
16
17
 
17
18
  type ReferenceName = {
@@ -27,6 +28,12 @@ type TsTypeTarget = {
27
28
  renderGtype: () => string;
28
29
  };
29
30
 
31
+ type ModuleTypeOptions = {
32
+ byteArrayType: string;
33
+ isValueWidened: boolean;
34
+ isGtypeWidened: boolean;
35
+ };
36
+
30
37
  const BYTE_ARRAY_TYPE = "Uint8Array";
31
38
  const BYTE_ARRAY_INPUT_TYPE = "Uint8Array | number[]";
32
39
  const NUMBER_ARRAY_TYPE = "number[]";
@@ -101,6 +108,8 @@ const renderContainerType = (
101
108
  return renderSequenceType(library, target, type);
102
109
  };
103
110
 
111
+ const parenthesizeUnion = (rendered: string): string => (rendered.includes(" | ") ? `(${rendered})` : rendered);
112
+
104
113
  const renderSequenceType = (library: Library, target: TsTypeTarget, type: CArrayType | ListType): string => {
105
114
  if (type.kind === "carray" && hasUnknownArrayLength(type)) {
106
115
  return "number";
@@ -110,7 +119,7 @@ const renderSequenceType = (library: Library, target: TsTypeTarget, type: CArray
110
119
  return target.byteArrayType;
111
120
  }
112
121
 
113
- return `${renderBaseType(library, target, type.element)}[]`;
122
+ return `${parenthesizeUnion(renderBaseType(library, target, type.element))}[]`;
114
123
  };
115
124
 
116
125
  const renderNamedType = (
@@ -128,30 +137,55 @@ const renderNamedType = (
128
137
  const getByteArrayType = (library: Library): string =>
129
138
  library.isByteArrayTyped ? BYTE_ARRAY_TYPE : NUMBER_ARRAY_TYPE;
130
139
 
131
- const moduleTarget = (context: ModuleContext, byteArrayType: string): TsTypeTarget => ({
140
+ const renderNamedModuleType = (context: ModuleContext, name: ReferenceName, isValueWidened: boolean): string => {
141
+ const qualified = context.qualify(name.namespaceName, name.typeName);
142
+
143
+ if (!isValueWidened || !isValueTypeName(name)) {
144
+ return qualified;
145
+ }
146
+
147
+ context.addRuntimeTypeImport("JsValue");
148
+
149
+ return `${qualified} | JsValue`;
150
+ };
151
+
152
+ const moduleTarget = (context: ModuleContext, options: ModuleTypeOptions): TsTypeTarget => ({
132
153
  containerStyle: "map",
133
154
  callbackType: "((...args: any[]) => any)",
134
- byteArrayType,
135
- renderNamed: (_resolved, name) => context.qualify(name.namespaceName, name.typeName),
136
- renderGtype: () => gtypeTsType(context),
155
+ byteArrayType: options.byteArrayType,
156
+ renderNamed: (_resolved, name) => renderNamedModuleType(context, name, options.isValueWidened),
157
+ renderGtype: () => (options.isGtypeWidened ? gtypeParamTsType(context) : gtypeTsType(context)),
137
158
  });
138
159
 
139
160
  const renderModuleType = (
140
161
  context: ModuleContext,
141
162
  ref: TypeId | undefined,
142
163
  isNullable: boolean,
143
- byteArrayType: string,
164
+ options: ModuleTypeOptions,
144
165
  ): string => {
145
- const base = renderBaseType(context.library, moduleTarget(context, byteArrayType), ref);
166
+ const base = renderBaseType(context.library, moduleTarget(context, options), ref);
146
167
 
147
168
  return isNullable ? `${base} | null` : base;
148
169
  };
149
170
 
150
171
  const renderTsType = (context: ModuleContext, ref: TypeId | undefined, isNullable = false): string =>
151
- renderModuleType(context, ref, isNullable, getByteArrayType(context.library));
172
+ renderModuleType(context, ref, isNullable, {
173
+ byteArrayType: getByteArrayType(context.library),
174
+ isValueWidened: false,
175
+ isGtypeWidened: false,
176
+ });
152
177
 
153
- const renderParameterTsType = (context: ModuleContext, ref: TypeId | undefined, isNullable = false): string =>
154
- renderModuleType(context, ref, isNullable, BYTE_ARRAY_INPUT_TYPE);
178
+ const renderParameterTsType = (
179
+ context: ModuleContext,
180
+ ref: TypeId | undefined,
181
+ isNullable = false,
182
+ isValueWidened = true,
183
+ ): string =>
184
+ renderModuleType(context, ref, isNullable, {
185
+ byteArrayType: BYTE_ARRAY_INPUT_TYPE,
186
+ isValueWidened,
187
+ isGtypeWidened: true,
188
+ });
155
189
 
156
190
  const recordTypeTarget = (
157
191
  library: Library,
package/src/compile.ts CHANGED
@@ -76,7 +76,7 @@ const DECLARATION_EMIT = {
76
76
  };
77
77
 
78
78
  const MODULE_EMIT = {
79
- removeComments: false,
79
+ removeComments: true,
80
80
  };
81
81
 
82
82
  const DECLARATION_OPTIONS = ts.convertCompilerOptionsFromJson(
@@ -3,6 +3,7 @@ import type { GirClass } from "../gir/class.js";
3
3
  import type { GirFunction } from "../gir/function.js";
4
4
  import type { GirRecord } from "../gir/record.js";
5
5
  import { isEmittableEntity } from "../gir/emittable.js";
6
+ import { externalPackageFor } from "../gir/external-namespaces.js";
6
7
  import { Library } from "../gir/library.js";
7
8
  import { type GirNamespace, namespaceDirectory } from "../gir/namespace.js";
8
9
  import { dedupeCallables, isEmittableCallable } from "../store/gi/callables.js";
@@ -26,6 +27,12 @@ type ApiReferenceOptions = {
26
27
  omittedProps?: OmittedProps;
27
28
  /** Describes GIR byte sequences as `Uint8Array` rather than `number[]`; defaults to false. */
28
29
  isByteArrayTyped?: boolean;
30
+ /** Describes what a `GObject.Value` a binding hands back holds, rather than the value itself; off by default. */
31
+ isValueUnwrapped?: boolean;
32
+ /** Describes promisified results of a throwing finish without their leading success boolean; off by default. */
33
+ isFinishTrimmed?: boolean;
34
+ /** Describes a handle-passing inout parameter as mutated in place rather than returned; off by default. */
35
+ isInoutInPlace?: boolean;
29
36
  };
30
37
 
31
38
  /** Narrows an `ApiReference.symbols` enumeration. */
@@ -373,7 +380,14 @@ class ApiReference {
373
380
  this.libraries = options.libraries;
374
381
  this.props = options.props ?? {};
375
382
  this.omittedProps = options.omittedProps ?? {};
376
- this.library = Library.load(options.libraries, options.girPath, options.isByteArrayTyped === true);
383
+
384
+ this.library = Library.load(options.libraries, options.girPath, {
385
+ isByteArrayTyped: options.isByteArrayTyped === true,
386
+ isValueUnwrapped: options.isValueUnwrapped === true,
387
+ isFinishTrimmed: options.isFinishTrimmed === true,
388
+ isInoutInPlace: options.isInoutInPlace === true,
389
+ });
390
+
377
391
  this.elementContext = { library: this.library, linkFor: (): string | undefined => undefined };
378
392
  this.buildIndex();
379
393
  }
@@ -399,6 +413,10 @@ class ApiReference {
399
413
 
400
414
  private buildIndex(): void {
401
415
  for (const namespace of this.library.namespaces.values()) {
416
+ if (externalPackageFor(namespace.name) !== undefined) {
417
+ continue;
418
+ }
419
+
402
420
  this.indexNamespace(namespace);
403
421
  }
404
422
 
@@ -475,7 +493,7 @@ class ApiReference {
475
493
  const lower = name.toLowerCase();
476
494
 
477
495
  for (const namespace of this.library.namespaces.values()) {
478
- if (namespace.name.toLowerCase() === lower) {
496
+ if (namespace.name.toLowerCase() === lower && externalPackageFor(namespace.name) === undefined) {
479
497
  return namespace;
480
498
  }
481
499
  }
@@ -39,6 +39,9 @@ type DocsOptions = {
39
39
  omittedProps?: OmittedProps;
40
40
  isForced?: boolean;
41
41
  isByteArrayTyped?: boolean;
42
+ isValueUnwrapped?: boolean;
43
+ isFinishTrimmed?: boolean;
44
+ isInoutInPlace?: boolean;
42
45
  };
43
46
 
44
47
  type DocsManifest = {
@@ -259,7 +262,11 @@ const giInputs = (options: DocsOptions, girFiles: string[]): GiInputs => ({
259
262
  girFiles,
260
263
  libraries: options.libraries,
261
264
  girPath: options.girPath,
265
+ storeVersion: undefined,
262
266
  isByteArrayTyped: options.isByteArrayTyped === true,
267
+ isValueUnwrapped: options.isValueUnwrapped === true,
268
+ isFinishTrimmed: options.isFinishTrimmed === true,
269
+ isInoutInPlace: options.isInoutInPlace === true,
263
270
  });
264
271
 
265
272
  const assertOwnedOutDir = (options: DocsOptions, manifest: DocsManifest | undefined): void => {
@@ -344,7 +351,14 @@ const writeDocs = (options: DocsOptions): DocsResult => {
344
351
  }
345
352
 
346
353
  assertOwnedOutDir(options, previous);
347
- const library = Library.load(options.libraries, options.girPath, options.isByteArrayTyped === true);
354
+
355
+ const library = Library.load(options.libraries, options.girPath, {
356
+ isByteArrayTyped: options.isByteArrayTyped === true,
357
+ isValueUnwrapped: options.isValueUnwrapped === true,
358
+ isFinishTrimmed: options.isFinishTrimmed === true,
359
+ isInoutInPlace: options.isInoutInPlace === true,
360
+ });
361
+
348
362
  const { pages, namespaces } = generatePages(options, input.basePath, library);
349
363
  clearOutDir(options, previous);
350
364
  mkdirSync(options.outDir, { recursive: true });
@@ -1,9 +1,10 @@
1
- import { sortStrings } from "@gtkx/utils";
2
- import { createHash } from "node:crypto";
1
+ import { createHash, type Hash } from "node:crypto";
3
2
  import { type Dirent, existsSync, readdirSync, readFileSync } from "node:fs";
4
- import { dirname, join, resolve } from "node:path";
3
+ import { createRequire } from "node:module";
4
+ import { dirname, join, relative, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import packageManifest from "../package.json" with { type: "json" };
7
+ import { EXTERNAL_NAMESPACES } from "./gir/external-namespaces.js";
7
8
  import { arrayGuard, hasFields, isNumber, isString, optionalGuard } from "./guards.js";
8
9
  import { readJsonFile } from "./json.js";
9
10
 
@@ -11,7 +12,11 @@ type GiInputs = {
11
12
  girFiles: string[];
12
13
  libraries: string[];
13
14
  girPath: string[];
15
+ storeVersion: string | undefined;
14
16
  isByteArrayTyped: boolean;
17
+ isValueUnwrapped: boolean;
18
+ isFinishTrimmed: boolean;
19
+ isInoutInPlace: boolean;
15
20
  };
16
21
 
17
22
  type GiFingerprint = {
@@ -36,6 +41,7 @@ type DocsFingerprint = {
36
41
 
37
42
  type JsxFingerprintInput = {
38
43
  reactVersion: string;
44
+ reactSubexports: string[];
39
45
  components: Record<string, ModuleExport>;
40
46
  lazyElements: string[];
41
47
  props: Record<string, ModuleExport>;
@@ -47,25 +53,146 @@ type JsxFingerprint = {
47
53
  intrinsicElementCount: number;
48
54
  };
49
55
 
56
+ type InstalledManifest = { name: string; version: string };
57
+
50
58
  const FINGERPRINT_FILENAME = ".codegen-fingerprint.json";
51
- const CODEGEN_VERSION: string = packageManifest.version;
52
- const OVERRIDES_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..", "overrides");
59
+ const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
60
+ const OVERRIDES_ROOT = join(PACKAGE_ROOT, "overrides");
61
+ const requireFromCodegen = createRequire(import.meta.url);
62
+ const codegenHashCache: Map<string, string> = new Map();
63
+
64
+ const compareOrdinal = (a: string, b: string): number => {
65
+ if (a < b) {
66
+ return -1;
67
+ }
68
+
69
+ return a > b ? 1 : 0;
70
+ };
53
71
 
54
- const sortAlpha = (values: string[]): string => sortStrings(values).join(",");
72
+ const sortOrdinal = (values: string[]): string[] => values.toSorted(compareOrdinal);
73
+ const sortAlpha = (values: string[]): string => sortOrdinal(values).join(",");
55
74
 
56
- const overrideFiles = (): string[] => {
57
- if (!existsSync(OVERRIDES_ROOT)) {
75
+ const filesUnder = (root: string): string[] => {
76
+ if (!existsSync(root)) {
58
77
  return [];
59
78
  }
60
79
 
61
- return readdirSync(OVERRIDES_ROOT, { recursive: true, withFileTypes: true })
80
+ return readdirSync(root, { recursive: true, withFileTypes: true })
62
81
  .filter((entry: Dirent) => entry.isFile())
63
82
  .map((entry: Dirent) => join(entry.parentPath, entry.name));
64
83
  };
65
84
 
85
+ const hashTree = (hash: Hash, label: string, root: string): void => {
86
+ const files = sortOrdinal(filesUnder(root));
87
+
88
+ for (const file of files) {
89
+ hash.update("\n");
90
+ hash.update(join(label, relative(root, file)));
91
+ hash.update("\0");
92
+ hash.update(readFileSync(file));
93
+ }
94
+ };
95
+
96
+ const isInstalledManifest = (value: unknown): value is InstalledManifest =>
97
+ hasFields<InstalledManifest>(value, { name: isString, version: isString });
98
+
99
+ const tryResolve = (specifier: string): string | undefined => {
100
+ try {
101
+ return requireFromCodegen.resolve(specifier);
102
+ } catch {
103
+ return undefined;
104
+ }
105
+ };
106
+
107
+ const manifestVersion = (path: string | undefined, name: string): string | undefined => {
108
+ if (path === undefined) {
109
+ return undefined;
110
+ }
111
+
112
+ const manifest = readJsonFile(path);
113
+
114
+ return isInstalledManifest(manifest) && manifest.name === name ? manifest.version : undefined;
115
+ };
116
+
117
+ const packageRootFor = (name: string): string | undefined => {
118
+ const entry = tryResolve(name);
119
+
120
+ if (entry === undefined) {
121
+ return undefined;
122
+ }
123
+
124
+ for (let directory = dirname(entry); directory !== dirname(directory); directory = dirname(directory)) {
125
+ if (manifestVersion(join(directory, "package.json"), name) !== undefined) {
126
+ return directory;
127
+ }
128
+ }
129
+
130
+ return undefined;
131
+ };
132
+
133
+ const installedVersion = (name: string): string | undefined => {
134
+ const direct = manifestVersion(tryResolve(`${name}/package.json`), name);
135
+
136
+ if (direct !== undefined) {
137
+ return direct;
138
+ }
139
+
140
+ const root = packageRootFor(name);
141
+
142
+ return root === undefined ? undefined : manifestVersion(join(root, "package.json"), name);
143
+ };
144
+
145
+ const dependencyVersions = (): string[] =>
146
+ sortOrdinal(
147
+ Object.entries(packageManifest.dependencies)
148
+ .filter(([name]) => !name.startsWith("@types/"))
149
+ .map(([name, range]) => `${name}@${installedVersion(name) ?? range}`),
150
+ );
151
+
152
+ const hashPackageCode = (hash: Hash, label: string, root: string): void => {
153
+ const source = join(root, "src");
154
+
155
+ if (existsSync(source)) {
156
+ hashTree(hash, join(label, "src"), source);
157
+
158
+ return;
159
+ }
160
+
161
+ hashTree(hash, join(label, "dist"), join(root, "dist"));
162
+ };
163
+
164
+ const computeCodegenHash = (): string => {
165
+ const hash = createHash("sha256");
166
+ hash.update(dependencyVersions().join(","));
167
+ hashPackageCode(hash, "codegen", PACKAGE_ROOT);
168
+ hashTree(hash, "overrides", OVERRIDES_ROOT);
169
+ const utilsRoot = packageRootFor("@gtkx/utils");
170
+
171
+ if (utilsRoot !== undefined) {
172
+ hashPackageCode(hash, "@gtkx/utils", utilsRoot);
173
+ }
174
+
175
+ return hash.digest("hex");
176
+ };
177
+
178
+ const codegenHash = (): string => {
179
+ const cached = codegenHashCache.get("codegen");
180
+
181
+ if (cached !== undefined) {
182
+ return cached;
183
+ }
184
+
185
+ const value = computeCodegenHash();
186
+ codegenHashCache.set("codegen", value);
187
+
188
+ return value;
189
+ };
190
+
66
191
  const hashGi = (inputs: GiInputs): string => {
67
192
  const hash = createHash("sha256");
68
- hash.update(CODEGEN_VERSION);
193
+ hash.update(codegenHash());
194
+ hash.update("\n");
195
+ hash.update(JSON.stringify(EXTERNAL_NAMESPACES));
69
196
  hash.update("\n");
70
197
  hash.update(sortAlpha(inputs.libraries));
71
198
  hash.update("\n");
@@ -73,9 +200,16 @@ const hashGi = (inputs: GiInputs): string => {
73
200
  hash.update("\n");
74
201
  hash.update(String(inputs.isByteArrayTyped));
75
202
  hash.update("\n");
76
- const hashedFiles = sortStrings([...inputs.girFiles, ...overrideFiles()]);
203
+ hash.update(String(inputs.isValueUnwrapped));
204
+ hash.update("\n");
205
+ hash.update(String(inputs.isFinishTrimmed));
206
+ hash.update("\n");
207
+ hash.update(String(inputs.isInoutInPlace));
208
+ hash.update("\n");
209
+ hash.update(String(inputs.storeVersion));
210
+ const girFiles = sortOrdinal(inputs.girFiles);
77
211
 
78
- for (const file of hashedFiles) {
212
+ for (const file of girFiles) {
79
213
  hash.update("\n");
80
214
  hash.update(file);
81
215
  hash.update("\0");
@@ -161,19 +295,20 @@ const isDocsOutputFresh = (outDir: string, inputs: GiInputs, input: DocsFingerpr
161
295
  };
162
296
 
163
297
  const serializeModuleExports = (map: Record<string, ModuleExport>): [string, string, string][] =>
164
- sortStrings(Object.keys(map)).map((type) => [type, map[type]?.module ?? "", map[type]?.export ?? ""]);
298
+ sortOrdinal(Object.keys(map)).map((type) => [type, map[type]?.module ?? "", map[type]?.export ?? ""]);
165
299
 
166
300
  const serializeOmittedProps = (map: Record<string, string[]>): [string, string][] =>
167
- sortStrings(Object.keys(map)).map((type) => [type, sortAlpha(map[type] ?? [])]);
301
+ sortOrdinal(Object.keys(map)).map((type) => [type, sortAlpha(map[type] ?? [])]);
168
302
 
169
303
  const hashJsx = (input: JsxFingerprintInput): string =>
170
304
  createHash("sha256")
171
305
  .update(
172
306
  JSON.stringify([
173
- CODEGEN_VERSION,
307
+ codegenHash(),
174
308
  input.reactVersion,
309
+ sortOrdinal(input.reactSubexports),
175
310
  serializeModuleExports(input.components),
176
- sortStrings(input.lazyElements),
311
+ sortOrdinal(input.lazyElements),
177
312
  serializeModuleExports(input.props),
178
313
  serializeOmittedProps(input.omittedProps),
179
314
  ]),
package/src/gi.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import type { Library } from "./gir/library.js";
2
2
  import type { StoreOptions } from "./store/store-fs.js";
3
3
  import { computeGiFingerprint } from "./fingerprint.js";
4
+ import { externalPackageFor } from "./gir/external-namespaces.js";
4
5
  import { namespaceDirectory } from "./gir/namespace.js";
5
- import { type GiNamespaceInput, writeGiStore } from "./store/gi-store.js";
6
+ import { type GiExternalNamespaceInput, type GiNamespaceInput, writeGiStore } from "./store/gi-store.js";
7
+ import { collectGeneratedLibraries } from "./store/gi/generated-libraries.js";
6
8
  import { generateNamespaceModule } from "./store/gi/pipeline.js";
7
9
 
8
10
  type GiCodegenOptions = {
@@ -10,25 +12,62 @@ type GiCodegenOptions = {
10
12
  libraries: string[];
11
13
  girPath: string[];
12
14
  isByteArrayTyped: boolean;
15
+ isValueUnwrapped: boolean;
16
+ isFinishTrimmed: boolean;
17
+ isInoutInPlace: boolean;
13
18
  };
14
19
 
15
- const runGiCodegen = (library: Library, options: GiCodegenOptions): number => {
16
- const { gi, libraries, girPath, isByteArrayTyped } = options;
20
+ type SplitNamespaces = {
21
+ namespaces: GiNamespaceInput[];
22
+ externalNamespaces: GiExternalNamespaceInput[];
23
+ };
24
+
25
+ const splitNamespaces = (library: Library): SplitNamespaces => {
26
+ const namespaces: GiNamespaceInput[] = [];
27
+ const externalNamespaces: GiExternalNamespaceInput[] = [];
28
+
29
+ for (const namespace of library.namespaces.values()) {
30
+ const packageName = externalPackageFor(namespace.name);
31
+
32
+ if (packageName !== undefined) {
33
+ externalNamespaces.push({
34
+ directory: namespaceDirectory(namespace),
35
+ packageName,
36
+ girFile: namespace.girFile,
37
+ });
38
+
39
+ continue;
40
+ }
17
41
 
18
- const namespaces: GiNamespaceInput[] = Array.from(library.namespaces.values(), (namespace) => ({
19
- directory: namespaceDirectory(namespace),
20
- rawSource: generateNamespaceModule(namespace, library),
21
- girFile: namespace.girFile,
22
- }));
42
+ namespaces.push({
43
+ directory: namespaceDirectory(namespace),
44
+ rawSource: generateNamespaceModule(namespace, library),
45
+ girFile: namespace.girFile,
46
+ });
47
+ }
48
+
49
+ return { namespaces, externalNamespaces };
50
+ };
51
+
52
+ const runGiCodegen = (library: Library, options: GiCodegenOptions): number => {
53
+ const { gi, libraries, girPath, isByteArrayTyped, isValueUnwrapped, isFinishTrimmed, isInoutInPlace } = options;
54
+ const { namespaces, externalNamespaces } = splitNamespaces(library);
23
55
 
24
56
  const fingerprint = computeGiFingerprint({
25
57
  girFiles: library.girFiles,
26
58
  libraries: [...libraries],
27
59
  girPath: [...girPath],
60
+ storeVersion: gi.version,
28
61
  isByteArrayTyped,
62
+ isValueUnwrapped,
63
+ isFinishTrimmed,
64
+ isInoutInPlace,
29
65
  });
30
66
 
31
- writeGiStore(gi, namespaces, fingerprint);
67
+ writeGiStore(gi, namespaces, externalNamespaces, {
68
+ fingerprint,
69
+ libraries: collectGeneratedLibraries(library.namespaces, libraries),
70
+ });
32
71
 
33
72
  return library.namespaces.size;
34
73
  };
@@ -0,0 +1,8 @@
1
+ type ExternalNamespace = { namespace: string; packageName: string };
2
+
3
+ const EXTERNAL_NAMESPACES: ExternalNamespace[] = [{ namespace: "cairo", packageName: "@gtkx/cairo" }];
4
+
5
+ const externalPackageFor = (namespaceName: string): string | undefined =>
6
+ EXTERNAL_NAMESPACES.find((entry) => entry.namespace === namespaceName)?.packageName;
7
+
8
+ export { EXTERNAL_NAMESPACES, externalPackageFor, type ExternalNamespace };
@@ -0,0 +1,5 @@
1
+ const FUNCTIONS_MISSING_FINISH_FUNC: Map<string, string> = new Map([
2
+ ["g_file_replace_contents_bytes_async", "replace_contents_finish"],
3
+ ]);
4
+
5
+ export { FUNCTIONS_MISSING_FINISH_FUNC };
@@ -1,9 +1,12 @@
1
1
  import type { GirAnnotations } from "./annotations.js";
2
2
  import type { ParseContext } from "./type-id.js";
3
3
  import { type CursorParameterNames, PARAMETERS_MISSING_ARRAY_EXTENT } from "./cursor-overrides.js";
4
+ import { FUNCTIONS_MISSING_FINISH_FUNC } from "./finish-overrides.js";
5
+ import { HIDDEN_SYMBOLS } from "./hidden-symbols.js";
4
6
  import { PARAMETERS_MISSING_NULLABLE_ANNOTATION } from "./nullable-overrides.js";
5
7
  import { type GirParameter, type GirReturnValue, parameterFromNode, parseCallable } from "./parameter.js";
6
8
  import { attr, getChild, type RawNode } from "./parse.js";
9
+ import { RETURNS_MISSING_UCS4_ARRAY_TYPE } from "./ucs4-overrides.js";
7
10
 
8
11
  /** A callable declared by a GIR namespace, class, or record: a function, a method, or a constructor. */
9
12
  type GirFunction = {
@@ -17,13 +20,16 @@ type GirFunction = {
17
20
  cIdentifier: string | undefined;
18
21
  /** Whether the C function takes a trailing `GError` out parameter and can fail. */
19
22
  throws: boolean;
20
- /** Whether the GIR marks the callable as introspectable, and so bindable. */
23
+ /** Whether the callable is bindable: the GIR marks it introspectable and GTKX does not hide it. */
21
24
  introspectable: boolean;
22
25
  /** Qualified `Type.member` the GIR redirects the callable to, from its `moved-to` annotation. */
23
26
  movedTo: string | undefined;
24
27
  /** Name of the callable that shadows this one, from the GIR's `shadowed-by` annotation. */
25
28
  shadowedBy: string | undefined;
26
- /** Name of the callable that completes this asynchronous one, from the GIR's `glib:finish-func`. */
29
+ /**
30
+ * Name of the callable that completes this asynchronous one, from the GIR's `glib:finish-func`
31
+ * or a GTKX override for a GIR that lacks the annotation.
32
+ */
27
33
  finishFunc: string | undefined;
28
34
  /** The instance the callable is invoked on, absent for constructors and namespace-level functions. */
29
35
  instance: GirParameter | undefined;
@@ -84,20 +90,52 @@ const bindMissingArrayExtent = (fn: GirFunction): GirFunction => {
84
90
  return fn;
85
91
  };
86
92
 
93
+ const bindMissingUcs4ReturnArray = (fn: GirFunction, context: ParseContext): GirFunction => {
94
+ if (fn.cIdentifier !== undefined && RETURNS_MISSING_UCS4_ARRAY_TYPE.has(fn.cIdentifier)) {
95
+ fn.returnValue.type = context.addContainer({
96
+ kind: "carray",
97
+ element: context.addPrimitive("unichar"),
98
+ elementCType: "gunichar",
99
+ arrayCType: "gunichar*",
100
+ lengthParameterIndex: undefined,
101
+ fixedSize: undefined,
102
+ isZeroTerminated: true,
103
+ });
104
+ }
105
+
106
+ return fn;
107
+ };
108
+
109
+ const annotatedFinishFunc = (node: RawNode, cIdentifier: string | undefined): string | undefined => {
110
+ const annotated = attr(node, "glib:finish-func");
111
+
112
+ if (annotated !== undefined) {
113
+ return annotated;
114
+ }
115
+
116
+ return cIdentifier === undefined ? undefined : FUNCTIONS_MISSING_FINISH_FUNC.get(cIdentifier);
117
+ };
118
+
119
+ const isHiddenSymbol = (cIdentifier: string | undefined): boolean =>
120
+ cIdentifier !== undefined && HIDDEN_SYMBOLS.has(cIdentifier);
121
+
87
122
  const functionFromNode = (node: RawNode, context: ParseContext): GirFunction => {
88
123
  const instanceNode = getChild(getChild(node, "parameters"), "instance-parameter");
89
-
90
- return bindMissingArrayExtent(
91
- relaxMissingNullable({
92
- ...parseCallable(node, context),
93
- name: attr(node, "shadows") ?? attr(node, "name") ?? "",
94
- cIdentifier: attr(node, "c:identifier"),
95
- movedTo: attr(node, "moved-to"),
96
- shadowedBy: attr(node, "shadowed-by"),
97
- finishFunc: attr(node, "glib:finish-func"),
98
- instance: instanceNode === undefined ? undefined : parameterFromNode(instanceNode, context),
99
- }),
100
- );
124
+ const callable = parseCallable(node, context);
125
+ const cIdentifier = attr(node, "c:identifier");
126
+
127
+ const fn: GirFunction = {
128
+ ...callable,
129
+ introspectable: callable.introspectable && !isHiddenSymbol(cIdentifier),
130
+ name: attr(node, "shadows") ?? attr(node, "name") ?? "",
131
+ cIdentifier,
132
+ movedTo: attr(node, "moved-to"),
133
+ shadowedBy: attr(node, "shadowed-by"),
134
+ finishFunc: annotatedFinishFunc(node, cIdentifier),
135
+ instance: instanceNode === undefined ? undefined : parameterFromNode(instanceNode, context),
136
+ };
137
+
138
+ return bindMissingUcs4ReturnArray(bindMissingArrayExtent(relaxMissingNullable(fn)), context);
101
139
  };
102
140
 
103
141
  export { functionFromNode, type GirFunction };
@@ -0,0 +1,39 @@
1
+ const HIDDEN_SYMBOLS: Set<string> = new Set([
2
+ "g_action_map_add_action_entries",
3
+ "g_ascii_dtostr",
4
+ "g_ascii_formatd",
5
+ "g_file_info_set_attribute",
6
+ "g_file_set_attribute",
7
+ "g_log_set_writer_func",
8
+ "g_object_get_data",
9
+ "g_object_get_qdata",
10
+ "g_object_set_data",
11
+ "g_object_set_data_full",
12
+ "g_object_set_qdata",
13
+ "g_object_set_qdata_full",
14
+ "g_object_steal_data",
15
+ "g_object_steal_qdata",
16
+ "g_param_spec_get_qdata",
17
+ "g_param_spec_set_qdata",
18
+ "g_param_spec_set_qdata_full",
19
+ "g_param_spec_steal_qdata",
20
+ "g_ref_string_acquire",
21
+ "g_ref_string_length",
22
+ "g_ref_string_new",
23
+ "g_ref_string_new_intern",
24
+ "g_ref_string_new_len",
25
+ "g_ref_string_release",
26
+ "g_stpcpy",
27
+ "g_strcanon",
28
+ "g_strchomp",
29
+ "g_strchug",
30
+ "g_strdelimit",
31
+ "g_strdown",
32
+ "g_strreverse",
33
+ "g_strup",
34
+ "g_thread_exit",
35
+ "g_thread_new",
36
+ "g_thread_try_new",
37
+ ]);
38
+
39
+ export { HIDDEN_SYMBOLS };