@gtkx/codegen 1.1.0 → 1.2.1

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 (169) hide show
  1. package/dist/analysis/descriptor-render.d.ts.map +1 -1
  2. package/dist/analysis/descriptor-render.js +14 -10
  3. package/dist/analysis/descriptor-render.js.map +1 -1
  4. package/dist/analysis/descriptor.d.ts +11 -5
  5. package/dist/analysis/descriptor.d.ts.map +1 -1
  6. package/dist/analysis/descriptor.js +25 -9
  7. package/dist/analysis/descriptor.js.map +1 -1
  8. package/dist/analysis/interface-conflicts.d.ts +3 -1
  9. package/dist/analysis/interface-conflicts.d.ts.map +1 -1
  10. package/dist/analysis/interface-conflicts.js +3 -8
  11. package/dist/analysis/interface-conflicts.js.map +1 -1
  12. package/dist/analysis/ts-type.d.ts +3 -2
  13. package/dist/analysis/ts-type.d.ts.map +1 -1
  14. package/dist/analysis/ts-type.js +16 -9
  15. package/dist/analysis/ts-type.js.map +1 -1
  16. package/dist/analysis/type-shape.d.ts +3 -2
  17. package/dist/analysis/type-shape.d.ts.map +1 -1
  18. package/dist/analysis/type-shape.js +2 -1
  19. package/dist/analysis/type-shape.js.map +1 -1
  20. package/dist/compile.d.ts +4 -5
  21. package/dist/compile.d.ts.map +1 -1
  22. package/dist/compile.js +56 -4
  23. package/dist/compile.js.map +1 -1
  24. package/dist/docs/api-reference.d.ts +2 -0
  25. package/dist/docs/api-reference.d.ts.map +1 -1
  26. package/dist/docs/api-reference.js +1 -1
  27. package/dist/docs/api-reference.js.map +1 -1
  28. package/dist/docs/pipeline.d.ts +1 -0
  29. package/dist/docs/pipeline.d.ts.map +1 -1
  30. package/dist/docs/pipeline.js +10 -3
  31. package/dist/docs/pipeline.js.map +1 -1
  32. package/dist/docs/symbol-page.d.ts.map +1 -1
  33. package/dist/docs/symbol-page.js +2 -4
  34. package/dist/docs/symbol-page.js.map +1 -1
  35. package/dist/fingerprint.d.ts +11 -5
  36. package/dist/fingerprint.d.ts.map +1 -1
  37. package/dist/fingerprint.js +22 -19
  38. package/dist/fingerprint.js.map +1 -1
  39. package/dist/gi.d.ts +1 -0
  40. package/dist/gi.d.ts.map +1 -1
  41. package/dist/gi.js +7 -2
  42. package/dist/gi.js.map +1 -1
  43. package/dist/gir/class.d.ts +7 -2
  44. package/dist/gir/class.d.ts.map +1 -1
  45. package/dist/gir/class.js +4 -1
  46. package/dist/gir/class.js.map +1 -1
  47. package/dist/gir/gir-path.d.ts +1 -1
  48. package/dist/gir/gir-path.js +1 -1
  49. package/dist/gir/gir-path.js.map +1 -1
  50. package/dist/gir/library.d.ts +5 -1
  51. package/dist/gir/library.d.ts.map +1 -1
  52. package/dist/gir/library.js +8 -1
  53. package/dist/gir/library.js.map +1 -1
  54. package/dist/gir/parse.d.ts.map +1 -1
  55. package/dist/gir/parse.js +4 -1
  56. package/dist/gir/parse.js.map +1 -1
  57. package/dist/gir/primitives.js +1 -1
  58. package/dist/gir/primitives.js.map +1 -1
  59. package/dist/khronos/args.d.ts.map +1 -1
  60. package/dist/khronos/args.js +8 -6
  61. package/dist/khronos/args.js.map +1 -1
  62. package/dist/khronos/render.d.ts.map +1 -1
  63. package/dist/khronos/render.js +8 -4
  64. package/dist/khronos/render.js.map +1 -1
  65. package/dist/runner.d.ts +2 -0
  66. package/dist/runner.d.ts.map +1 -1
  67. package/dist/runner.js +5 -3
  68. package/dist/runner.js.map +1 -1
  69. package/dist/staging.d.ts.map +1 -1
  70. package/dist/staging.js +1 -1
  71. package/dist/staging.js.map +1 -1
  72. package/dist/store/compile-store.d.ts +0 -1
  73. package/dist/store/compile-store.d.ts.map +1 -1
  74. package/dist/store/compile-store.js +7 -34
  75. package/dist/store/compile-store.js.map +1 -1
  76. package/dist/store/gi/callables.d.ts +2 -4
  77. package/dist/store/gi/callables.d.ts.map +1 -1
  78. package/dist/store/gi/callables.js +22 -7
  79. package/dist/store/gi/callables.js.map +1 -1
  80. package/dist/store/gi/class.d.ts.map +1 -1
  81. package/dist/store/gi/class.js +63 -17
  82. package/dist/store/gi/class.js.map +1 -1
  83. package/dist/store/gi/constant.js +1 -1
  84. package/dist/store/gi/constant.js.map +1 -1
  85. package/dist/store/gi/enum.d.ts.map +1 -1
  86. package/dist/store/gi/enum.js +2 -5
  87. package/dist/store/gi/enum.js.map +1 -1
  88. package/dist/store/gi/gtype-binding.d.ts +2 -1
  89. package/dist/store/gi/gtype-binding.d.ts.map +1 -1
  90. package/dist/store/gi/gtype-binding.js +13 -1
  91. package/dist/store/gi/gtype-binding.js.map +1 -1
  92. package/dist/store/gi/interface.js +4 -4
  93. package/dist/store/gi/interface.js.map +1 -1
  94. package/dist/store/gi/method.d.ts.map +1 -1
  95. package/dist/store/gi/method.js +4 -7
  96. package/dist/store/gi/method.js.map +1 -1
  97. package/dist/store/gi/property-accessor.d.ts +0 -3
  98. package/dist/store/gi/property-accessor.d.ts.map +1 -1
  99. package/dist/store/gi/property-accessor.js +15 -108
  100. package/dist/store/gi/property-accessor.js.map +1 -1
  101. package/dist/store/gi/record-constructor.d.ts.map +1 -1
  102. package/dist/store/gi/record-constructor.js +7 -5
  103. package/dist/store/gi/record-constructor.js.map +1 -1
  104. package/dist/store/gi/record-field-accessor.d.ts +3 -2
  105. package/dist/store/gi/record-field-accessor.d.ts.map +1 -1
  106. package/dist/store/gi/record-field-accessor.js +42 -12
  107. package/dist/store/gi/record-field-accessor.js.map +1 -1
  108. package/dist/store/gi/record.d.ts.map +1 -1
  109. package/dist/store/gi/record.js +5 -9
  110. package/dist/store/gi/record.js.map +1 -1
  111. package/dist/store/gi/return-wrap.js +1 -1
  112. package/dist/store/gi/return-wrap.js.map +1 -1
  113. package/dist/store/gi/signal.d.ts.map +1 -1
  114. package/dist/store/gi/signal.js +27 -6
  115. package/dist/store/gi/signal.js.map +1 -1
  116. package/dist/store/jsx-store.d.ts.map +1 -1
  117. package/dist/store/jsx-store.js +0 -1
  118. package/dist/store/jsx-store.js.map +1 -1
  119. package/dist/store/resolve-store.d.ts.map +1 -1
  120. package/dist/store/resolve-store.js +1 -0
  121. package/dist/store/resolve-store.js.map +1 -1
  122. package/dist/store/store-fs.d.ts +0 -1
  123. package/dist/store/store-fs.d.ts.map +1 -1
  124. package/dist/store/store-fs.js +2 -7
  125. package/dist/store/store-fs.js.map +1 -1
  126. package/dist/xml.d.ts +1 -0
  127. package/dist/xml.d.ts.map +1 -1
  128. package/dist/xml.js +28 -1
  129. package/dist/xml.js.map +1 -1
  130. package/overrides/gobject/object.ts.ejs +2 -0
  131. package/package.json +3 -3
  132. package/src/analysis/descriptor-render.ts +19 -10
  133. package/src/analysis/descriptor.ts +43 -9
  134. package/src/analysis/interface-conflicts.ts +6 -8
  135. package/src/analysis/ts-type.ts +30 -11
  136. package/src/analysis/type-shape.ts +5 -2
  137. package/src/compile.ts +87 -6
  138. package/src/docs/api-reference.ts +3 -1
  139. package/src/docs/pipeline.ts +13 -7
  140. package/src/docs/symbol-page.ts +1 -5
  141. package/src/fingerprint.ts +31 -38
  142. package/src/gi.ts +9 -2
  143. package/src/gir/class.ts +12 -3
  144. package/src/gir/gir-path.ts +1 -1
  145. package/src/gir/library.ts +9 -1
  146. package/src/gir/parse.ts +6 -1
  147. package/src/gir/primitives.ts +1 -1
  148. package/src/khronos/args.ts +9 -6
  149. package/src/khronos/render.ts +8 -6
  150. package/src/runner.ts +7 -3
  151. package/src/staging.ts +1 -3
  152. package/src/store/compile-store.ts +7 -40
  153. package/src/store/gi/callables.ts +30 -11
  154. package/src/store/gi/class.ts +96 -21
  155. package/src/store/gi/constant.ts +1 -1
  156. package/src/store/gi/enum.ts +2 -6
  157. package/src/store/gi/gtype-binding.ts +16 -1
  158. package/src/store/gi/interface.ts +4 -4
  159. package/src/store/gi/method.ts +4 -9
  160. package/src/store/gi/property-accessor.ts +22 -193
  161. package/src/store/gi/record-constructor.ts +7 -5
  162. package/src/store/gi/record-field-accessor.ts +65 -13
  163. package/src/store/gi/record.ts +13 -17
  164. package/src/store/gi/return-wrap.ts +1 -1
  165. package/src/store/gi/signal.ts +36 -13
  166. package/src/store/jsx-store.ts +0 -1
  167. package/src/store/resolve-store.ts +1 -0
  168. package/src/store/store-fs.ts +3 -9
  169. package/src/xml.ts +43 -1
package/src/gi.ts CHANGED
@@ -9,10 +9,11 @@ type GiCodegenOptions = {
9
9
  gi: StoreOptions;
10
10
  libraries: string[];
11
11
  girPath: string[];
12
+ isByteArrayTyped: boolean;
12
13
  };
13
14
 
14
15
  const runGiCodegen = (library: Library, options: GiCodegenOptions): number => {
15
- const { gi, libraries, girPath } = options;
16
+ const { gi, libraries, girPath, isByteArrayTyped } = options;
16
17
 
17
18
  const namespaces: GiNamespaceInput[] = Array.from(library.namespaces.values(), (namespace) => ({
18
19
  directory: namespaceDirectory(namespace),
@@ -20,7 +21,13 @@ const runGiCodegen = (library: Library, options: GiCodegenOptions): number => {
20
21
  girFile: namespace.girFile,
21
22
  }));
22
23
 
23
- const fingerprint = computeGiFingerprint(library.girFiles, [...libraries], [...girPath]);
24
+ const fingerprint = computeGiFingerprint({
25
+ girFiles: library.girFiles,
26
+ libraries: [...libraries],
27
+ girPath: [...girPath],
28
+ isByteArrayTyped,
29
+ });
30
+
24
31
  writeGiStore(gi, namespaces, fingerprint);
25
32
 
26
33
  return library.namespaces.size;
package/src/gir/class.ts CHANGED
@@ -11,6 +11,12 @@ type GirVirtualMethod = GirCallable & {
11
11
  invoker: string | undefined;
12
12
  };
13
13
 
14
+ /** A `<glib:signal>` on a class or interface. */
15
+ type GirSignal = GirCallable & {
16
+ /** Whether the signal takes a `::detail` suffix, so `changed::key` reaches only that detail. */
17
+ isDetailed: boolean;
18
+ };
19
+
14
20
  /** A `<class>` or `<interface>` declared by a GIR namespace. */
15
21
  type GirClass = {
16
22
  /** Name GIR gives the type, without its namespace prefix. */
@@ -54,7 +60,7 @@ type GirClass = {
54
60
  /** GObject properties the type declares. */
55
61
  properties: GirProperty[];
56
62
  /** GObject signals the type declares. */
57
- signals: GirCallable[];
63
+ signals: GirSignal[];
58
64
  /** Virtual methods the type declares, carrying the documentation their vtable slots are generated from. */
59
65
  vfuncs: GirVirtualMethod[];
60
66
  };
@@ -88,8 +94,11 @@ const classFromNode = (node: RawNode, isInterface: boolean, context: ParseContex
88
94
  constructors: getChildren(node, GIR_CONSTRUCTOR_TAG).map((ctor) => functionFromNode(ctor, context)),
89
95
  functions: getChildren(node, "function").map((fn) => functionFromNode(fn, context)),
90
96
  properties: getChildren(node, "property").map((property) => propertyFromNode(property, context)),
91
- signals: getChildren(node, "glib:signal").map((signal) => parseCallable(signal, context)),
97
+ signals: getChildren(node, "glib:signal").map((signal) => ({
98
+ ...parseCallable(signal, context),
99
+ isDetailed: isAttrTrue(signal, "detailed"),
100
+ })),
92
101
  vfuncs: virtualMethodsFromNode(node, context),
93
102
  });
94
103
 
95
- export { classFromNode, type GirClass, type GirVirtualMethod };
104
+ export { classFromNode, type GirClass, type GirSignal, type GirVirtualMethod };
@@ -7,7 +7,7 @@ const SYSTEM_GIR_PATH = "/usr/share/gir-1.0";
7
7
  /**
8
8
  * The directories to search for `.gir` files, in precedence order: the config's own `girPath`, then
9
9
  * `GTKX_GIR_PATH`, then `/usr/share/gir-1.0`, then the girdir pkg-config reports for
10
- * gobject-introspection-1.0. Duplicates are dropped, and the last two are included only when they exist.
10
+ * gobject-introspection-1.0. Duplicates are dropped, and the system directories are included only when they exist.
11
11
  *
12
12
  * @throws If pkg-config is installed but fails while being queried.
13
13
  */
@@ -84,10 +84,12 @@ class Library {
84
84
  *
85
85
  * @param libraries GIR identifiers to load, such as `Gtk-4.0`.
86
86
  * @param girPath Directories searched, in order, for each `.gir` file.
87
+ * @param isByteArrayTyped Whether byte sequences render as `Uint8Array` rather than `number[]`.
87
88
  * @returns A library holding every namespace that was parsed.
88
89
  */
89
- static load(libraries: string[], girPath: string[]): Library {
90
+ static load(libraries: string[], girPath: string[], isByteArrayTyped = false): Library {
90
91
  const library = new Library();
92
+ library.isByteArrayTypedValue = isByteArrayTyped;
91
93
  this.drive(library, libraries, girPath);
92
94
 
93
95
  return library;
@@ -99,6 +101,7 @@ class Library {
99
101
  private nsIdByName: Map<string, number> = new Map();
100
102
  private typeTables: TypeTable[] = [];
101
103
  private girFilesValue: string[] = [];
104
+ private isByteArrayTypedValue = false;
102
105
 
103
106
  /** Creates a library with no namespaces loaded; {@link Library.load} builds a populated one. */
104
107
  constructor() {
@@ -328,6 +331,11 @@ class Library {
328
331
  return this.namespacesByName;
329
332
  }
330
333
 
334
+ /** Whether byte sequences render as `Uint8Array` rather than `number[]`. */
335
+ public get isByteArrayTyped(): boolean {
336
+ return this.isByteArrayTypedValue;
337
+ }
338
+
331
339
  /** Paths of the `.gir` files that were read, in the order they were discovered. */
332
340
  public get girFiles(): string[] {
333
341
  return this.girFilesValue;
package/src/gir/parse.ts CHANGED
@@ -5,6 +5,11 @@ type RawNode = Record<string, unknown>;
5
5
 
6
6
  const GIR_LABEL = "GIR file";
7
7
 
8
+ const GIR_MALFORMED_REMEDY =
9
+ "No XML parser can read this file, so report it to whoever ships it. To get past it now, name the " +
10
+ "libraries you need explicitly in `libraries` and leave this one out, or put a corrected copy of the " +
11
+ "file in a directory listed in `girPath`, which is searched ahead of the system location.";
12
+
8
13
  const MULTI_TAGS: Set<string> = new Set([
9
14
  "include",
10
15
  "class",
@@ -44,7 +49,7 @@ function renameReservedTag(tag: string): string {
44
49
  }
45
50
 
46
51
  const parseGirFile = (path: string): RawNode =>
47
- parseXmlFile({ parser: PARSER, label: GIR_LABEL, path }) as RawNode;
52
+ parseXmlFile({ parser: PARSER, label: GIR_LABEL, path, malformedRemedy: GIR_MALFORMED_REMEDY }) as RawNode;
48
53
 
49
54
  const attr = (node: RawNode | undefined, name: string): string | undefined => {
50
55
  if (node === undefined) {
@@ -110,7 +110,7 @@ const PRIMITIVE_TS_TYPE: Record<PrimitiveCategory, string> = {
110
110
  float64: "number",
111
111
  string: "string",
112
112
  unichar: "string",
113
- pointer: "number",
113
+ pointer: "bigint",
114
114
  };
115
115
 
116
116
  const primitiveCategory = (name: string): PrimitiveCategory | undefined => PRIMITIVE_BY_NAME.get(name);
@@ -2,6 +2,7 @@ import { toCamelIdentifier } from "@gtkx/utils";
2
2
  import type { GlCommand, GlParam } from "./model.js";
3
3
  import type { CommandPlan, GlScalar, ParamPlan, ReturnPlan } from "./plan.js";
4
4
  import {
5
+ type ArrayLayout,
5
6
  tArray,
6
7
  tBoolean,
7
8
  tBuffer,
@@ -50,6 +51,7 @@ type BuildArgOptions = {
50
51
  };
51
52
 
52
53
  const OUT_PLAN_KINDS: Set<string> = new Set(["ref-out", "ref-array-out", "ref-fixed-out", "string-out"]);
54
+ const NUMERIC_LAYOUT: ArrayLayout = { isBytes: false };
53
55
 
54
56
  const scalarAliasOrGroup = (scalar: GlScalar, group: string | undefined): string =>
55
57
  group !== undefined && scalar.isGroupBearing === true ? group : scalar.tsAlias;
@@ -122,12 +124,13 @@ const buildInArg = (options: BuildArgOptions, name: string, track: (alias: strin
122
124
  return inArg(name, "string", tString("borrowed"));
123
125
  }
124
126
  case "string-array-in": {
125
- return inArg(name, "string[]", tArray(tString("borrowed")));
127
+ return inArg(name, "string[]", tArray(tString("borrowed"), undefined, NUMERIC_LAYOUT));
126
128
  }
127
129
  case "array-in": {
128
130
  track(scalarAliasOrGroup(plan.scalar, param.group));
131
+ const descriptor = tArray(plan.scalar.descriptor, undefined, NUMERIC_LAYOUT);
129
132
 
130
- return inArg(name, arrayInTsType(plan.scalar, param.group), tArray(plan.scalar.descriptor));
133
+ return inArg(name, arrayInTsType(plan.scalar, param.group), descriptor);
131
134
  }
132
135
  case "buffer": {
133
136
  return inArg(name, `ArrayBufferView | ${track("GLintptr")} | null`, tBuffer);
@@ -136,7 +139,7 @@ const buildInArg = (options: BuildArgOptions, name: string, track: (alias: strin
136
139
  return inArg(name, track("GLintptr"), tUint64);
137
140
  }
138
141
  case "byte-offset-array": {
139
- return inArg(name, `${track("GLintptr")}[]`, tArray(tUint64));
142
+ return inArg(name, `${track("GLintptr")}[]`, tArray(tUint64, undefined, NUMERIC_LAYOUT));
140
143
  }
141
144
  case "ref-array-out":
142
145
  case "ref-fixed-out":
@@ -165,14 +168,14 @@ const outArgFields = (options: OutArgFieldsOptions): OutArgFields => {
165
168
  return {
166
169
  seed: `const ${cellName} = { value: new Array<number>(${lenIdentifier}).fill(0) };`,
167
170
  tsType: `${track(scalarAliasOrGroup(plan.scalar, param.group))}[]`,
168
- descriptor: tRef(tSizedArray(plan.scalar.descriptor, sizeIndex)),
171
+ descriptor: tRef(tSizedArray(plan.scalar.descriptor, sizeIndex, undefined, NUMERIC_LAYOUT)),
169
172
  };
170
173
  }
171
174
  case "ref-fixed-out": {
172
175
  return {
173
176
  seed: `const ${cellName} = { value: new Array<number>(${String(plan.length)}).fill(0) };`,
174
177
  tsType: `${track(plan.scalar.tsAlias)}[]`,
175
- descriptor: tRef(tFixedArray(plan.scalar.descriptor, plan.length)),
178
+ descriptor: tRef(tFixedArray(plan.scalar.descriptor, plan.length, undefined, NUMERIC_LAYOUT)),
176
179
  };
177
180
  }
178
181
  case "string-out": {
@@ -204,7 +207,7 @@ const buildOutArg = (options: BuildArgOptions, track: (alias: string) => string)
204
207
  throw new Error(`Parameter index ${String(index)} out of range on ${command.name}`);
205
208
  }
206
209
 
207
- const cellName = `out${String(outIndex)}`;
210
+ const cellName = `__out${String(outIndex)}`;
208
211
  const cell: OutArgCell = { isOut: true, cellName, paramIndex: index };
209
212
 
210
213
  return { ...cell, ...outArgFields({ command, plan, param, cellName, track }) };
@@ -114,7 +114,7 @@ const returnStatements = (call: string, returned: EmittedReturn, outs: OutArg[])
114
114
  return [`${call};`, tail];
115
115
  }
116
116
 
117
- return [`const result = ${call};`, `return [${returned.expr("result")}, ${outValues.join(", ")}];`];
117
+ return [`const __result = ${call};`, `return [${returned.expr("__result")}, ${outValues.join(", ")}];`];
118
118
  };
119
119
 
120
120
  const renderCommand = (
@@ -135,7 +135,7 @@ const renderCommand = (
135
135
  const seeds = outs.map((out) => out.seed);
136
136
  const bindExpression = glBind(command.name, descriptors, returned.descriptor);
137
137
  const isInline = plan.params.some((paramPlan) => paramPlan.kind === "string-out");
138
- const bindingName = isInline ? "binding" : toCamelIdentifier(command.name);
138
+ const bindingName = isInline ? "__binding" : toCamelIdentifier(command.name);
139
139
 
140
140
  const body = [
141
141
  ...seeds,
@@ -252,12 +252,14 @@ const deriveGenSingular = (
252
252
  const descriptors = [...prefix.map((arg) => arg.descriptor), countScalar.descriptor, tRef(outScalar.descriptor)];
253
253
  usedTypes.add(outScalar.tsAlias);
254
254
  const signature = prefix.map((arg) => `${arg.name}: ${arg.tsType}`).join(", ");
255
- const callArgs = [...prefix.map((arg) => arg.name), "1", "out"].join(", ");
255
+ const callArgs = [...prefix.map((arg) => arg.name), "1", "__out"].join(", ");
256
256
  const jsDoc = genSingularJsDoc({ plan, provenance, docs }, prefix, shape);
257
257
 
258
- const body = [" const out = { value: 0 };", ` ${bindingName}(${callArgs});`, " return out.value;"].join(
259
- "\n",
260
- );
258
+ const body = [
259
+ " const __out = { value: 0 };",
260
+ ` ${bindingName}(${callArgs});`,
261
+ " return __out.value;",
262
+ ].join("\n");
261
263
 
262
264
  const binding = glBind(plan.command.name, renderDescriptorList(descriptors), tVoid);
263
265
 
package/src/runner.ts CHANGED
@@ -39,6 +39,8 @@ type CodegenRunnerOptions = {
39
39
  userOmittedProps?: OmittedProps;
40
40
  /** Regenerates both stores even when their fingerprints are fresh. */
41
41
  isForced?: boolean;
42
+ /** Emits GIR byte sequences as `Uint8Array` rather than `number[]`; defaults to false. */
43
+ isByteArrayTyped?: boolean;
42
44
  };
43
45
 
44
46
  /** What a `runCodegen` run produced. */
@@ -157,12 +159,14 @@ const emitStoresWithConfig = async (config: {
157
159
  girPath: string[];
158
160
  }): Promise<StoreResult> => {
159
161
  const { options, gi, jsx, libraries, girPath } = config;
162
+ const isByteArrayTyped = options.isByteArrayTyped === true;
160
163
  let library: Library | undefined;
161
- const loadLibrary = (): Library => (library ??= Library.load(libraries, girPath));
162
- const isGiRegenerated = options.isForced === true || !isGiStoreFresh(gi.storeDir, libraries, girPath);
164
+ const loadLibrary = (): Library => (library ??= Library.load(libraries, girPath, isByteArrayTyped));
165
+ const giInputs = { girFiles: [] as string[], libraries, girPath, isByteArrayTyped };
166
+ const isGiRegenerated = options.isForced === true || !isGiStoreFresh(gi.storeDir, giInputs);
163
167
 
164
168
  const namespaces = isGiRegenerated
165
- ? runGiCodegen(loadLibrary(), { gi, libraries, girPath })
169
+ ? runGiCodegen(loadLibrary(), { gi, libraries, girPath, isByteArrayTyped })
166
170
  : 0;
167
171
 
168
172
  if (jsx === undefined) {
package/src/staging.ts CHANGED
@@ -1,12 +1,10 @@
1
+ import { errorCode } from "@gtkx/utils";
1
2
  import { mkdirSync, mkdtempSync, readdirSync, rmSync } from "node:fs";
2
3
  import { basename, dirname, join } from "node:path";
3
4
 
4
5
  const STAGING_SUFFIX = ".tmp-";
5
6
  const OWNER_PATTERN = /^(?<pid>\d+)-/;
6
7
 
7
- const errorCode = (error: unknown): string | undefined =>
8
- error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : undefined;
9
-
10
8
  const isOwnerRunning = (pid: number): boolean => {
11
9
  if (!Number.isSafeInteger(pid) || pid <= 0) {
12
10
  return false;
@@ -1,52 +1,19 @@
1
- import { rmSync, writeFileSync } from "node:fs";
1
+ import { rmSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import { compileProject, type ProjectFile, type SourceModule } from "../compile.js";
3
+ import { emitModules, type SourceModule } from "../compile.js";
4
4
 
5
5
  type CompileStoreParams = {
6
6
  storeDir: string;
7
7
  files: SourceModule[];
8
8
  packageName: string;
9
- requiresEnvReference?: boolean;
10
- };
11
-
12
- const EMIT_OPTIONS = {
13
- declaration: true,
14
- removeComments: false,
15
- sourceMap: false,
16
- declarationMap: false,
17
- rootDir: ".",
18
- outDir: ".",
19
- };
20
-
21
- const ENV_REFERENCE_FILE = "__gtkx-env__.d.ts";
22
-
23
- const writeEnvReference = (storeDir: string): (() => void) => {
24
- writeFileSync(join(storeDir, ENV_REFERENCE_FILE), "/// <reference types=\"@gtkx/react/env\" />\n");
25
-
26
- return () => {
27
- rmSync(join(storeDir, ENV_REFERENCE_FILE), { force: true });
28
- };
29
9
  };
30
10
 
31
11
  const compileStore = (params: CompileStoreParams): void => {
32
- const removeEnvReference = params.requiresEnvReference === true
33
- ? writeEnvReference(params.storeDir)
34
- : (): void => undefined;
35
-
36
- const files: ProjectFile[] = params.requiresEnvReference === true
37
- ? [{ fileName: ENV_REFERENCE_FILE }, ...params.files]
38
- : params.files;
39
-
40
- try {
41
- compileProject({
42
- projectDir: params.storeDir,
43
- files,
44
- compilerOptions: EMIT_OPTIONS,
45
- label: `the generated ${params.packageName} store`,
46
- });
47
- } finally {
48
- removeEnvReference();
49
- }
12
+ emitModules({
13
+ projectDir: params.storeDir,
14
+ files: params.files,
15
+ label: `the generated ${params.packageName} store`,
16
+ });
50
17
 
51
18
  for (const file of params.files) {
52
19
  rmSync(join(params.storeDir, file.fileName), { force: true });
@@ -7,7 +7,7 @@ import { renderBlock } from "../../writer/emit.js";
7
7
  import { matchAsyncFinish } from "./async.js";
8
8
  import { callableDoc, callableSpec } from "./callable-doc.js";
9
9
  import { renderFnExpression } from "./function.js";
10
- import { gtypeMemberDeclaration } from "./gtype-binding.js";
10
+ import { gtypeMemberDeclaration, gtypeStaticMember } from "./gtype-binding.js";
11
11
  import {
12
12
  finishCallExpression,
13
13
  memberName,
@@ -66,9 +66,18 @@ type PlainTypeMembersOptions = {
66
66
  context: ModuleContext;
67
67
  className: string;
68
68
  callables: Callables;
69
- hasGtype: boolean;
69
+ gtypeExpr: string | undefined;
70
70
  };
71
71
 
72
+ const RUNTIME_OWNED_LIFETIME_METHODS: Set<string> = new Set([
73
+ "force_floating",
74
+ "free",
75
+ "ref",
76
+ "ref_sink",
77
+ "take_ref",
78
+ "unref",
79
+ ]);
80
+
72
81
  const renderInstanceMethodSignature: InstanceMemberRenderer = instanceMemberRenderer(
73
82
  (context, callable, { name, finishFn }) =>
74
83
  `${memberDoc(context, callable, finishFn)}${memberSignatureText(context, callable, name, { finishFn })};`,
@@ -373,6 +382,7 @@ const isEmittableCallable = (context: ModuleContext, callable: GirFunction): boo
373
382
  callable.introspectable &&
374
383
  callable.shadowedBy === undefined &&
375
384
  callable.cIdentifier !== undefined &&
385
+ !RUNTIME_OWNED_LIFETIME_METHODS.has(callable.name) &&
376
386
  !hasUnmarshalableParam(context, callable);
377
387
 
378
388
  const constructorMemberName = (girName: string): string | undefined => {
@@ -408,6 +418,20 @@ const renderStaticSignature = (
408
418
  };
409
419
  };
410
420
 
421
+ const staticMemberName = (
422
+ context: ModuleContext,
423
+ callable: GirFunction,
424
+ resolveName: (girName: string) => string | undefined,
425
+ ): string | undefined => {
426
+ if (!isEmittableCallable(context, callable)) {
427
+ return undefined;
428
+ }
429
+
430
+ const name = resolveName(callable.name);
431
+
432
+ return name !== undefined && isEmittableMemberName(name) ? name : undefined;
433
+ };
434
+
411
435
  const collectStaticMembers = (
412
436
  context: ModuleContext,
413
437
  group: GirFunction[],
@@ -416,7 +440,7 @@ const collectStaticMembers = (
416
440
  const members: StaticMember[] = [];
417
441
 
418
442
  for (const callable of group) {
419
- const name = isEmittableCallable(context, callable) ? resolveName(callable.name) : undefined;
443
+ const name = staticMemberName(context, callable, resolveName);
420
444
 
421
445
  if (name !== undefined) {
422
446
  members.push({ callable, name });
@@ -427,13 +451,10 @@ const collectStaticMembers = (
427
451
  };
428
452
 
429
453
  const staticMembers = (context: ModuleContext, callables: Callables): StaticMember[] => [
430
- ...collectStaticMembers(context, callables.constructors, constructorMemberName),
454
+ ...collectStaticMembers(context, callables.constructors, memberName),
431
455
  ...collectStaticMembers(context, callables.functions, memberName),
432
456
  ];
433
457
 
434
- const constructorMemberNames = (context: ModuleContext, constructors: GirFunction[]): string[] =>
435
- collectStaticMembers(context, constructors, constructorMemberName).map((member) => member.name);
436
-
437
458
  const collectStaticEntries = (
438
459
  context: ModuleContext,
439
460
  group: GirFunction[],
@@ -493,11 +514,11 @@ const renderPlainInstanceMethods = (
493
514
  const renderPlainTypeMembers = (
494
515
  options: PlainTypeMembersOptions,
495
516
  ): { members: string[]; claimedNames: Set<string> } => {
496
- const { context, className, callables, hasGtype } = options;
517
+ const { context, className, callables, gtypeExpr } = options;
497
518
  const claimedNames: Set<string> = new Set();
498
519
 
499
520
  const members: string[] = [
500
- ...(hasGtype ? [gtypeMemberDeclaration(context)] : []),
521
+ ...(gtypeExpr === undefined ? [] : [gtypeMemberDeclaration(context), gtypeStaticMember(context)]),
501
522
  ...renderStaticHead(context, callables, className),
502
523
  ...renderPlainInstanceMethods(context, callables.methods, claimedNames),
503
524
  ];
@@ -513,10 +534,8 @@ export {
513
534
  dedupeCallables,
514
535
  generateBindings,
515
536
  matchStaticFinishFunction,
516
- indexMethodsByName,
517
537
  isEmittableCallable,
518
538
  renderStaticSignature,
519
- constructorMemberNames,
520
539
  renderStaticHead,
521
540
  renderPlainTypeMembers,
522
541
  staticMembers,
@@ -11,10 +11,12 @@ import {
11
11
  type InheritedMethods,
12
12
  } from "../../analysis/inheritance.js";
13
13
  import {
14
+ areCallablesAssignable,
14
15
  type ClaimedMembers,
15
16
  claimInterfaceMembers,
16
17
  inheritedMembers,
17
18
  interfaceConflicts,
19
+ omittedKeys,
18
20
  omittedTypeRef,
19
21
  } from "../../analysis/interface-conflicts.js";
20
22
  import { ancestorChain, getParentRef, type ResolvedAncestor } from "../../gir/ancestry.js";
@@ -22,17 +24,17 @@ import { isEmittableEntity } from "../../gir/emittable.js";
22
24
  import { indentMembers } from "../../writer/emit.js";
23
25
  import {
24
26
  type Callables,
25
- constructorMemberNames,
26
27
  dedupeCallables,
27
28
  generateBindings,
28
29
  type InstanceScope,
29
30
  instanceScope,
30
31
  renderClassInstanceMember,
31
32
  renderStaticHead,
33
+ staticMembers,
32
34
  } from "./callables.js";
33
35
  import { renderClassConstructor, renderConstructorPropsInterface } from "./constructor-props.js";
34
36
  import { getDoc } from "./doc-spec.js";
35
- import { gtypeMemberDeclaration, renderSourceGtype } from "./gtype-binding.js";
37
+ import { gtypeMemberDeclaration, gtypeStaticMember, renderSourceGtype } from "./gtype-binding.js";
36
38
  import { methodExportName } from "./method.js";
37
39
  import { renderPropertyDeclarations } from "./properties.js";
38
40
  import { renderResolvedPropertyAccessor, resolveAccessor, type ResolvedAccessor } from "./property-accessor.js";
@@ -58,6 +60,14 @@ type MemberDeclarationsOptions = {
58
60
 
59
61
  type ClassMembers = { members: string[]; accessors: ResolvedAccessor[] };
60
62
 
63
+ type ClassMembersOptions = {
64
+ context: ModuleContext;
65
+ klass: GirClass;
66
+ callables: Callables;
67
+ gtypeExpr: string | undefined;
68
+ hasParent: boolean;
69
+ };
70
+
61
71
  type ImplementedRefOptions = {
62
72
  context: ModuleContext;
63
73
  klass: GirClass;
@@ -91,11 +101,20 @@ const generateClass = (context: ModuleContext, klass: GirClass): void => {
91
101
 
92
102
  generateBindings(context, callables);
93
103
  const parentExpression = resolveParent(context, klass);
94
- const extendsClause = renderExtendsClause(context, parentExpression, callables);
104
+ const extendsClause = renderExtendsClause(context, parentExpression, klass, callables);
95
105
  const implemented = resolveImplementedRefs(context, klass);
96
106
  const typeRefs = implemented.map((ref) => omittedTypeRef(ref.typeRef, ref.conflicts));
97
107
  const implementsClause = typeRefs.length === 0 ? "" : ` implements ${typeRefs.join(", ")}`;
98
- const { members, accessors } = renderClassMembers(context, klass, callables, parentExpression !== undefined);
108
+ const gtypeExpr = renderSourceGtype(context, klass);
109
+
110
+ const { members, accessors } = renderClassMembers({
111
+ context,
112
+ klass,
113
+ callables,
114
+ gtypeExpr,
115
+ hasParent: parentExpression !== undefined,
116
+ });
117
+
99
118
  const body = indentMembers(members);
100
119
  const doc = getDoc(klass);
101
120
  const modifier = klass.isAbstract ? "abstract " : "";
@@ -113,7 +132,7 @@ const generateClass = (context: ModuleContext, klass: GirClass): void => {
113
132
 
114
133
  appendMemberDeclarations({ context, klass, className, accessors, implemented });
115
134
  appendInstallMixins(context, className, implemented);
116
- appendClassRegistrations(context, klass, className);
135
+ appendClassRegistrations(context, klass, className, gtypeExpr);
117
136
  };
118
137
 
119
138
  const appendMemberDeclarations = (options: MemberDeclarationsOptions): void => {
@@ -148,15 +167,14 @@ const appendInterfaceMerge = (
148
167
  });
149
168
  };
150
169
 
151
- const renderClassMembers = (
152
- context: ModuleContext,
153
- klass: GirClass,
154
- callables: Callables,
155
- hasParent: boolean,
156
- ): ClassMembers => {
170
+ const renderClassMembers = (options: ClassMembersOptions): ClassMembers => {
171
+ const { context, klass, callables, gtypeExpr } = options;
157
172
  const className = sanitizeTypeIdentifier(klass.name);
158
- const members: string[] = [gtypeMemberDeclaration(context)];
159
- const constructorBlock = renderClassConstructor(context, klass, className, hasParent);
173
+
174
+ const members: string[] =
175
+ gtypeExpr === undefined ? [] : [gtypeMemberDeclaration(context), gtypeStaticMember(context)];
176
+
177
+ const constructorBlock = renderClassConstructor(context, klass, className, options.hasParent);
160
178
 
161
179
  if (constructorBlock !== undefined) {
162
180
  members.push(constructorBlock);
@@ -189,7 +207,6 @@ const renderClassMembers = (
189
207
  context,
190
208
  property,
191
209
  claimedNames,
192
- methodByName: scope.methodByName,
193
210
  inheritedTypes,
194
211
  inheritedNames,
195
212
  });
@@ -238,9 +255,12 @@ const appendInstallMixins = (context: ModuleContext, className: string, implemen
238
255
  ]);
239
256
  };
240
257
 
241
- const appendClassRegistrations = (context: ModuleContext, klass: GirClass, className: string): void => {
242
- const gtypeExpr = renderSourceGtype(context, klass);
243
-
258
+ const appendClassRegistrations = (
259
+ context: ModuleContext,
260
+ klass: GirClass,
261
+ className: string,
262
+ gtypeExpr: string | undefined,
263
+ ): void => {
244
264
  appendWrapperClassRegistration(context, {
245
265
  className,
246
266
  gtypeExpr,
@@ -336,25 +356,80 @@ const resolveImplementedRefs = (context: ModuleContext, klass: GirClass): Implem
336
356
  return refs;
337
357
  };
338
358
 
359
+ const classCallables = (klass: GirClass): Callables => ({
360
+ constructors: klass.constructors,
361
+ functions: klass.functions,
362
+ methods: klass.methods,
363
+ });
364
+
365
+ const resolveParentClass = (context: ModuleContext, klass: GirClass): ResolvedAncestor | undefined => {
366
+ const parent = getParentRef(klass);
367
+
368
+ if (parent === undefined) {
369
+ return undefined;
370
+ }
371
+
372
+ const resolved = context.library.resolveType(parent.namespaceName ?? context.namespace.name, parent.typeName);
373
+
374
+ return resolved?.kind === "class" ? { klass: resolved.value, namespaceName: resolved.namespace.name } : undefined;
375
+ };
376
+
377
+ const addAncestorStatics = (context: ModuleContext, klass: GirClass, table: Map<string, GirFunction>): void => {
378
+ const members = staticMembers(context, classCallables(klass));
379
+
380
+ for (const member of members) {
381
+ if (!table.has(member.name)) {
382
+ table.set(member.name, member.callable);
383
+ }
384
+ }
385
+ };
386
+
387
+ const inheritedStatics = (context: ModuleContext, klass: GirClass): Map<string, GirFunction> => {
388
+ const table: Map<string, GirFunction> = new Map();
389
+ const parent = resolveParentClass(context, klass);
390
+
391
+ if (parent === undefined) {
392
+ return table;
393
+ }
394
+
395
+ for (const ancestor of ancestorChain(context.library, parent.klass, parent.namespaceName)) {
396
+ addAncestorStatics(context, ancestor.klass, table);
397
+ }
398
+
399
+ return table;
400
+ };
401
+
402
+ const shadowedStaticNames = (context: ModuleContext, klass: GirClass, callables: Callables): string[] => {
403
+ const inherited = inheritedStatics(context, klass);
404
+
405
+ return staticMembers(context, callables)
406
+ .filter((member) => {
407
+ const shadowed = inherited.get(member.name);
408
+
409
+ return shadowed !== undefined && !areCallablesAssignable(context.library, member.callable, shadowed);
410
+ })
411
+ .map((member) => member.name);
412
+ };
413
+
339
414
  const renderExtendsClause = (
340
415
  context: ModuleContext,
341
416
  parentExpression: string | undefined,
417
+ klass: GirClass,
342
418
  callables: Callables,
343
419
  ): string => {
344
420
  if (parentExpression === undefined) {
345
421
  return "";
346
422
  }
347
423
 
348
- const constructorNames = constructorMemberNames(context, callables.constructors);
424
+ const staticNames = shadowedStaticNames(context, klass, callables);
349
425
 
350
- if (constructorNames.length === 0) {
426
+ if (staticNames.length === 0) {
351
427
  return ` extends ${parentExpression}`;
352
428
  }
353
429
 
354
430
  context.addRuntimeTypeImport("StaticBase");
355
- const omitted = constructorNames.map((name) => JSON.stringify(name)).join(" | ");
356
431
 
357
- return ` extends (${parentExpression} as StaticBase<typeof ${parentExpression}, ${omitted}>)`;
432
+ return ` extends (${parentExpression} as StaticBase<typeof ${parentExpression}, ${omittedKeys(staticNames)}>)`;
358
433
  };
359
434
 
360
435
  const resolveParent = (context: ModuleContext, klass: GirClass): string | undefined => {
@@ -7,7 +7,7 @@ import { isEmittableEntity } from "../../gir/emittable.js";
7
7
  import { getDoc } from "./doc-spec.js";
8
8
 
9
9
  const TRUE_VALUES: Set<string> = new Set(["true", "1"]);
10
- const BIGINT_CATEGORIES: Set<PrimitiveCategory> = new Set(["bigint64", "biguint64", "gtype"]);
10
+ const BIGINT_CATEGORIES: Set<PrimitiveCategory> = new Set(["bigint64", "biguint64", "gtype", "pointer"]);
11
11
 
12
12
  const generateConstant = (context: ModuleContext, constant: GirConstant): void => {
13
13
  if (!isEmittableEntity(constant)) {