@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
@@ -87,6 +87,11 @@ type FundamentalOptions = {
87
87
 
88
88
  type ListDescriptorName = "list" | "slist" | "ptrArray" | "gArray";
89
89
 
90
+ type ArrayLayout = {
91
+ elementSize?: number | undefined;
92
+ isBytes: boolean;
93
+ };
94
+
90
95
  type BindArgs = {
91
96
  libExpr: string;
92
97
  symbolExpr: string;
@@ -156,6 +161,7 @@ const tUint8: string = T.uint8;
156
161
  const tUint32: string = T.uint32;
157
162
  const tInt32: string = T.int32;
158
163
  const tUint64: string = T.uint64;
164
+ const tBiguint64: string = T.biguint64;
159
165
  const tGtype: string = T.gtype;
160
166
  const tBuffer: string = T.buffer;
161
167
 
@@ -229,44 +235,69 @@ const tFlags = (lib: string, typeFnName: string, isSigned: boolean): string =>
229
235
 
230
236
  const tByteArray = (ownership: Ownership): string => call("byteArray", [sourceStringLiteral(ownership)]);
231
237
 
238
+ const tNumericByteArray = (ownership: Ownership): string =>
239
+ call("array", [
240
+ tUint8,
241
+ sourceStringLiteral("gbytearray"),
242
+ sourceStringLiteral(ownership),
243
+ ]);
244
+
232
245
  const tList = (name: ListDescriptorName, element: string, ownership: Ownership): string =>
233
246
  call(name, [element, sourceStringLiteral(ownership)]);
234
247
 
235
- const tArray = (element: string, ownership?: Ownership, elementSize?: number): string =>
248
+ const arrayLayoutArg = (ownership: Ownership | undefined, layout: ArrayLayout): string | undefined => {
249
+ if (ownership === undefined) {
250
+ return undefined;
251
+ }
252
+
253
+ const entries = [
254
+ layout.elementSize === undefined ? undefined : `elementSize: ${String(layout.elementSize)}`,
255
+ layout.isBytes ? "isBytes: true" : undefined,
256
+ ].filter((entry): entry is string => entry !== undefined);
257
+
258
+ return entries.length === 0 ? undefined : `{ ${entries.join(", ")} }`;
259
+ };
260
+
261
+ const tArray = (element: string, ownership: Ownership | undefined, layout: ArrayLayout): string =>
236
262
  call("array", [
237
263
  element,
238
264
  ownership === undefined ? undefined : sourceStringLiteral("array"),
239
265
  ownership === undefined ? undefined : sourceStringLiteral(ownership),
240
- elementSize === undefined ? undefined : `{ elementSize: ${String(elementSize)} }`,
266
+ arrayLayoutArg(ownership, layout),
241
267
  ]);
242
268
 
243
269
  const tSizedArray = (
244
270
  element: string,
245
271
  lengthIndex: number,
246
- ownership?: Ownership,
247
- elementSize?: number,
272
+ ownership: Ownership | undefined,
273
+ layout: ArrayLayout,
248
274
  ): string =>
249
275
  call("sizedArray", [
250
276
  element,
251
277
  String(lengthIndex),
252
278
  ownership === undefined ? undefined : sourceStringLiteral(ownership),
253
- elementSize === undefined ? undefined : String(elementSize),
279
+ arrayLayoutArg(ownership, layout),
254
280
  ]);
255
281
 
256
- const tCursorArray = (element: string, bounds: GirCursorBounds, ownership?: Ownership, elementSize?: number): string =>
282
+ const tCursorArray = (
283
+ element: string,
284
+ bounds: GirCursorBounds,
285
+ ownership: Ownership | undefined,
286
+ layout: ArrayLayout,
287
+ ): string =>
257
288
  call("cursorArray", [
258
289
  element,
259
290
  `{ baseParamIndex: ${String(bounds.baseIndex)}, sizeParamIndex: ${String(bounds.lengthIndex)} }`,
260
291
  ownership === undefined ? undefined : sourceStringLiteral(ownership),
261
- elementSize === undefined ? undefined : String(elementSize),
292
+ arrayLayoutArg(ownership, layout),
262
293
  ]);
263
294
 
264
- const tFixedArray = (element: string, length: number, ownership?: Ownership, elementSize?: number): string =>
295
+ const tFixedArray = (element: string, length: number, ownership: Ownership | undefined, layout: ArrayLayout): string =>
265
296
  call("fixedArray", [
266
297
  element,
267
298
  String(length),
268
299
  ownership === undefined ? undefined : sourceStringLiteral(ownership),
269
- elementSize === undefined ? undefined : String(elementSize),
300
+ arrayLayoutArg(ownership, layout),
270
301
  ]);
271
302
 
272
303
  const tCallback = (spec: CallbackSpecParts): string => {
@@ -296,6 +327,7 @@ export {
296
327
  tUint32,
297
328
  tInt32,
298
329
  tUint64,
330
+ tBiguint64,
299
331
  tGtype,
300
332
  tBuffer,
301
333
  tScalar,
@@ -310,6 +342,7 @@ export {
310
342
  tEnum,
311
343
  tFlags,
312
344
  tByteArray,
345
+ tNumericByteArray,
313
346
  tList,
314
347
  tArray,
315
348
  tSizedArray,
@@ -318,6 +351,7 @@ export {
318
351
  tCallback,
319
352
  tBind,
320
353
  tFn,
354
+ type ArrayLayout,
321
355
  type Ownership,
322
356
  type ScalarDescriptorName,
323
357
  type ListDescriptorName,
@@ -217,21 +217,19 @@ const prerequisiteConflicts = (library: Library, iface: GirClass, base: Resolved
217
217
  return names;
218
218
  };
219
219
 
220
- const omittedTypeRef = (typeRef: string, omissions: string[]): string => {
221
- if (omissions.length === 0) {
222
- return typeRef;
223
- }
224
-
225
- const keys = [...new Set(omissions)].map((name) => JSON.stringify(name)).join(" | ");
220
+ const omittedKeys = (omissions: string[]): string =>
221
+ [...new Set(omissions)].map((name) => JSON.stringify(name)).join(" | ");
226
222
 
227
- return `Omit<${typeRef}, ${keys}>`;
228
- };
223
+ const omittedTypeRef = (typeRef: string, omissions: string[]): string =>
224
+ omissions.length === 0 ? typeRef : `Omit<${typeRef}, ${omittedKeys(omissions)}>`;
229
225
 
230
226
  export {
227
+ areCallablesAssignable,
231
228
  type ClaimedMembers,
232
229
  claimInterfaceMembers,
233
230
  inheritedMembers,
234
231
  interfaceConflicts,
232
+ omittedKeys,
235
233
  omittedTypeRef,
236
234
  prerequisiteConflicts,
237
235
  };
@@ -12,6 +12,7 @@ import {
12
12
  type TypeId,
13
13
  } from "../gir/type-id.js";
14
14
  import { gtypeTsType } from "../store/gi/gtype-binding.js";
15
+ import { isByteSequence } from "./type-shape.js";
15
16
 
16
17
  type ReferenceName = {
17
18
  namespaceName: string;
@@ -21,11 +22,15 @@ type ReferenceName = {
21
22
  type TsTypeTarget = {
22
23
  containerStyle: "map" | "record";
23
24
  callbackType: string;
24
- shouldRenderByteArrayAsNumber: boolean;
25
+ byteArrayType: string;
25
26
  renderNamed: (resolved: GirType | undefined, name: ReferenceName) => string;
26
27
  renderGtype: () => string;
27
28
  };
28
29
 
30
+ const BYTE_ARRAY_TYPE = "Uint8Array";
31
+ const BYTE_ARRAY_INPUT_TYPE = "Uint8Array | number[]";
32
+ const NUMBER_ARRAY_TYPE = "number[]";
33
+
29
34
  const willEmitEntity = (type: EntityType): boolean => isEmittableEntity(type.value);
30
35
 
31
36
  const referenceName = (library: Library, ref: TypeId): ReferenceName | undefined => {
@@ -97,14 +102,14 @@ const renderContainerType = (
97
102
  };
98
103
 
99
104
  const renderSequenceType = (library: Library, target: TsTypeTarget, type: CArrayType | ListType): string => {
100
- if (type.kind === "list" && type.flavor === "gbytearray" && target.shouldRenderByteArrayAsNumber) {
101
- return "number[]";
102
- }
103
-
104
105
  if (type.kind === "carray" && hasUnknownArrayLength(type)) {
105
106
  return "number";
106
107
  }
107
108
 
109
+ if (isByteSequence(library, type)) {
110
+ return target.byteArrayType;
111
+ }
112
+
108
113
  return `${renderBaseType(library, target, type.element)}[]`;
109
114
  };
110
115
 
@@ -120,20 +125,34 @@ const renderNamedType = (
120
125
  return target.renderNamed(resolved, name);
121
126
  };
122
127
 
123
- const moduleTarget = (context: ModuleContext): TsTypeTarget => ({
128
+ const getByteArrayType = (library: Library): string =>
129
+ library.isByteArrayTyped ? BYTE_ARRAY_TYPE : NUMBER_ARRAY_TYPE;
130
+
131
+ const moduleTarget = (context: ModuleContext, byteArrayType: string): TsTypeTarget => ({
124
132
  containerStyle: "map",
125
133
  callbackType: "((...args: any[]) => any)",
126
- shouldRenderByteArrayAsNumber: true,
134
+ byteArrayType,
127
135
  renderNamed: (_resolved, name) => context.qualify(name.namespaceName, name.typeName),
128
136
  renderGtype: () => gtypeTsType(context),
129
137
  });
130
138
 
131
- const renderTsType = (context: ModuleContext, ref: TypeId | undefined, isNullable = false): string => {
132
- const base = renderBaseType(context.library, moduleTarget(context), ref);
139
+ const renderModuleType = (
140
+ context: ModuleContext,
141
+ ref: TypeId | undefined,
142
+ isNullable: boolean,
143
+ byteArrayType: string,
144
+ ): string => {
145
+ const base = renderBaseType(context.library, moduleTarget(context, byteArrayType), ref);
133
146
 
134
147
  return isNullable ? `${base} | null` : base;
135
148
  };
136
149
 
150
+ const renderTsType = (context: ModuleContext, ref: TypeId | undefined, isNullable = false): string =>
151
+ renderModuleType(context, ref, isNullable, getByteArrayType(context.library));
152
+
153
+ const renderParameterTsType = (context: ModuleContext, ref: TypeId | undefined, isNullable = false): string =>
154
+ renderModuleType(context, ref, isNullable, BYTE_ARRAY_INPUT_TYPE);
155
+
137
156
  const recordTypeTarget = (
138
157
  library: Library,
139
158
  renderNamedRef: (name: ReferenceName) => string,
@@ -142,7 +161,7 @@ const recordTypeTarget = (
142
161
  const target: TsTypeTarget = {
143
162
  containerStyle: "record",
144
163
  callbackType: "((...args: unknown[]) => unknown)",
145
- shouldRenderByteArrayAsNumber: false,
164
+ byteArrayType: getByteArrayType(library),
146
165
  renderNamed: (resolved, name) => {
147
166
  if (resolved?.kind === "alias") {
148
167
  return resolved.value.target === undefined
@@ -158,4 +177,4 @@ const recordTypeTarget = (
158
177
  return target;
159
178
  };
160
179
 
161
- export { renderBaseType, renderTsType, recordTypeTarget, type TsTypeTarget };
180
+ export { renderBaseType, renderParameterTsType, renderTsType, recordTypeTarget, type TsTypeTarget };
@@ -1,6 +1,6 @@
1
1
  import type { Library } from "../gir/library.js";
2
2
  import type { PrimitiveCategory } from "../gir/primitives.js";
3
- import type { CArrayType, TypeId } from "../gir/type-id.js";
3
+ import type { CArrayType, ListType, TypeId } from "../gir/type-id.js";
4
4
  import type { GirType } from "../gir/type.js";
5
5
 
6
6
  const resolvedTypeFor = (library: Library, ref: TypeId | undefined): GirType | undefined =>
@@ -21,4 +21,7 @@ const primitiveCategoryFor = (library: Library, ref: TypeId | undefined): Primit
21
21
  const isUnboundedArray = (type: CArrayType): boolean =>
22
22
  type.isZeroTerminated && type.lengthParameterIndex === undefined && type.fixedSize === undefined;
23
23
 
24
- export { carrayFor, isUnboundedArray, primitiveCategoryFor };
24
+ const isByteSequence = (library: Library, type: CArrayType | ListType): boolean =>
25
+ type.kind === "list" ? type.flavor === "gbytearray" : primitiveCategoryFor(library, type.element) === "uint8";
26
+
27
+ export { carrayFor, isByteSequence, isUnboundedArray, primitiveCategoryFor };
package/src/compile.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { errorMessage, normalizeError } from "@gtkx/utils";
1
+ import { errorCode, errorMessage, normalizeError } from "@gtkx/utils";
2
2
  import { existsSync, mkdirSync, renameSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
3
3
  import { dirname, isAbsolute, join, relative } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
@@ -14,10 +14,19 @@ type SourceModule = ProjectFile & {
14
14
  source: string;
15
15
  };
16
16
 
17
- type CompileProjectParams = {
17
+ type ProjectContext = {
18
18
  projectDir: string;
19
19
  files: ProjectFile[];
20
+ label: string;
21
+ };
22
+
23
+ type CompileProjectParams = ProjectContext & {
20
24
  compilerOptions: Record<string, unknown>;
25
+ };
26
+
27
+ type EmitModulesParams = {
28
+ projectDir: string;
29
+ files: SourceModule[];
21
30
  label: string;
22
31
  };
23
32
 
@@ -60,9 +69,33 @@ const CHECK_OPTIONS = {
60
69
  noEmit: true,
61
70
  };
62
71
 
72
+ const DECLARATION_EMIT = {
73
+ declaration: true,
74
+ isolatedDeclarations: true,
75
+ removeComments: false,
76
+ };
77
+
78
+ const MODULE_EMIT = {
79
+ removeComments: false,
80
+ };
81
+
82
+ const DECLARATION_OPTIONS = ts.convertCompilerOptionsFromJson(
83
+ { ...BASE_COMPILER_OPTIONS, ...DECLARATION_EMIT },
84
+ ".",
85
+ ).options;
86
+
87
+ const MODULE_OPTIONS = ts.convertCompilerOptionsFromJson(
88
+ { ...BASE_COMPILER_OPTIONS, ...MODULE_EMIT },
89
+ ".",
90
+ ).options;
91
+
92
+ const TS_EXTENSION_PATTERN = /\.tsx?$/;
93
+ const DECLARATION_EXTENSION = ".d.ts";
94
+ const JAVASCRIPT_EXTENSION = ".js";
63
95
  const CHECK_DIR = ".gtkx-check";
64
96
  const FAILED_CHECK_DIR = `${CHECK_DIR}.failed`;
65
97
  const LEGACY_CHECK_PREFIX = `${CHECK_DIR}-`;
98
+ const ESM_SCOPE_MANIFEST = `${JSON.stringify({ type: "module" }, null, 4)}\n`;
66
99
 
67
100
  const FORMAT_HOST: ts.FormatDiagnosticsHost = {
68
101
  getCanonicalFileName: (fileName) => fileName,
@@ -90,6 +123,16 @@ const linkToolingModules = (projectDir: string): (() => void) => {
90
123
  };
91
124
  };
92
125
 
126
+ const ensureModuleScope = (projectDir: string): void => {
127
+ try {
128
+ writeFileSync(join(projectDir, "package.json"), ESM_SCOPE_MANIFEST, { flag: "wx" });
129
+ } catch (error) {
130
+ if (errorCode(error) !== "EEXIST") {
131
+ throw error;
132
+ }
133
+ }
134
+ };
135
+
93
136
  const isProjectFile = (rel: string): boolean =>
94
137
  !rel.startsWith("..") && !isAbsolute(rel) && !rel.split(/[/\\]/).includes("node_modules");
95
138
 
@@ -160,17 +203,17 @@ const originLines = (diagnosed: DiagnosedFile[], files: ProjectFile[]): string[]
160
203
  ([origin, generated]) => `Generated from ${origin}: ${[...generated].join(", ")}`,
161
204
  );
162
205
 
163
- const diagnosticError = (params: CompileProjectParams, diagnostics: ts.Diagnostic[]): Error => {
206
+ const diagnosticError = (params: ProjectContext, diagnostics: ts.Diagnostic[]): Error => {
164
207
  const diagnosed = projectDiagnosticFiles(diagnostics, params.projectDir);
165
208
  const { label } = params;
166
209
 
167
210
  if (diagnosed.length === 0) {
168
- return new Error(`Type checking ${label} failed:\n${ts.formatDiagnostics(diagnostics, FORMAT_HOST).trim()}`);
211
+ return new Error(`Compiling ${label} failed:\n${ts.formatDiagnostics(diagnostics, FORMAT_HOST).trim()}`);
169
212
  }
170
213
 
171
214
  const lines = [...diagnosed.map((entry) => entry.text), ...originLines(diagnosed, params.files)];
172
215
 
173
- return new Error(`Type checking ${label} found ${String(diagnosed.length)} error(s):\n${lines.join("\n")}`);
216
+ return new Error(`Compiling ${label} found ${String(diagnosed.length)} error(s):\n${lines.join("\n")}`);
174
217
  };
175
218
 
176
219
  const keepFailedProject = (input: FailedProjectInput): Error => {
@@ -214,7 +257,45 @@ const runProgram = (params: CompileProjectParams): ts.Diagnostic[] => {
214
257
  return [...program.emit().diagnostics];
215
258
  };
216
259
 
260
+ const replaceExtension = (fileName: string, extension: string): string =>
261
+ `${fileName.replace(TS_EXTENSION_PATTERN, "")}${extension}`;
262
+
263
+ const transpileOptions = (compilerOptions: ts.CompilerOptions, fileName: string): ts.TranspileOptions => ({
264
+ compilerOptions,
265
+ fileName,
266
+ reportDiagnostics: true,
267
+ });
268
+
269
+ const emitModule = (module: SourceModule, projectDir: string): ts.Diagnostic[] => {
270
+ const fileName = join(projectDir, module.fileName);
271
+ const declaration = ts.transpileDeclaration(module.source, transpileOptions(DECLARATION_OPTIONS, fileName));
272
+ const javascript = ts.transpileModule(module.source, transpileOptions(MODULE_OPTIONS, fileName));
273
+ const diagnostics = [...declaration.diagnostics ?? [], ...javascript.diagnostics ?? []];
274
+
275
+ if (diagnostics.length > 0) {
276
+ return diagnostics;
277
+ }
278
+
279
+ writeFileSync(replaceExtension(fileName, DECLARATION_EXTENSION), declaration.outputText);
280
+ writeFileSync(replaceExtension(fileName, JAVASCRIPT_EXTENSION), javascript.outputText);
281
+
282
+ return [];
283
+ };
284
+
285
+ const emitModules = (params: EmitModulesParams): void => {
286
+ const diagnostics: ts.Diagnostic[] = [];
287
+
288
+ for (const module of params.files) {
289
+ diagnostics.push(...emitModule(module, params.projectDir));
290
+ }
291
+
292
+ if (diagnostics.length > 0) {
293
+ throw diagnosticError(params, diagnostics);
294
+ }
295
+ };
296
+
217
297
  const compileProject = (params: CompileProjectParams): void => {
298
+ ensureModuleScope(params.projectDir);
218
299
  const unlinkToolingModules = linkToolingModules(params.projectDir);
219
300
 
220
301
  try {
@@ -261,4 +342,4 @@ const checkModules = (params: { modules: SourceModule[]; resolveFrom: string; la
261
342
  rmSync(keepAt, { recursive: true, force: true });
262
343
  };
263
344
 
264
- export { compileProject, checkModules, keepFailedProject, type ProjectFile, type SourceModule };
345
+ export { checkModules, emitModules, keepFailedProject, type SourceModule };
@@ -24,6 +24,8 @@ type ApiReferenceOptions = {
24
24
  props?: ElementProps;
25
25
  /** GObject properties the project omits from generated props; without it pages show props that do not exist. */
26
26
  omittedProps?: OmittedProps;
27
+ /** Describes GIR byte sequences as `Uint8Array` rather than `number[]`; defaults to false. */
28
+ isByteArrayTyped?: boolean;
27
29
  };
28
30
 
29
31
  /** Narrows an `ApiReference.symbols` enumeration. */
@@ -371,7 +373,7 @@ class ApiReference {
371
373
  this.libraries = options.libraries;
372
374
  this.props = options.props ?? {};
373
375
  this.omittedProps = options.omittedProps ?? {};
374
- this.library = Library.load(options.libraries, options.girPath);
376
+ this.library = Library.load(options.libraries, options.girPath, options.isByteArrayTyped === true);
375
377
  this.elementContext = { library: this.library, linkFor: (): string | undefined => undefined };
376
378
  this.buildIndex();
377
379
  }
@@ -5,6 +5,7 @@ import {
5
5
  computeDocsFingerprint,
6
6
  type DocsFingerprintInput,
7
7
  FINGERPRINT_FILENAME,
8
+ type GiInputs,
8
9
  isDocsOutputFresh,
9
10
  } from "../fingerprint.js";
10
11
  import { Library } from "../gir/library.js";
@@ -37,6 +38,7 @@ type DocsOptions = {
37
38
  props?: ElementProps;
38
39
  omittedProps?: OmittedProps;
39
40
  isForced?: boolean;
41
+ isByteArrayTyped?: boolean;
40
42
  };
41
43
 
42
44
  type DocsManifest = {
@@ -253,6 +255,13 @@ const outDirRefusal = (outDir: string, reason: string): Error =>
253
255
  "empty directory or at one gtkx generated, or remove it yourself first.",
254
256
  );
255
257
 
258
+ const giInputs = (options: DocsOptions, girFiles: string[]): GiInputs => ({
259
+ girFiles,
260
+ libraries: options.libraries,
261
+ girPath: options.girPath,
262
+ isByteArrayTyped: options.isByteArrayTyped === true,
263
+ });
264
+
256
265
  const assertOwnedOutDir = (options: DocsOptions, manifest: DocsManifest | undefined): void => {
257
266
  const stats = statSync(options.outDir, { throwIfNoEntry: false });
258
267
 
@@ -301,7 +310,7 @@ const cachedDocsResult = (
301
310
  return undefined;
302
311
  }
303
312
 
304
- if (!isDocsOutputFresh(options.outDir, options.libraries, options.girPath, input)) {
313
+ if (!isDocsOutputFresh(options.outDir, giInputs(options, []), input)) {
305
314
  return undefined;
306
315
  }
307
316
 
@@ -335,18 +344,15 @@ const writeDocs = (options: DocsOptions): DocsResult => {
335
344
  }
336
345
 
337
346
  assertOwnedOutDir(options, previous);
338
- const library = Library.load(options.libraries, options.girPath);
347
+ const library = Library.load(options.libraries, options.girPath, options.isByteArrayTyped === true);
339
348
  const { pages, namespaces } = generatePages(options, input.basePath, library);
340
349
  clearOutDir(options, previous);
341
350
  mkdirSync(options.outDir, { recursive: true });
342
351
  const manifest: DocsManifest = { generator: MANIFEST_GENERATOR, namespaces };
343
352
  writeFileSync(manifestPath, JSON.stringify(manifest));
344
353
  writePages(options.outDir, pages);
345
-
346
- writeFileSync(
347
- join(options.outDir, FINGERPRINT_FILENAME),
348
- JSON.stringify(computeDocsFingerprint(library.girFiles, options.libraries, options.girPath, input)),
349
- );
354
+ const fingerprint = computeDocsFingerprint(giInputs(options, library.girFiles), input);
355
+ writeFileSync(join(options.outDir, FINGERPRINT_FILENAME), JSON.stringify(fingerprint));
350
356
 
351
357
  return { isRegenerated: true, namespaces };
352
358
  };
@@ -16,7 +16,6 @@ import { PRIMITIVE_TS_TYPE, primitiveCategory } from "../gir/primitives.js";
16
16
  import { callableSpec } from "../store/gi/callable-doc.js";
17
17
  import {
18
18
  dedupeCallables,
19
- indexMethodsByName,
20
19
  instanceScope,
21
20
  matchStaticFinishFunction,
22
21
  renderInstanceMethodSignature,
@@ -146,7 +145,6 @@ type MemberOwner = {
146
145
  type PropertyAccessorSetup = {
147
146
  context: ModuleContext;
148
147
  claimedNames: Set<string>;
149
- methodByName: Map<string, GirFunction>;
150
148
  };
151
149
 
152
150
  type ResolvedRecordField = NonNullable<ReturnType<typeof resolveRecordFieldEntry>>;
@@ -391,7 +389,6 @@ const propertyAccessorSetup = (
391
389
  return {
392
390
  context: docsSignatureContext(owner.namespace, library),
393
391
  claimedNames,
394
- methodByName: indexMethodsByName(dedupeCallables(owner.klass.methods)),
395
392
  };
396
393
  };
397
394
 
@@ -418,7 +415,6 @@ const ownerPropertyEntries = (owner: MemberOwner, setup: PropertyAccessorSetup,
418
415
  context: setup.context,
419
416
  property,
420
417
  claimedNames: setup.claimedNames,
421
- methodByName: setup.methodByName,
422
418
  });
423
419
 
424
420
  if (accessor === undefined || seen.has(accessor.jsName)) {
@@ -637,7 +633,7 @@ const fieldsSection = (record: GirRecord, context: ModuleContext, claimedNames:
637
633
  const entries: MetaDocEntry[] = [];
638
634
 
639
635
  for (const slot of slots) {
640
- const field = resolveRecordFieldEntry(context, slot, claimedNames);
636
+ const field = resolveRecordFieldEntry(context, slot, claimedNames, record.fields);
641
637
 
642
638
  if (field === undefined) {
643
639
  continue;
@@ -7,6 +7,13 @@ import packageManifest from "../package.json" with { type: "json" };
7
7
  import { arrayGuard, hasFields, isNumber, isString, optionalGuard } from "./guards.js";
8
8
  import { readJsonFile } from "./json.js";
9
9
 
10
+ type GiInputs = {
11
+ girFiles: string[];
12
+ libraries: string[];
13
+ girPath: string[];
14
+ isByteArrayTyped: boolean;
15
+ };
16
+
10
17
  type GiFingerprint = {
11
18
  value: string;
12
19
  girFiles: string[];
@@ -56,15 +63,17 @@ const overrideFiles = (): string[] => {
56
63
  .map((entry: Dirent) => join(entry.parentPath, entry.name));
57
64
  };
58
65
 
59
- const hashGi = (girFiles: string[], libraries: string[], girPath: string[]): string => {
66
+ const hashGi = (inputs: GiInputs): string => {
60
67
  const hash = createHash("sha256");
61
68
  hash.update(CODEGEN_VERSION);
62
69
  hash.update("\n");
63
- hash.update(sortAlpha(libraries));
70
+ hash.update(sortAlpha(inputs.libraries));
71
+ hash.update("\n");
72
+ hash.update(sortAlpha(inputs.girPath));
64
73
  hash.update("\n");
65
- hash.update(sortAlpha(girPath));
74
+ hash.update(String(inputs.isByteArrayTyped));
66
75
  hash.update("\n");
67
- const hashedFiles = sortStrings([...girFiles, ...overrideFiles()]);
76
+ const hashedFiles = sortStrings([...inputs.girFiles, ...overrideFiles()]);
68
77
 
69
78
  for (const file of hashedFiles) {
70
79
  hash.update("\n");
@@ -76,15 +85,11 @@ const hashGi = (girFiles: string[], libraries: string[], girPath: string[]): str
76
85
  return hash.digest("hex");
77
86
  };
78
87
 
79
- const computeGiFingerprint = (
80
- girFiles: string[],
81
- libraries: string[],
82
- girPath: string[],
83
- ): GiFingerprint => ({
84
- value: hashGi(girFiles, libraries, girPath),
85
- girFiles,
86
- libraries,
87
- girPath,
88
+ const computeGiFingerprint = (inputs: GiInputs): GiFingerprint => ({
89
+ value: hashGi(inputs),
90
+ girFiles: inputs.girFiles,
91
+ libraries: inputs.libraries,
92
+ girPath: inputs.girPath,
88
93
  });
89
94
 
90
95
  const readSentinel = (storeDir: string): unknown => readJsonFile(join(storeDir, FINGERPRINT_FILENAME));
@@ -103,30 +108,27 @@ const isDocsFingerprint = (value: unknown): value is DocsFingerprint =>
103
108
  const isJsxFingerprint = (value: unknown): value is JsxFingerprint =>
104
109
  hasFields<JsxFingerprint>(value, { value: isString, intrinsicElementCount: isNumber });
105
110
 
106
- const recordedGiValue = (sentinel: GiFingerprint, libraries: string[], girPath: string[]): string | undefined => {
107
- if (!hasMatchingRecordedInputs(sentinel, libraries, girPath)) {
111
+ const recordedGiValue = (sentinel: GiFingerprint, inputs: GiInputs): string | undefined => {
112
+ if (!hasMatchingRecordedInputs(sentinel, inputs)) {
108
113
  return undefined;
109
114
  }
110
115
 
111
116
  try {
112
- return hashGi(sentinel.girFiles, sentinel.libraries, girPath);
117
+ return hashGi({ ...inputs, girFiles: sentinel.girFiles, libraries: sentinel.libraries });
113
118
  } catch {
114
119
  return undefined;
115
120
  }
116
121
  };
117
122
 
118
- const isGiStoreFresh = (
119
- giStoreDir: string,
120
- libraries: string[],
121
- girPath: string[],
122
- ): boolean => {
123
+ const isGiStoreFresh = (giStoreDir: string, inputs: GiInputs): boolean => {
123
124
  const sentinel = readSentinel(giStoreDir);
124
125
 
125
- return isGiFingerprint(sentinel) && recordedGiValue(sentinel, libraries, girPath) === sentinel.value;
126
+ return isGiFingerprint(sentinel) && recordedGiValue(sentinel, inputs) === sentinel.value;
126
127
  };
127
128
 
128
- const hasMatchingRecordedInputs = (sentinel: GiFingerprint, libraries: string[], girPath: string[]): boolean =>
129
- sortAlpha(sentinel.libraries) === sortAlpha(libraries) && sortAlpha(sentinel.girPath ?? []) === sortAlpha(girPath);
129
+ const hasMatchingRecordedInputs = (sentinel: GiFingerprint, inputs: GiInputs): boolean =>
130
+ sortAlpha(sentinel.libraries) === sortAlpha(inputs.libraries) &&
131
+ sortAlpha(sentinel.girPath ?? []) === sortAlpha(inputs.girPath);
130
132
 
131
133
  const hashDocs = (giValue: string, input: DocsFingerprintInput): string =>
132
134
  createHash("sha256")
@@ -140,30 +142,20 @@ const hashDocs = (giValue: string, input: DocsFingerprintInput): string =>
140
142
  )
141
143
  .digest("hex");
142
144
 
143
- const computeDocsFingerprint = (
144
- girFiles: string[],
145
- libraries: string[],
146
- girPath: string[],
147
- input: DocsFingerprintInput,
148
- ): DocsFingerprint => {
149
- const gi = computeGiFingerprint(girFiles, libraries, girPath);
145
+ const computeDocsFingerprint = (inputs: GiInputs, input: DocsFingerprintInput): DocsFingerprint => {
146
+ const gi = computeGiFingerprint(inputs);
150
147
 
151
148
  return { value: hashDocs(gi.value, input), gi };
152
149
  };
153
150
 
154
- const isDocsOutputFresh = (
155
- outDir: string,
156
- libraries: string[],
157
- girPath: string[],
158
- input: DocsFingerprintInput,
159
- ): boolean => {
151
+ const isDocsOutputFresh = (outDir: string, inputs: GiInputs, input: DocsFingerprintInput): boolean => {
160
152
  const sentinel = readSentinel(outDir);
161
153
 
162
154
  if (!isDocsFingerprint(sentinel)) {
163
155
  return false;
164
156
  }
165
157
 
166
- const giValue = recordedGiValue(sentinel.gi, libraries, girPath);
158
+ const giValue = recordedGiValue(sentinel.gi, inputs);
167
159
 
168
160
  return giValue !== undefined && hashDocs(giValue, input) === sentinel.value;
169
161
  };
@@ -217,6 +209,7 @@ export {
217
209
  computeJsxFingerprint,
218
210
  jsxStoreFreshness,
219
211
  type DocsFingerprintInput,
212
+ type GiInputs,
220
213
  type GiFingerprint,
221
214
  type JsxFingerprintInput,
222
215
  };