@gtkx/codegen 1.1.0 → 1.2.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 (149) 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 +1 -3
  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/gir-path.d.ts +1 -1
  44. package/dist/gir/gir-path.js +1 -1
  45. package/dist/gir/gir-path.js.map +1 -1
  46. package/dist/gir/library.d.ts +5 -1
  47. package/dist/gir/library.d.ts.map +1 -1
  48. package/dist/gir/library.js +8 -1
  49. package/dist/gir/library.js.map +1 -1
  50. package/dist/gir/parse.d.ts.map +1 -1
  51. package/dist/gir/parse.js +4 -1
  52. package/dist/gir/parse.js.map +1 -1
  53. package/dist/gir/primitives.js +1 -1
  54. package/dist/gir/primitives.js.map +1 -1
  55. package/dist/khronos/args.d.ts.map +1 -1
  56. package/dist/khronos/args.js +8 -6
  57. package/dist/khronos/args.js.map +1 -1
  58. package/dist/khronos/render.d.ts.map +1 -1
  59. package/dist/khronos/render.js +8 -4
  60. package/dist/khronos/render.js.map +1 -1
  61. package/dist/runner.d.ts +2 -0
  62. package/dist/runner.d.ts.map +1 -1
  63. package/dist/runner.js +5 -3
  64. package/dist/runner.js.map +1 -1
  65. package/dist/staging.d.ts.map +1 -1
  66. package/dist/staging.js +1 -1
  67. package/dist/staging.js.map +1 -1
  68. package/dist/store/compile-store.d.ts +0 -1
  69. package/dist/store/compile-store.d.ts.map +1 -1
  70. package/dist/store/compile-store.js +7 -34
  71. package/dist/store/compile-store.js.map +1 -1
  72. package/dist/store/gi/callables.d.ts +1 -3
  73. package/dist/store/gi/callables.d.ts.map +1 -1
  74. package/dist/store/gi/callables.js +19 -4
  75. package/dist/store/gi/callables.js.map +1 -1
  76. package/dist/store/gi/class.d.ts.map +1 -1
  77. package/dist/store/gi/class.js +48 -9
  78. package/dist/store/gi/class.js.map +1 -1
  79. package/dist/store/gi/constant.js +1 -1
  80. package/dist/store/gi/constant.js.map +1 -1
  81. package/dist/store/gi/enum.d.ts.map +1 -1
  82. package/dist/store/gi/enum.js +2 -5
  83. package/dist/store/gi/enum.js.map +1 -1
  84. package/dist/store/gi/interface.js +2 -2
  85. package/dist/store/gi/interface.js.map +1 -1
  86. package/dist/store/gi/method.d.ts.map +1 -1
  87. package/dist/store/gi/method.js +4 -7
  88. package/dist/store/gi/method.js.map +1 -1
  89. package/dist/store/gi/property-accessor.d.ts +0 -3
  90. package/dist/store/gi/property-accessor.d.ts.map +1 -1
  91. package/dist/store/gi/property-accessor.js +15 -108
  92. package/dist/store/gi/property-accessor.js.map +1 -1
  93. package/dist/store/gi/record-constructor.js +3 -3
  94. package/dist/store/gi/record-constructor.js.map +1 -1
  95. package/dist/store/gi/record-field-accessor.d.ts.map +1 -1
  96. package/dist/store/gi/record-field-accessor.js +15 -3
  97. package/dist/store/gi/record-field-accessor.js.map +1 -1
  98. package/dist/store/gi/return-wrap.js +1 -1
  99. package/dist/store/gi/return-wrap.js.map +1 -1
  100. package/dist/store/gi/signal.d.ts.map +1 -1
  101. package/dist/store/gi/signal.js +25 -4
  102. package/dist/store/gi/signal.js.map +1 -1
  103. package/dist/store/jsx-store.d.ts.map +1 -1
  104. package/dist/store/jsx-store.js +0 -1
  105. package/dist/store/jsx-store.js.map +1 -1
  106. package/dist/store/store-fs.d.ts +0 -1
  107. package/dist/store/store-fs.d.ts.map +1 -1
  108. package/dist/store/store-fs.js +2 -7
  109. package/dist/store/store-fs.js.map +1 -1
  110. package/dist/xml.d.ts +1 -0
  111. package/dist/xml.d.ts.map +1 -1
  112. package/dist/xml.js +28 -1
  113. package/dist/xml.js.map +1 -1
  114. package/overrides/gobject/object.ts.ejs +2 -0
  115. package/package.json +3 -3
  116. package/src/analysis/descriptor-render.ts +19 -10
  117. package/src/analysis/descriptor.ts +43 -9
  118. package/src/analysis/interface-conflicts.ts +6 -8
  119. package/src/analysis/ts-type.ts +30 -11
  120. package/src/analysis/type-shape.ts +5 -2
  121. package/src/compile.ts +87 -6
  122. package/src/docs/api-reference.ts +3 -1
  123. package/src/docs/pipeline.ts +13 -7
  124. package/src/docs/symbol-page.ts +0 -4
  125. package/src/fingerprint.ts +31 -38
  126. package/src/gi.ts +9 -2
  127. package/src/gir/gir-path.ts +1 -1
  128. package/src/gir/library.ts +9 -1
  129. package/src/gir/parse.ts +6 -1
  130. package/src/gir/primitives.ts +1 -1
  131. package/src/khronos/args.ts +9 -6
  132. package/src/khronos/render.ts +8 -6
  133. package/src/runner.ts +7 -3
  134. package/src/staging.ts +1 -3
  135. package/src/store/compile-store.ts +7 -40
  136. package/src/store/gi/callables.ts +26 -7
  137. package/src/store/gi/class.ts +63 -7
  138. package/src/store/gi/constant.ts +1 -1
  139. package/src/store/gi/enum.ts +2 -6
  140. package/src/store/gi/interface.ts +2 -2
  141. package/src/store/gi/method.ts +4 -9
  142. package/src/store/gi/property-accessor.ts +22 -193
  143. package/src/store/gi/record-constructor.ts +3 -3
  144. package/src/store/gi/record-field-accessor.ts +18 -3
  145. package/src/store/gi/return-wrap.ts +1 -1
  146. package/src/store/gi/signal.ts +30 -4
  147. package/src/store/jsx-store.ts +0 -1
  148. package/src/store/store-fs.ts +3 -9
  149. package/src/xml.ts +43 -1
@@ -366,6 +366,21 @@ const visitInlineStructFields = (
366
366
  }
367
367
  };
368
368
 
369
+ const inlineLeafCount = (context: ModuleContext, fields: GirField[]): number => {
370
+ let count = 0;
371
+
372
+ visitInlineStructFields(context, fields, 0, {
373
+ leaf: () => {
374
+ count += 1;
375
+ },
376
+ nested: (_jsName, nested) => {
377
+ count += inlineLeafCount(context, nested);
378
+ },
379
+ });
380
+
381
+ return count;
382
+ };
383
+
369
384
  const elementAccessExpr = (descriptor: string, offset: number): string =>
370
385
  `read(__array, ${descriptor}, __base + ${String(offset)})`;
371
386
 
@@ -452,7 +467,7 @@ const structArrayGetterBlock = (options: StructArrayAccessorOptions): string =>
452
467
  };
453
468
 
454
469
  const structArraySetterStatements = (context: ModuleContext, options: StructArrayAccessorOptions): string => {
455
- const { elementDescriptor, offset, elementSize, elementFields } = options;
470
+ const { elementDescriptor, offset, elementSize, elementFields, lengthExpr } = options;
456
471
  const writes: string[] = [];
457
472
 
458
473
  appendElementWriteStatements(context, {
@@ -468,7 +483,7 @@ const structArraySetterStatements = (context: ModuleContext, options: StructArra
468
483
  `const __descriptor = ${elementDescriptor};`,
469
484
  `const __array = read(getHandle(this), __descriptor, ${String(offset)}) as ReturnType<typeof getHandle>;`,
470
485
  "for (const [__index, __element] of __value.entries()) {",
471
- indent(loop, 1),
486
+ indent(`if (__index >= ${lengthExpr}) {\n break;\n}\n\n${loop}`, 1),
472
487
  "}",
473
488
  `write(getHandle(this), __descriptor, ${String(offset)}, __array);`,
474
489
  ].join("\n");
@@ -543,7 +558,7 @@ const resolveStructArray = (context: ModuleContext, target: StructArrayTarget):
543
558
 
544
559
  const shape = resolveStructArrayShape(context, elements, siblingFields);
545
560
 
546
- if (shape === undefined) {
561
+ if (shape === undefined || inlineLeafCount(context, elements.elementFields) === 0) {
547
562
  return undefined;
548
563
  }
549
564
 
@@ -10,7 +10,7 @@ type WrapReturnOptions = {
10
10
  valueExpression: string;
11
11
  };
12
12
 
13
- const BIGINT_CATEGORIES: Set<PrimitiveCategory> = new Set(["gtype", "bigint64", "biguint64"]);
13
+ const BIGINT_CATEGORIES: Set<PrimitiveCategory> = new Set(["gtype", "bigint64", "biguint64", "pointer"]);
14
14
 
15
15
  const wrapReturnValue = (context: ModuleContext, options: WrapReturnOptions): string => {
16
16
  const { ref, isNullable, valueExpression } = options;
@@ -48,6 +48,21 @@ const SIGNAL_HANDLER_TYPE = "(...args: any[]) => any";
48
48
  const SIGNALS_SUFFIX = "Signals";
49
49
  const SIGNAL_EMIT_SUFFIX = "SignalEmit";
50
50
 
51
+ const DEPRECATED_SIGNAL_ALIASES: Map<string, string> = new Map([
52
+ ["addEventListener", "on"],
53
+ ["removeEventListener", "off"],
54
+ ]);
55
+
56
+ const deprecatedAliasDoc = (method: string): string => {
57
+ const replacement = DEPRECATED_SIGNAL_ALIASES.get(method);
58
+
59
+ if (replacement === undefined) {
60
+ return "";
61
+ }
62
+
63
+ return `/** @deprecated Since 1.2. Use \`${replacement}\` instead. Removed in v2. */\n`;
64
+ };
65
+
51
66
  const renderSignalMembers = (context: ModuleContext, klass: GirClass): string[] => {
52
67
  if (klass.glibGetType === undefined) {
53
68
  return [];
@@ -68,7 +83,7 @@ const renderSignalMembers = (context: ModuleContext, klass: GirClass): string[]
68
83
 
69
84
  const connectDefault = isRootObject
70
85
  ? "default:\n throw new globalThis.Error(\"Unknown signal '\" + signal + \"'\");"
71
- : "default:\n return super.connect(signal, handler, isAfter);";
86
+ : "default:\n return super.connect(signal as never, handler as never, isAfter);";
72
87
 
73
88
  const emitDefault = isRootObject
74
89
  ? "default:\n throw new globalThis.Error(\"Unknown signal '\" + sigName + \"'\");"
@@ -129,7 +144,11 @@ const renderSignalMap = (spec: SignalMapSpec): string => {
129
144
  (signal) => `${signalDoc(signal)}${sourceStringLiteral(signal.name)}: ${renderEntry(context, signal)};`,
130
145
  );
131
146
 
132
- const entries = [...signalEntries, ...renderNotifyDetailEntries(context, klass, suffix)];
147
+ const entries = [
148
+ ...signalEntries,
149
+ ...renderNotifyDetailEntries(context, klass, suffix),
150
+ ...renderCustomNotifyEntry(context, klass, suffix),
151
+ ];
133
152
 
134
153
  return renderBracedOrEmpty(`export interface ${className}${suffix}${extendsClause}`, entries.join("\n"));
135
154
  };
@@ -137,6 +156,14 @@ const renderSignalMap = (spec: SignalMapSpec): string => {
137
156
  const signalDoc = (signal: GirCallable): string =>
138
157
  renderJsDoc(signal.doc, undefined, handlerSpec(signal, signal.parameters));
139
158
 
159
+ const renderCustomNotifyEntry = (context: ModuleContext, klass: GirClass, suffix: string): string[] => {
160
+ if (context.namespace.name !== "GObject" || klass.name !== "Object") {
161
+ return [];
162
+ }
163
+
164
+ return [`[detail: \`notify::\${string}\`]: ${gobjectObjectMapRef(context, suffix)}["notify"];`];
165
+ };
166
+
140
167
  const renderNotifyDetailEntries = (context: ModuleContext, klass: GirClass, suffix: string): string[] => {
141
168
  const notifyValue = `${gobjectObjectMapRef(context, suffix)}["notify"]`;
142
169
 
@@ -191,7 +218,6 @@ const renderSignalConnectInterface = (className: string, isRootObject: boolean):
191
218
  const lines = [
192
219
  `__signals__?: ${map};`,
193
220
  `connect<K extends keyof ${map}>(signal: K, handler: ${map}[K], isAfter?: boolean): number;`,
194
- `connect(signal: string, handler: ${SIGNAL_HANDLER_TYPE}, isAfter?: boolean): number;`,
195
221
  `emit<K extends keyof ${emitMap}>(sigName: K, ...args: ${emitMap}[K]["args"]): ${emitMap}[K]["result"];`,
196
222
  "emit(sigName: string, ...args: unknown[]): unknown;",
197
223
  ];
@@ -200,8 +226,8 @@ const renderSignalConnectInterface = (className: string, isRootObject: boolean):
200
226
  const chainable = (methods: string[], trailing: string): void => {
201
227
  for (const method of methods) {
202
228
  lines.push(
229
+ deprecatedAliasDoc(method) +
203
230
  `${method}<K extends keyof ${map}>(signal: K, handler: ${map}[K]${trailing}): this;`,
204
- `${method}(signal: string, handler: ${SIGNAL_HANDLER_TYPE}${trailing}): this;`,
205
231
  );
206
232
  }
207
233
  };
@@ -30,7 +30,6 @@ const writeJsxStore = (
30
30
  peerDependencies: { "@gtkx/gi": "*", "@gtkx/react": "*", react: "*" },
31
31
  }),
32
32
  rawFiles,
33
- requiresEnvReference: true,
34
33
  });
35
34
  };
36
35
 
@@ -50,7 +50,6 @@ type WriteStoreParams = {
50
50
  files: SourceModule[];
51
51
  manifest: Manifest;
52
52
  rawFiles?: RawFile[];
53
- requiresEnvReference?: boolean;
54
53
  };
55
54
 
56
55
  const STORE_DIR_MODE = 0o755;
@@ -98,18 +97,13 @@ const writeStore = (params: WriteStoreParams): void => {
98
97
  };
99
98
 
100
99
  const buildTempStore = (tmp: string, params: WriteStoreParams): void => {
100
+ writePackageJson(tmp, params.manifest);
101
+
101
102
  for (const file of params.files) {
102
103
  writeSourceFile(tmp, file.fileName, file.source);
103
104
  }
104
105
 
105
- compileStore({
106
- storeDir: tmp,
107
- files: params.files,
108
- packageName: params.manifest.name,
109
- ...(params.requiresEnvReference !== undefined && { requiresEnvReference: params.requiresEnvReference }),
110
- });
111
-
112
- writePackageJson(tmp, params.manifest);
106
+ compileStore({ storeDir: tmp, files: params.files, packageName: params.manifest.name });
113
107
  const rawFiles = params.rawFiles ?? [];
114
108
 
115
109
  for (const raw of rawFiles) {
package/src/xml.ts CHANGED
@@ -7,8 +7,12 @@ type XmlFileInput = {
7
7
  parser: XMLParser;
8
8
  label: string;
9
9
  path: string;
10
+ malformedRemedy?: string;
10
11
  };
11
12
 
13
+ const HIGHEST_CONTROL_CODE = 0x1F;
14
+ const LEGAL_CONTROL_CODES: Set<number> = new Set([0x09, 0x0A, 0x0D]);
15
+
12
16
  const createXmlParser = (options: Partial<X2jOptions>): XMLParser =>
13
17
  new XMLParser({
14
18
  ignoreAttributes: false,
@@ -46,13 +50,51 @@ const positionSuffix = (error: unknown): string => {
46
50
  return ` (line ${String(line)}, column ${String(col)})`;
47
51
  };
48
52
 
53
+ const isIllegalControlCode = (code: number): boolean =>
54
+ code <= HIGHEST_CONTROL_CODE && !LEGAL_CONTROL_CODES.has(code);
55
+
56
+ const escapedCode = (code: number): string => `[U+${code.toString(16).toUpperCase().padStart(4, "0")}]`;
57
+
58
+ const escapeControlCodes = (text: string): string => {
59
+ let escaped = "";
60
+
61
+ for (const character of text) {
62
+ const code = character.codePointAt(0) ?? 0;
63
+ escaped += isIllegalControlCode(code) ? escapedCode(code) : character;
64
+ }
65
+
66
+ return escaped;
67
+ };
68
+
69
+ const offendingLine = (xml: string, error: unknown): string => {
70
+ const line = isRecord(error) ? error.line : undefined;
71
+
72
+ if (typeof line !== "number") {
73
+ return "";
74
+ }
75
+
76
+ const text = xml.split("\n")[line - 1];
77
+
78
+ if (text === undefined) {
79
+ return "";
80
+ }
81
+
82
+ return `\n ${escapeControlCodes(text.replace(/^[ \t]+/, "").replace(/\r$/, ""))}`;
83
+ };
84
+
85
+ const remedySuffix = (input: XmlFileInput, xml: string, error: unknown): string => {
86
+ const remedy = input.malformedRemedy;
87
+
88
+ return remedy === undefined ? "" : `${offendingLine(xml, error)}\n${remedy}`;
89
+ };
90
+
49
91
  const assertWellFormed = (input: XmlFileInput, xml: string): void => {
50
92
  try {
51
93
  SyntaxValidator.validate(xml);
52
94
  } catch (error) {
53
95
  throw new Error(
54
96
  `The ${input.label} at ${input.path} is not well-formed XML: ` +
55
- `${errorMessage(error)}${positionSuffix(error)}`,
97
+ `${errorMessage(error)}${positionSuffix(error)}${remedySuffix(input, xml, error)}`,
56
98
  { cause: error },
57
99
  );
58
100
  }