@ts-for-gir/lib 4.0.0-rc.9 → 4.0.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.
- package/package.json +59 -59
- package/src/gir/function.ts +2 -2
- package/src/gir/introspected-classes.ts +2 -1
- package/src/gir/namespace.ts +2 -2
- package/src/gir/promisify.ts +2 -2
- package/src/gir/property.ts +2 -2
- package/src/gir/signal.ts +2 -1
- package/src/gir.ts +48 -8
- package/src/injections/shell.ts +5 -3
- package/src/utils/gir-parsing.ts +3 -3
- package/src/utils/types.ts +171 -21
package/package.json
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
2
|
+
"name": "@ts-for-gir/lib",
|
|
3
|
+
"version": "4.0.1",
|
|
4
|
+
"description": "Typescript .d.ts generator from GIR for gjs",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"module": "src/index.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=18"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./src/index.ts"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"check": "tsc --noEmit"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/gjsify/ts-for-gir.git"
|
|
20
|
+
},
|
|
21
|
+
"author": "Pascal Garber <pascal@mailfreun.de>",
|
|
22
|
+
"files": [
|
|
23
|
+
"src"
|
|
24
|
+
],
|
|
25
|
+
"license": "Apache-2.0",
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/gjsify/ts-for-gir/issues"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/gjsify/ts-for-gir#readme",
|
|
30
|
+
"keywords": [
|
|
31
|
+
"gjs",
|
|
32
|
+
"typescript",
|
|
33
|
+
"generate",
|
|
34
|
+
"gir",
|
|
35
|
+
"gobject-introspection",
|
|
36
|
+
"gnome",
|
|
37
|
+
"gtk",
|
|
38
|
+
"glib",
|
|
39
|
+
"gobject",
|
|
40
|
+
"dts",
|
|
41
|
+
"type definitions"
|
|
42
|
+
],
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@ts-for-gir/tsconfig": "^4.0.1",
|
|
45
|
+
"@types/ejs": "^3.1.5",
|
|
46
|
+
"@types/lodash": "^4.17.24",
|
|
47
|
+
"@types/node": "^25.6.2",
|
|
48
|
+
"rimraf": "^6.1.3",
|
|
49
|
+
"typescript": "^6.0.3"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@gi.ts/parser": "^4.0.1",
|
|
53
|
+
"@ts-for-gir/reporter": "^4.0.1",
|
|
54
|
+
"@ts-for-gir/templates": "^4.0.1",
|
|
55
|
+
"colorette": "^2.0.20",
|
|
56
|
+
"ejs": "^5.0.2",
|
|
57
|
+
"glob": "^13.0.6",
|
|
58
|
+
"lodash": "4.18.1"
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/gir/function.ts
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
ArrayType,
|
|
5
5
|
ClosureType,
|
|
6
6
|
type Generic,
|
|
7
|
-
|
|
7
|
+
makeNullable,
|
|
8
8
|
type TypeExpression,
|
|
9
9
|
TypeIdentifier,
|
|
10
10
|
UnknownType,
|
|
@@ -259,7 +259,7 @@ export class IntrospectedFunction extends IntrospectedNamespaceMember {
|
|
|
259
259
|
}
|
|
260
260
|
} else {
|
|
261
261
|
if (isOptional) {
|
|
262
|
-
params.push(p.copy({ type:
|
|
262
|
+
params.push(p.copy({ type: makeNullable(type), isOptional: false }));
|
|
263
263
|
} else {
|
|
264
264
|
params.push(p);
|
|
265
265
|
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
Generic,
|
|
6
6
|
GenericType,
|
|
7
7
|
GenerifiedTypeIdentifier,
|
|
8
|
+
makeNullable,
|
|
8
9
|
NullableType,
|
|
9
10
|
type TypeExpression,
|
|
10
11
|
TypeIdentifier,
|
|
@@ -77,7 +78,7 @@ function resolveNullableProperties(cls: IntrospectedBaseClass): void {
|
|
|
77
78
|
const getter = cls.members.find((m) => m.name === getterName && !(m instanceof IntrospectedStaticClassFunction));
|
|
78
79
|
|
|
79
80
|
if (getter instanceof IntrospectedClassFunction && getter.return() instanceof NullableType) {
|
|
80
|
-
prop.type =
|
|
81
|
+
prop.type = makeNullable(prop.type);
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
}
|
package/src/gir/namespace.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BooleanType, ClosureType, makeUnion, PromiseType, TupleType, TypeIdentifier, VoidType } from "../gir.ts";
|
|
2
2
|
|
|
3
3
|
import type { GirModule } from "../gir-module.ts";
|
|
4
4
|
import type { IntrospectedAlias } from "./alias.ts";
|
|
@@ -65,7 +65,7 @@ export function promisifyNamespaceFunctions(namespace: GirModule) {
|
|
|
65
65
|
parameters: sync_parameters,
|
|
66
66
|
}),
|
|
67
67
|
node.copy({
|
|
68
|
-
return_type:
|
|
68
|
+
return_type: makeUnion(async_return, node.return()),
|
|
69
69
|
}),
|
|
70
70
|
]);
|
|
71
71
|
}
|
package/src/gir/promisify.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BooleanType, ClosureType, makeUnion, PromiseType, TupleType, TypeIdentifier, VoidType } from "../gir.ts";
|
|
2
2
|
import { IntrospectedConstructor } from "./constructor.ts";
|
|
3
3
|
import type { IntrospectedClassFunction } from "./introspected-classes.ts";
|
|
4
4
|
import {
|
|
@@ -42,7 +42,7 @@ function generatePromisifyOverloadedSignatures(
|
|
|
42
42
|
// Union overload (with optional callback)
|
|
43
43
|
const unionOverload = node.copy({
|
|
44
44
|
parameters: [...async_parameters, sync_parameters[sync_parameters.length - 1].copy({ isOptional: true })],
|
|
45
|
-
returnType:
|
|
45
|
+
returnType: makeUnion(async_return, VoidType),
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
return [promiseOverload, callbackOverload, unionOverload];
|
package/src/gir/property.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FormatGenerator } from "../generators/generator.ts";
|
|
2
|
-
import {
|
|
2
|
+
import { makeNullable, type TypeExpression } from "../gir.ts";
|
|
3
3
|
import type { GirFieldElement, GirPropertyElement } from "../index.ts";
|
|
4
4
|
import type { OptionsLoad } from "../types/index.ts";
|
|
5
5
|
import type { Options } from "../types/introspected.ts";
|
|
@@ -210,7 +210,7 @@ export class IntrospectedProperty extends IntrospectedBase<IntrospectedEnum | In
|
|
|
210
210
|
property.getter = element.$.getter;
|
|
211
211
|
|
|
212
212
|
if (element.$.nullable === "1" || element.$["allow-none"] === "1") {
|
|
213
|
-
property.type =
|
|
213
|
+
property.type = makeNullable(property.type);
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
return property;
|
package/src/gir/signal.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { GirDirection } from "@gi.ts/parser";
|
|
|
3
3
|
import type { FormatGenerator } from "../generators/generator.ts";
|
|
4
4
|
import {
|
|
5
5
|
ArrayType,
|
|
6
|
+
makeNullable,
|
|
6
7
|
NativeType,
|
|
7
8
|
NullableType,
|
|
8
9
|
NumberType,
|
|
@@ -171,7 +172,7 @@ export class IntrospectedSignal extends IntrospectedClassMember<IntrospectedClas
|
|
|
171
172
|
}
|
|
172
173
|
} else {
|
|
173
174
|
if (isOptional) {
|
|
174
|
-
params.push(p.copy({ type:
|
|
175
|
+
params.push(p.copy({ type: makeNullable(type), isOptional: false }));
|
|
175
176
|
} else {
|
|
176
177
|
params.push(p);
|
|
177
178
|
}
|
package/src/gir.ts
CHANGED
|
@@ -348,9 +348,9 @@ export class NativeType extends TypeExpression {
|
|
|
348
348
|
export class OrType extends TypeExpression {
|
|
349
349
|
readonly types: ReadonlyArray<TypeExpression>;
|
|
350
350
|
|
|
351
|
-
constructor(
|
|
351
|
+
constructor(...types: TypeExpression[]) {
|
|
352
352
|
super();
|
|
353
|
-
this.types = [
|
|
353
|
+
this.types = [...types];
|
|
354
354
|
}
|
|
355
355
|
|
|
356
356
|
rewrap(type: TypeExpression): TypeExpression {
|
|
@@ -362,17 +362,15 @@ export class OrType extends TypeExpression {
|
|
|
362
362
|
}
|
|
363
363
|
|
|
364
364
|
resolve(namespace: IntrospectedNamespace, options: OptionsGeneration): TypeExpression {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
return new OrType(type.resolve(namespace, options), ...types.map((t) => t.resolve(namespace, options)));
|
|
365
|
+
return makeUnion(...this.types.map((t) => t.resolve(namespace, options)));
|
|
368
366
|
}
|
|
369
367
|
|
|
370
368
|
print(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
|
|
371
|
-
return
|
|
369
|
+
return `${this.types.map((t) => t.print(namespace, options)).join(" | ")}`;
|
|
372
370
|
}
|
|
373
371
|
|
|
374
372
|
rootPrint(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
|
|
375
|
-
return
|
|
373
|
+
return this.print(namespace, options);
|
|
376
374
|
}
|
|
377
375
|
|
|
378
376
|
equals(type: TypeExpression) {
|
|
@@ -384,6 +382,36 @@ export class OrType extends TypeExpression {
|
|
|
384
382
|
}
|
|
385
383
|
}
|
|
386
384
|
|
|
385
|
+
export function makeUnion(...inputTypes: TypeExpression[]) {
|
|
386
|
+
const types: Set<TypeExpression> = new Set();
|
|
387
|
+
for (const type of inputTypes) {
|
|
388
|
+
if (type instanceof BinaryType) {
|
|
389
|
+
types.add(type.a);
|
|
390
|
+
types.add(type.b);
|
|
391
|
+
} else if (type instanceof OrType && !(type instanceof TupleType)) {
|
|
392
|
+
for (const t of type.types) {
|
|
393
|
+
types.add(t);
|
|
394
|
+
}
|
|
395
|
+
} else {
|
|
396
|
+
types.add(type);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (types.size === 1) {
|
|
400
|
+
return [...types][0];
|
|
401
|
+
}
|
|
402
|
+
if (types.size === 2) {
|
|
403
|
+
const typesArray = [...types];
|
|
404
|
+
if (typesArray[0] === NullType) {
|
|
405
|
+
return new NullableType(typesArray[1]);
|
|
406
|
+
}
|
|
407
|
+
if (typesArray[1] === NullType) {
|
|
408
|
+
return new NullableType(typesArray[0]);
|
|
409
|
+
}
|
|
410
|
+
return new BinaryType(...typesArray);
|
|
411
|
+
}
|
|
412
|
+
return new OrType(...types);
|
|
413
|
+
}
|
|
414
|
+
|
|
387
415
|
export class TupleType extends OrType {
|
|
388
416
|
print(namespace: IntrospectedNamespace, options: OptionsGeneration): string {
|
|
389
417
|
return `[${this.types.map((t) => t.print(namespace, options)).join(", ")}]`;
|
|
@@ -624,7 +652,7 @@ export class NullableType extends BinaryType {
|
|
|
624
652
|
}
|
|
625
653
|
|
|
626
654
|
rewrap(type: TypeExpression): TypeExpression {
|
|
627
|
-
return
|
|
655
|
+
return makeNullable(this.a.rewrap(type));
|
|
628
656
|
}
|
|
629
657
|
|
|
630
658
|
get type() {
|
|
@@ -632,6 +660,13 @@ export class NullableType extends BinaryType {
|
|
|
632
660
|
}
|
|
633
661
|
}
|
|
634
662
|
|
|
663
|
+
export function makeNullable(type: TypeExpression) {
|
|
664
|
+
if (type instanceof NullableType) return type;
|
|
665
|
+
if (type === RawPointerType) return NullType;
|
|
666
|
+
if (type === AnyType) return AnyType;
|
|
667
|
+
return makeUnion(type, NullType);
|
|
668
|
+
}
|
|
669
|
+
|
|
635
670
|
export class PromiseType extends TypeExpression {
|
|
636
671
|
type: TypeExpression;
|
|
637
672
|
|
|
@@ -828,6 +863,8 @@ export class ArrayType extends TypeExpression {
|
|
|
828
863
|
typeSuffix = "".padStart(2 * depth, "[]");
|
|
829
864
|
}
|
|
830
865
|
|
|
866
|
+
if (this.type instanceof OrType && !(this.type instanceof TupleType))
|
|
867
|
+
return `(${this.type.print(namespace, options)})${typeSuffix}`;
|
|
831
868
|
return `${this.type.print(namespace, options)}${typeSuffix}`;
|
|
832
869
|
}
|
|
833
870
|
|
|
@@ -866,5 +903,8 @@ export const NullType = new NativeType("null");
|
|
|
866
903
|
export const VoidType = new NativeType("void");
|
|
867
904
|
export const UnknownType = new NativeType("unknown");
|
|
868
905
|
export const AnyFunctionType = new NativeType("(...args: any[]) => any");
|
|
906
|
+
// Distinct from NeverType, so that we can transform it into NullType when
|
|
907
|
+
// marshalled from C to JS
|
|
908
|
+
export const RawPointerType = new NativeType("never");
|
|
869
909
|
|
|
870
910
|
export type GirClassField = IntrospectedProperty | IntrospectedField;
|
package/src/injections/shell.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IntrospectedNamespace } from "../gir/namespace.ts";
|
|
2
2
|
import type { NSRegistry } from "../gir/registry.ts";
|
|
3
|
-
import {
|
|
3
|
+
import { makeUnion, NullType, TypeIdentifier } from "../gir.ts";
|
|
4
4
|
|
|
5
5
|
const shellTemplate = (version: string) => ({
|
|
6
6
|
namespace: "Shell",
|
|
@@ -24,8 +24,10 @@ const shellTemplate = (version: string) => ({
|
|
|
24
24
|
if (addGlslSnippet) {
|
|
25
25
|
// Create a new parameter with updated type using copy()
|
|
26
26
|
const updatedParameter = addGlslSnippet.parameters[0].copy({
|
|
27
|
-
type:
|
|
28
|
-
new
|
|
27
|
+
type: makeUnion(
|
|
28
|
+
new TypeIdentifier("SnippetHook", "Shell"),
|
|
29
|
+
new TypeIdentifier("SnippetHook", "Cogl"),
|
|
30
|
+
NullType,
|
|
29
31
|
),
|
|
30
32
|
});
|
|
31
33
|
|
package/src/utils/gir-parsing.ts
CHANGED
|
@@ -17,8 +17,8 @@ import {
|
|
|
17
17
|
BigintOrNumberType,
|
|
18
18
|
ClosureType,
|
|
19
19
|
GenerifiedTypeIdentifier,
|
|
20
|
+
makeNullable,
|
|
20
21
|
NativeType,
|
|
21
|
-
NullableType,
|
|
22
22
|
type TypeExpression,
|
|
23
23
|
TypeIdentifier,
|
|
24
24
|
VoidType,
|
|
@@ -242,11 +242,11 @@ export function getType(
|
|
|
242
242
|
!(variableType instanceof NativeType) &&
|
|
243
243
|
variableType !== BigintOrNumberType
|
|
244
244
|
) {
|
|
245
|
-
return
|
|
245
|
+
return makeNullable(variableType);
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
if ((!parameter.$?.direction || parameter.$.direction === GirDirection.In) && nullable) {
|
|
249
|
-
return
|
|
249
|
+
return makeNullable(variableType);
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
variableType.isPointer = isPointer;
|
package/src/utils/types.ts
CHANGED
|
@@ -7,17 +7,21 @@ import {
|
|
|
7
7
|
BigintOrNumberType,
|
|
8
8
|
BinaryType,
|
|
9
9
|
BooleanType,
|
|
10
|
+
GenerifiedTypeIdentifier,
|
|
11
|
+
makeNullable,
|
|
12
|
+
makeUnion,
|
|
10
13
|
NativeType,
|
|
11
14
|
NeverType,
|
|
12
15
|
NullableType,
|
|
16
|
+
NullType,
|
|
13
17
|
NumberType,
|
|
14
18
|
ObjectType,
|
|
15
|
-
OrType,
|
|
16
19
|
PromiseType,
|
|
20
|
+
RawPointerType,
|
|
17
21
|
StringType,
|
|
18
22
|
ThisType,
|
|
19
23
|
TupleType,
|
|
20
|
-
|
|
24
|
+
TypeExpression,
|
|
21
25
|
TypeIdentifier,
|
|
22
26
|
Uint8ArrayType,
|
|
23
27
|
UnknownType,
|
|
@@ -188,8 +192,13 @@ export function resolvePrimitiveType(name: string): TypeExpression | null {
|
|
|
188
192
|
return BigintOrNumberType;
|
|
189
193
|
case "gboolean":
|
|
190
194
|
return BooleanType;
|
|
191
|
-
case "gpointer":
|
|
192
|
-
|
|
195
|
+
case "gpointer":
|
|
196
|
+
// You can't use pointers. Pointer arguments are mostly not exposed
|
|
197
|
+
// in GJS, but any exposed pointer arguments are always marshalled
|
|
198
|
+
// as null pointers. If the argument is nullable, this will combine
|
|
199
|
+
// with `null` to produce `null`, but if the argument isn't nullable
|
|
200
|
+
// it's impossible to pass a valid parameter to the function.
|
|
201
|
+
return RawPointerType;
|
|
193
202
|
case "object": // Support TS "object"
|
|
194
203
|
return ObjectType;
|
|
195
204
|
case "va_list":
|
|
@@ -224,52 +233,193 @@ export function resolveDirectedType(type: TypeExpression, direction: GirDirectio
|
|
|
224
233
|
type.type.equals(Uint8ArrayType) &&
|
|
225
234
|
type.arrayDepth === 0
|
|
226
235
|
) {
|
|
227
|
-
return
|
|
236
|
+
return makeUnion(type, StringType);
|
|
228
237
|
} else {
|
|
229
238
|
// Rewrap arrays if they have directional types
|
|
230
239
|
return type.rewrap(resolveDirectedType(type.type, direction) ?? type.type);
|
|
231
240
|
}
|
|
232
241
|
} else if (type instanceof TypeIdentifier) {
|
|
233
242
|
if ((direction === GirDirection.In || direction === GirDirection.Inout) && type.is("GLib", "Bytes")) {
|
|
234
|
-
return
|
|
243
|
+
return makeUnion(type, Uint8ArrayType);
|
|
235
244
|
} else if (type.is("GObject", "Value")) {
|
|
236
245
|
if (direction === GirDirection.In || direction === GirDirection.Inout) {
|
|
237
|
-
return
|
|
246
|
+
return makeUnion(type, AnyType);
|
|
238
247
|
} else {
|
|
239
248
|
// GJS converts GObject.Value out parameters to their unboxed type, which we don't know,
|
|
240
249
|
// so type as `unknown`
|
|
241
250
|
return UnknownType;
|
|
242
251
|
}
|
|
243
252
|
} else if (type.is("GLib", "HashTable")) {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
253
|
+
// GJS marshalls `GHashTable<K, V>` to and from a plain JS object
|
|
254
|
+
// in both directions — only string-typed keys (`utf8`, `filename`)
|
|
255
|
+
// and integer-typed keys (`bool`, signed/unsigned 8/16/32-bit
|
|
256
|
+
// ints) are supported. `gunichar` accepts either string or
|
|
257
|
+
// integer. Anything else throws during marshalling — see
|
|
258
|
+
// https://gitlab.gnome.org/GNOME/gjs/-/blob/main/gi/arg.cpp#L316-420
|
|
259
|
+
// and the discussion at
|
|
260
|
+
// https://github.com/gjsify/ts-for-gir/issues/392.
|
|
261
|
+
//
|
|
262
|
+
// The TS type `GLib.HashTable<K, V>` does not represent any value
|
|
263
|
+
// that user code can actually hold — there's no GHashTable
|
|
264
|
+
// constructor exposed to JS. So in EVERY direction we emit the
|
|
265
|
+
// concrete JS shape (`{ [key: string]: V }` for string-keyed,
|
|
266
|
+
// `{ [key: number]: V }` for integer-keyed) and fall back to
|
|
267
|
+
// `never` when the declared key type can't be marshalled —
|
|
268
|
+
// statically encoding that the function can't be called rather
|
|
269
|
+
// than silently lying with a HashTable type that has no runtime
|
|
270
|
+
// instances.
|
|
271
|
+
return hashTableToJsDict(type);
|
|
250
272
|
}
|
|
251
273
|
} else if (type === BigintOrNumberType && direction === GirDirection.Out) {
|
|
252
274
|
// 64-bit integers accept number or bigint, but only return number to JS
|
|
253
275
|
return NumberType;
|
|
276
|
+
} else if (type === RawPointerType && direction === GirDirection.Out) {
|
|
277
|
+
// Raw pointers are always marshalled as JS null.
|
|
278
|
+
return NullType;
|
|
254
279
|
} else if (type instanceof PromiseType) {
|
|
255
280
|
// Propagate direction into the Promise's inner type so e.g. async
|
|
256
281
|
// functions returning 64-bit ints resolve to `Promise<number>` rather
|
|
257
282
|
// than `Promise<bigint | number>`.
|
|
258
283
|
const resolvedInner = resolveDirectedType(type.type, direction);
|
|
259
284
|
if (resolvedInner) return new PromiseType(resolvedInner);
|
|
260
|
-
} else if (type instanceof
|
|
285
|
+
} else if (type instanceof NullableType) {
|
|
286
|
+
// Walk into the wrapped type and rebuild as NullableType so e.g.
|
|
287
|
+
// `GLib.HashTable<string, string> | null` becomes
|
|
288
|
+
// `{ [key: string]: string } | null` (without the rebuild, the
|
|
289
|
+
// outer NullableType-aware BinaryType branch below would skip
|
|
290
|
+
// this case to preserve NullableType identity, leaving the inner
|
|
291
|
+
// type unrewritten).
|
|
292
|
+
const inner = resolveDirectedType(type.a, direction);
|
|
293
|
+
if (inner !== null) return makeNullable(inner);
|
|
294
|
+
} else if (type instanceof TupleType) {
|
|
295
|
+
// Walk into each tuple element so a `[HashTable<string, V>, …]`
|
|
296
|
+
// (typical async [result, out-params, …] shape) gets each element
|
|
297
|
+
// rewritten. BinaryType's branch below would also match TupleType
|
|
298
|
+
// since it extends OrType → BinaryType is one of its supertypes,
|
|
299
|
+
// but the rebuild via `makeUnion` would collapse tuple semantics
|
|
300
|
+
// into a union — preserve the tuple by rebuilding the same class.
|
|
301
|
+
let changed = false;
|
|
302
|
+
const inner = type.types.map((t) => {
|
|
303
|
+
const resolved = resolveDirectedType(t, direction);
|
|
304
|
+
if (resolved !== null) {
|
|
305
|
+
changed = true;
|
|
306
|
+
return resolved;
|
|
307
|
+
}
|
|
308
|
+
return t;
|
|
309
|
+
});
|
|
310
|
+
if (changed) {
|
|
311
|
+
const [first, ...rest] = inner;
|
|
312
|
+
return new TupleType(first, ...rest);
|
|
313
|
+
}
|
|
314
|
+
} else if (type instanceof BinaryType) {
|
|
261
315
|
// Walk through binary unions like `Promise<T> | void` (the dual-call
|
|
262
316
|
// async overload) so the inner types still get direction propagation.
|
|
263
|
-
// NullableType is skipped to preserve its subclass behaviour.
|
|
264
317
|
const a = resolveDirectedType(type.a, direction) ?? type.a;
|
|
265
318
|
const b = resolveDirectedType(type.b, direction) ?? type.b;
|
|
266
|
-
if (a !== type.a || b !== type.b) return
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
319
|
+
if (a !== type.a || b !== type.b) return makeUnion(a, b);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Map a `GLib.HashTable<K, V>` reference to the concrete JS object shape
|
|
327
|
+
* GJS marshals it to/from. Direction-independent — the marshalling is
|
|
328
|
+
* symmetric (a method that accepts a HashTable and a method that returns
|
|
329
|
+
* one both see a plain object on the JS side).
|
|
330
|
+
*
|
|
331
|
+
* Key-type rules (per gi/arg.cpp's `gjs_value_from_g_hash` /
|
|
332
|
+
* `gjs_value_to_g_hash`):
|
|
333
|
+
*
|
|
334
|
+
* string-shaped (`utf8`, `filename`) → `{ [key: string]: V }`
|
|
335
|
+
* integer-shaped (`gboolean`, `gint8`…`guint32`) → `{ [key: number]: V }`
|
|
336
|
+
* `gunichar` → `{ [key: string]: V }`
|
|
337
|
+
* (also accepts numbers,
|
|
338
|
+
* but the broader string
|
|
339
|
+
* case is more useful)
|
|
340
|
+
* anything else (raw pointers, classes, records, …) → `never`
|
|
341
|
+
* (uncallable on the JS side)
|
|
342
|
+
*
|
|
343
|
+
* The fallback for "type information missing" (e.g. an introspection
|
|
344
|
+
* record with bare `HashTable` and no generics) is `{ [key: string]: any }`
|
|
345
|
+
* — the most common shape, matching the historical generator output.
|
|
346
|
+
*/
|
|
347
|
+
function hashTableToJsDict(type: TypeIdentifier): TypeExpression {
|
|
348
|
+
if (!(type instanceof GenerifiedTypeIdentifier) || type.generics.length === 0) {
|
|
349
|
+
// Bare `HashTable` with no generics — emit the catch-all dict shape.
|
|
350
|
+
return new NativeType("{ [key: string]: any }");
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const [keyType, valueType] = type.generics;
|
|
354
|
+
const keyShape = jsKeyShapeFor(keyType);
|
|
355
|
+
if (keyShape === null) {
|
|
356
|
+
// Unsupported key type — function is uncallable from JS.
|
|
357
|
+
return NeverType;
|
|
272
358
|
}
|
|
273
359
|
|
|
360
|
+
// Return a TypeExpression subclass that defers printing of V until a
|
|
361
|
+
// real namespace is available (which is the case both at type-resolve
|
|
362
|
+
// time and at template-emit time). A plain `NativeType((options) =>
|
|
363
|
+
// …)` would not work — its callback receives only `options`, not the
|
|
364
|
+
// namespace V's own print needs.
|
|
365
|
+
return new HashTableDictType(keyShape, valueType ?? AnyType);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Decide which TypeScript index-signature key type a GHashTable key maps to.
|
|
370
|
+
* Returns `null` for unsupported key types — the caller emits `never` so the
|
|
371
|
+
* containing method/property surfaces as uncallable rather than lying with a
|
|
372
|
+
* synthetic key type that doesn't match runtime marshalling.
|
|
373
|
+
*/
|
|
374
|
+
function jsKeyShapeFor(keyType: TypeExpression): "string" | "number" | null {
|
|
375
|
+
if (keyType === StringType) return "string";
|
|
376
|
+
if (keyType instanceof TypeIdentifier && keyType.is("GLib", "filename")) return "string";
|
|
377
|
+
// Integer-shaped keys: signed/unsigned 8/16/32-bit ints + gboolean
|
|
378
|
+
// (0/1 number) all collapse to TS `number`. 64-bit ints accept either
|
|
379
|
+
// number or bigint at the JS boundary; pick `number` for index keys
|
|
380
|
+
// since object keys are always strings/numbers in JS (no bigint keys).
|
|
381
|
+
if (keyType === NumberType) return "number";
|
|
382
|
+
if (keyType === BooleanType) return "number";
|
|
383
|
+
if (keyType === BigintOrNumberType) return "number";
|
|
274
384
|
return null;
|
|
275
385
|
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* `{ [key: K]: V }` shape for `GLib.HashTable<K, V>` — a TypeExpression
|
|
389
|
+
* subclass so `V.rootPrint(namespace, options)` happens at emit time with
|
|
390
|
+
* the right namespace, instead of being baked in at type-resolve time.
|
|
391
|
+
*/
|
|
392
|
+
class HashTableDictType extends TypeExpression {
|
|
393
|
+
constructor(
|
|
394
|
+
readonly keyShape: "string" | "number",
|
|
395
|
+
readonly valueType: TypeExpression,
|
|
396
|
+
) {
|
|
397
|
+
super();
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
print(namespace: IntrospectedNamespace, options: import("../types/index.ts").OptionsGeneration): string {
|
|
401
|
+
return `{ [key: ${this.keyShape}]: ${this.valueType.rootPrint(namespace, options)} }`;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
resolve(_namespace: IntrospectedNamespace, _options: import("../types/index.ts").OptionsGeneration): TypeExpression {
|
|
405
|
+
return this;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
equals(type: TypeExpression): boolean {
|
|
409
|
+
return (
|
|
410
|
+
type instanceof HashTableDictType && this.keyShape === type.keyShape && this.valueType.equals(type.valueType)
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
rewrap(_type: TypeExpression): TypeExpression {
|
|
415
|
+
return this;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
unwrap(): TypeExpression {
|
|
419
|
+
return this;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
deepUnwrap(): TypeExpression {
|
|
423
|
+
return this.valueType.deepUnwrap();
|
|
424
|
+
}
|
|
425
|
+
}
|