@ts-for-gir/generator-typescript 4.0.0-beta.40 → 4.0.0-beta.41
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ts-for-gir/generator-typescript",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.41",
|
|
4
4
|
"description": "TypeScript type definition generator for ts-for-gir",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"module": "src/index.ts",
|
|
@@ -31,17 +31,18 @@
|
|
|
31
31
|
"generator"
|
|
32
32
|
],
|
|
33
33
|
"devDependencies": {
|
|
34
|
+
"@ts-for-gir/tsconfig": "^4.0.0-beta.41",
|
|
34
35
|
"@types/ejs": "^3.1.5",
|
|
35
|
-
"@types/node": "^24.
|
|
36
|
+
"@types/node": "^24.12.0",
|
|
36
37
|
"@types/xml2js": "^0.4.14",
|
|
37
38
|
"typescript": "^5.9.3"
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
|
40
|
-
"@gi.ts/parser": "^4.0.0-beta.
|
|
41
|
-
"@ts-for-gir/generator-base": "^4.0.0-beta.
|
|
42
|
-
"@ts-for-gir/lib": "^4.0.0-beta.
|
|
43
|
-
"@ts-for-gir/templates": "^4.0.0-beta.
|
|
44
|
-
"ejs": "^
|
|
41
|
+
"@gi.ts/parser": "^4.0.0-beta.41",
|
|
42
|
+
"@ts-for-gir/generator-base": "^4.0.0-beta.41",
|
|
43
|
+
"@ts-for-gir/lib": "^4.0.0-beta.41",
|
|
44
|
+
"@ts-for-gir/templates": "^4.0.0-beta.41",
|
|
45
|
+
"ejs": "^5.0.1",
|
|
45
46
|
"xml2js": "^0.6.2"
|
|
46
47
|
}
|
|
47
48
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { DependencyManager, GirModule, NSRegistry, OptionsGeneration, Reporter } from "@ts-for-gir/lib";
|
|
2
|
+
import type { ModuleGenerator } from "../module-generator.ts";
|
|
3
|
+
import { NpmPackage } from "../npm-package.ts";
|
|
4
|
+
import type { TemplateProcessor } from "../template-processor.ts";
|
|
5
|
+
|
|
6
|
+
/** Handles exporting generated modules to files. */
|
|
7
|
+
export class ModuleExporter {
|
|
8
|
+
constructor(private readonly core: ModuleGenerator) {}
|
|
9
|
+
|
|
10
|
+
private get config(): OptionsGeneration {
|
|
11
|
+
return this.core.config;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private get log(): Reporter {
|
|
15
|
+
return this.core.log;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
private get moduleTemplateProcessor(): TemplateProcessor {
|
|
19
|
+
return this.core.moduleTemplateProcessor;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private get dependencyManager(): DependencyManager {
|
|
23
|
+
return this.core.dependencyManager;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Export a template file to outdir or log its content. */
|
|
27
|
+
private async exportTemplate(template: string, target: string): Promise<void> {
|
|
28
|
+
if (this.config.outdir) {
|
|
29
|
+
await this.moduleTemplateProcessor.create(template, this.config.outdir, target);
|
|
30
|
+
} else {
|
|
31
|
+
const { append, prepend } = await this.moduleTemplateProcessor.load(template);
|
|
32
|
+
this.log.log(append + prepend);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async exportModuleTS(): Promise<void> {
|
|
37
|
+
const girModule = this.core.girNamespace;
|
|
38
|
+
const template = "module.d.ts";
|
|
39
|
+
const explicitTemplate = `${girModule.importName}.d.ts`;
|
|
40
|
+
const output = await this.core.generateModule(girModule);
|
|
41
|
+
|
|
42
|
+
if (!output) {
|
|
43
|
+
this.log.error("Failed to generate gir module");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (await this.moduleTemplateProcessor.exists(explicitTemplate)) {
|
|
48
|
+
const { append: appendExplicit, prepend: prependExplicit } =
|
|
49
|
+
await this.moduleTemplateProcessor.load(explicitTemplate);
|
|
50
|
+
output.unshift(prependExplicit);
|
|
51
|
+
output.push(appendExplicit);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const { append, prepend } = await this.moduleTemplateProcessor.load(template);
|
|
55
|
+
output.unshift(prepend);
|
|
56
|
+
output.push(append);
|
|
57
|
+
|
|
58
|
+
if (this.config.outdir) {
|
|
59
|
+
await this.moduleTemplateProcessor.write(output.join("\n"), this.config.outdir, explicitTemplate);
|
|
60
|
+
} else {
|
|
61
|
+
this.log.log(output.join("\n"));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async exportModule(registry: NSRegistry, girModule: GirModule): Promise<void> {
|
|
66
|
+
await this.exportModuleTS();
|
|
67
|
+
|
|
68
|
+
if (this.config.package) {
|
|
69
|
+
const name = girModule.importName;
|
|
70
|
+
await this.exportTemplate("module.js", `${name}.js`);
|
|
71
|
+
await this.exportTemplate("index.d.ts", "index.d.ts");
|
|
72
|
+
await this.exportTemplate("index.js", "index.js");
|
|
73
|
+
await this.exportTemplate("module-ambient.d.ts", `${name}-ambient.d.ts`);
|
|
74
|
+
await this.exportTemplate("module-ambient.js", `${name}-ambient.js`);
|
|
75
|
+
await this.exportTemplate("module-import.d.ts", `${name}-import.d.ts`);
|
|
76
|
+
await this.exportTemplate("module-import.js", `${name}-import.js`);
|
|
77
|
+
|
|
78
|
+
const pkg = new NpmPackage(
|
|
79
|
+
this.config,
|
|
80
|
+
this.dependencyManager,
|
|
81
|
+
registry,
|
|
82
|
+
girModule,
|
|
83
|
+
girModule.transitiveDependencies,
|
|
84
|
+
);
|
|
85
|
+
await pkg.exportNPMPackage();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BinaryType,
|
|
3
|
+
BooleanType,
|
|
4
|
+
FilterBehavior,
|
|
5
|
+
filterConflicts,
|
|
6
|
+
type GirModule,
|
|
7
|
+
generateIndent,
|
|
8
|
+
type IntrospectedBaseClass,
|
|
9
|
+
IntrospectedClass,
|
|
10
|
+
IntrospectedClassFunction,
|
|
11
|
+
IntrospectedInterface,
|
|
12
|
+
type IntrospectedRecord,
|
|
13
|
+
mergeDescs,
|
|
14
|
+
NumberType,
|
|
15
|
+
type OptionsGeneration,
|
|
16
|
+
VoidType,
|
|
17
|
+
} from "@ts-for-gir/lib";
|
|
18
|
+
import type { ModuleGenerator } from "../module-generator.ts";
|
|
19
|
+
|
|
20
|
+
const SIGNAL_JSDOC = "/** @signal */";
|
|
21
|
+
|
|
22
|
+
/** Handles generation of GObject signal-related TypeScript definitions. */
|
|
23
|
+
export class SignalGenerator {
|
|
24
|
+
constructor(private readonly core: ModuleGenerator) {}
|
|
25
|
+
|
|
26
|
+
private get namespace(): GirModule {
|
|
27
|
+
return this.core.girNamespace;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private get config(): OptionsGeneration {
|
|
31
|
+
return this.core.config;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Generate SignalSignatures interface for type-safe signal handling.
|
|
36
|
+
*
|
|
37
|
+
* Creates a comprehensive mapping of signal names to their callback types,
|
|
38
|
+
* enabling TypeScript to provide proper type checking and IntelliSense for
|
|
39
|
+
* GObject signals using the centralized getAllSignals() method from the model.
|
|
40
|
+
*/
|
|
41
|
+
generateClassSignalInterfaces(girClass: IntrospectedClass, indentCount = 0): string[] {
|
|
42
|
+
const def: string[] = [];
|
|
43
|
+
const indent = generateIndent(indentCount);
|
|
44
|
+
|
|
45
|
+
def.push(`${indent}// Signal signatures`);
|
|
46
|
+
def.push(`${indent}interface SignalSignatures`);
|
|
47
|
+
|
|
48
|
+
const parentSignatures: string[] = [];
|
|
49
|
+
|
|
50
|
+
// Inherit signal signatures from parent class
|
|
51
|
+
const parentResolution = girClass.resolveParents().extends();
|
|
52
|
+
if (parentResolution && parentResolution.node instanceof IntrospectedClass) {
|
|
53
|
+
const parentClass = parentResolution.node as IntrospectedClass;
|
|
54
|
+
const parentTypeIdentifier = parentResolution.identifier
|
|
55
|
+
.resolveIdentifier(this.namespace, this.config)
|
|
56
|
+
?.print(this.namespace, this.config);
|
|
57
|
+
|
|
58
|
+
const hasSignalMethods = parentClass.signals?.length > 0;
|
|
59
|
+
const isNotTemplateWorkaround = !(
|
|
60
|
+
this.namespace.namespace === "Gimp" && ["ParamObject", "ParamItem", "ParamArray"].includes(parentClass.name)
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
if (parentTypeIdentifier && (hasSignalMethods || isNotTemplateWorkaround)) {
|
|
64
|
+
parentSignatures.push(`${parentTypeIdentifier}.SignalSignatures`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Inherit signal signatures from implemented interfaces
|
|
69
|
+
const interfaceSignatures = girClass
|
|
70
|
+
.resolveParents()
|
|
71
|
+
.implements()
|
|
72
|
+
.filter((iface) => iface.node instanceof IntrospectedInterface)
|
|
73
|
+
.filter((iface) => {
|
|
74
|
+
const node = iface.node as unknown as { signals?: unknown[] };
|
|
75
|
+
return node.signals && node.signals.length > 0;
|
|
76
|
+
})
|
|
77
|
+
.map((iface) => {
|
|
78
|
+
const interfaceTypeIdentifier = iface.identifier
|
|
79
|
+
.resolveIdentifier(this.namespace, this.config)
|
|
80
|
+
?.print(this.namespace, this.config);
|
|
81
|
+
return interfaceTypeIdentifier ? `${interfaceTypeIdentifier}.SignalSignatures` : null;
|
|
82
|
+
})
|
|
83
|
+
.filter((sig): sig is string => !!sig);
|
|
84
|
+
|
|
85
|
+
parentSignatures.push(...interfaceSignatures);
|
|
86
|
+
|
|
87
|
+
if (parentSignatures.length > 0) {
|
|
88
|
+
def.push(` extends ${parentSignatures.join(", ")} {`);
|
|
89
|
+
} else {
|
|
90
|
+
const isGObjectObject = girClass.name === "Object" && girClass.namespace.namespace === "GObject";
|
|
91
|
+
|
|
92
|
+
if (isGObjectObject) {
|
|
93
|
+
def.push(" {");
|
|
94
|
+
} else {
|
|
95
|
+
const gobjectNamespace = this.namespace.assertInstalledImport("GObject");
|
|
96
|
+
const gobjectObjectClass = gobjectNamespace.assertClass("Object");
|
|
97
|
+
const gobjectRef = gobjectObjectClass
|
|
98
|
+
.getType()
|
|
99
|
+
.resolveIdentifier(this.namespace, this.config)
|
|
100
|
+
?.print(this.namespace, this.config);
|
|
101
|
+
|
|
102
|
+
const fallbackRef = gobjectRef ? `${gobjectRef}.SignalSignatures` : "GObject.Object.SignalSignatures";
|
|
103
|
+
def.push(` extends ${fallbackRef} {`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const allSignals = girClass.getAllSignals();
|
|
108
|
+
|
|
109
|
+
allSignals.forEach((signalInfo) => {
|
|
110
|
+
const signalKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(signalInfo.name) ? signalInfo.name : `"${signalInfo.name}"`;
|
|
111
|
+
|
|
112
|
+
let cbType: string;
|
|
113
|
+
|
|
114
|
+
if (signalInfo.isNotifySignal) {
|
|
115
|
+
const gobjectRef = this.namespace.namespace === "GObject" ? "" : "GObject.";
|
|
116
|
+
cbType = `(pspec: ${gobjectRef}ParamSpec) => void`;
|
|
117
|
+
} else if (signalInfo.signal) {
|
|
118
|
+
const paramTypes = signalInfo.signal.parameters
|
|
119
|
+
.map((p, idx) => `arg${idx}: ${this.core.generateType(p.type)}`)
|
|
120
|
+
.join(", ");
|
|
121
|
+
|
|
122
|
+
let returnType = signalInfo.signal.return_type;
|
|
123
|
+
if (signalInfo.signal.return_type.equals(BooleanType)) {
|
|
124
|
+
returnType = new BinaryType(BooleanType, VoidType);
|
|
125
|
+
}
|
|
126
|
+
const returnTypeStr = this.core.generateType(returnType);
|
|
127
|
+
|
|
128
|
+
cbType = `(${paramTypes}) => ${returnTypeStr}`;
|
|
129
|
+
} else {
|
|
130
|
+
const paramTypes = signalInfo.parameterTypes?.map((type, idx) => `arg${idx}: ${type}`) || [];
|
|
131
|
+
const returnTypeStr = signalInfo.returnType || "void";
|
|
132
|
+
cbType = `(${paramTypes.join(", ")}) => ${returnTypeStr}`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Add signal doc comment with @signal tag and signal-specific modifier tags
|
|
136
|
+
if (!signalInfo.isNotifySignal && signalInfo.signal) {
|
|
137
|
+
const signalTags = [
|
|
138
|
+
{ tagName: "signal", paramName: "", text: "" },
|
|
139
|
+
...this.namespace.getTsDocMetadataTags(signalInfo.signal.metadata),
|
|
140
|
+
];
|
|
141
|
+
if (signalInfo.signal.detailed) signalTags.push({ tagName: "detailed", paramName: "", text: "" });
|
|
142
|
+
if (signalInfo.signal.action) signalTags.push({ tagName: "action", paramName: "", text: "" });
|
|
143
|
+
if (signalInfo.signal.when)
|
|
144
|
+
signalTags.push({ tagName: `run-${signalInfo.signal.when}`, paramName: "", text: "" });
|
|
145
|
+
const comment = this.core.addGirDocComment(signalInfo.signal.doc, signalTags, indentCount + 1);
|
|
146
|
+
if (comment.length) {
|
|
147
|
+
def.push(...comment);
|
|
148
|
+
} else {
|
|
149
|
+
def.push(`${indent} /** @signal */`);
|
|
150
|
+
}
|
|
151
|
+
} else if (!signalInfo.isNotifySignal) {
|
|
152
|
+
def.push(`${indent} /** @signal */`);
|
|
153
|
+
}
|
|
154
|
+
def.push(`${indent} ${signalKey}: ${cbType};`);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
def.push(`${indent}}`);
|
|
158
|
+
def.push("");
|
|
159
|
+
|
|
160
|
+
return def;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Generate signal methods section with header comment
|
|
165
|
+
*/
|
|
166
|
+
generateClassSignals(girClass: IntrospectedClass): string[] {
|
|
167
|
+
return mergeDescs(this.generateSignalMethods(girClass), "Signals", 1);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Generate the $signals property for type-safe signal access
|
|
172
|
+
*/
|
|
173
|
+
generateClassSignalsProperty(girClass: IntrospectedClass | IntrospectedRecord, indentCount = 1): string[] {
|
|
174
|
+
const isGObjectObject = girClass.name === "Object" && girClass.namespace.namespace === "GObject";
|
|
175
|
+
const hasGObjectParent =
|
|
176
|
+
isGObjectObject ||
|
|
177
|
+
girClass.someParent((p: IntrospectedBaseClass) => p.namespace.namespace === "GObject" && p.name === "Object");
|
|
178
|
+
|
|
179
|
+
if (!hasGObjectParent) return [];
|
|
180
|
+
|
|
181
|
+
const indent = generateIndent(indentCount);
|
|
182
|
+
return [
|
|
183
|
+
"",
|
|
184
|
+
`${indent}/**`,
|
|
185
|
+
`${indent} * Compile-time signal type information.`,
|
|
186
|
+
`${indent} *`,
|
|
187
|
+
`${indent} * This instance property is generated only for TypeScript type checking.`,
|
|
188
|
+
`${indent} * It is not defined at runtime and should not be accessed in JS code.`,
|
|
189
|
+
`${indent} * @internal`,
|
|
190
|
+
`${indent} */`,
|
|
191
|
+
`${indent}$signals: ${girClass.name}.SignalSignatures;`,
|
|
192
|
+
"",
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Generate type-safe connect/connect_after/emit signal methods
|
|
198
|
+
*/
|
|
199
|
+
private generateSignalMethods(girClass: IntrospectedClass): string[] {
|
|
200
|
+
const signalFunctions = [
|
|
201
|
+
new IntrospectedClassFunction({
|
|
202
|
+
name: "connect",
|
|
203
|
+
parent: girClass,
|
|
204
|
+
parameters: [],
|
|
205
|
+
return_type: NumberType,
|
|
206
|
+
}),
|
|
207
|
+
new IntrospectedClassFunction({
|
|
208
|
+
name: "connect_after",
|
|
209
|
+
parent: girClass,
|
|
210
|
+
parameters: [],
|
|
211
|
+
return_type: NumberType,
|
|
212
|
+
}),
|
|
213
|
+
new IntrospectedClassFunction({
|
|
214
|
+
name: "emit",
|
|
215
|
+
parent: girClass,
|
|
216
|
+
parameters: [],
|
|
217
|
+
return_type: VoidType,
|
|
218
|
+
}),
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
const filteredFunctions = filterConflicts(girClass.namespace, girClass, signalFunctions, FilterBehavior.DELETE);
|
|
222
|
+
const allowedNames = new Set(filteredFunctions.map((f) => f.name));
|
|
223
|
+
|
|
224
|
+
const gobjectRef = this.namespace.namespace === "GObject" ? "" : "GObject.";
|
|
225
|
+
|
|
226
|
+
const methods: string[] = [];
|
|
227
|
+
|
|
228
|
+
if (allowedNames.has("connect")) {
|
|
229
|
+
methods.push(
|
|
230
|
+
SIGNAL_JSDOC,
|
|
231
|
+
`connect<K extends keyof ${girClass.name}.SignalSignatures>(signal: K, callback: ${gobjectRef}SignalCallback<this, ${girClass.name}.SignalSignatures[K]>): number;`,
|
|
232
|
+
"connect(signal: string, callback: (...args: any[]) => any): number;",
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (allowedNames.has("connect_after")) {
|
|
237
|
+
methods.push(
|
|
238
|
+
SIGNAL_JSDOC,
|
|
239
|
+
`connect_after<K extends keyof ${girClass.name}.SignalSignatures>(signal: K, callback: ${gobjectRef}SignalCallback<this, ${girClass.name}.SignalSignatures[K]>): number;`,
|
|
240
|
+
"connect_after(signal: string, callback: (...args: any[]) => any): number;",
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (allowedNames.has("emit")) {
|
|
245
|
+
methods.push(
|
|
246
|
+
SIGNAL_JSDOC,
|
|
247
|
+
`emit<K extends keyof ${girClass.name}.SignalSignatures>(signal: K, ...args: ${gobjectRef}GjsParameters<${girClass.name}.SignalSignatures[K]> extends [any, ...infer Q] ? Q : never): void;`,
|
|
248
|
+
"emit(signal: string, ...args: any[]): void;",
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return methods;
|
|
253
|
+
}
|
|
254
|
+
}
|
package/src/module-generator.ts
CHANGED
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
addInfoComment,
|
|
5
5
|
addTSDocCommentLines,
|
|
6
6
|
BinaryType,
|
|
7
|
-
BooleanType,
|
|
8
7
|
ClassStructTypeIdentifier,
|
|
9
8
|
ConflictType,
|
|
10
9
|
DependencyManager,
|
|
@@ -13,6 +12,7 @@ import {
|
|
|
13
12
|
filterConflicts,
|
|
14
13
|
filterFunctionConflict,
|
|
15
14
|
type Generic,
|
|
15
|
+
type GirDocContext,
|
|
16
16
|
type GirEnumMember,
|
|
17
17
|
type GirModule,
|
|
18
18
|
generateIndent,
|
|
@@ -41,10 +41,8 @@ import {
|
|
|
41
41
|
IntrospectedStaticClassFunction,
|
|
42
42
|
IntrospectedVirtualClassFunction,
|
|
43
43
|
isInvalid,
|
|
44
|
-
mergeDescs,
|
|
45
44
|
NativeType,
|
|
46
45
|
type NSRegistry,
|
|
47
|
-
NumberType,
|
|
48
46
|
type OptionsGeneration,
|
|
49
47
|
printGirDocComment,
|
|
50
48
|
promisifyFunctions,
|
|
@@ -57,11 +55,11 @@ import {
|
|
|
57
55
|
type TsDocTag,
|
|
58
56
|
TypeConflict,
|
|
59
57
|
type TypeExpression,
|
|
58
|
+
transformGirDocTagTextWithContext,
|
|
60
59
|
transformGirDocText,
|
|
61
|
-
VoidType,
|
|
62
60
|
} from "@ts-for-gir/lib";
|
|
61
|
+
import { ModuleExporter, SignalGenerator } from "./generators/index.ts";
|
|
63
62
|
// import { PackageDataParser } from './package-data-parser.ts'
|
|
64
|
-
import { NpmPackage } from "./npm-package.ts";
|
|
65
63
|
import { override as overrideGLib } from "./overrides/glib.ts";
|
|
66
64
|
import { override as overrideGObject } from "./overrides/gobject.ts";
|
|
67
65
|
import { TemplateProcessor } from "./template-processor.ts";
|
|
@@ -80,6 +78,33 @@ export enum ModuleGeneratorFormat {
|
|
|
80
78
|
Inline = "inline",
|
|
81
79
|
}
|
|
82
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Base URLs for gi-docgen content pages per namespace.
|
|
83
|
+
* Version-specific keys (e.g. "Gtk-4.0") take priority over plain namespace keys.
|
|
84
|
+
*/
|
|
85
|
+
const DOC_BASE_URLS = new Map<string, string>([
|
|
86
|
+
// GNOME core (docs.gtk.org)
|
|
87
|
+
["GLib", "https://docs.gtk.org/glib/"],
|
|
88
|
+
["GObject", "https://docs.gtk.org/gobject/"],
|
|
89
|
+
["Gio", "https://docs.gtk.org/gio/"],
|
|
90
|
+
["GdkPixbuf", "https://docs.gtk.org/gdk-pixbuf/"],
|
|
91
|
+
["Pango", "https://docs.gtk.org/Pango/"],
|
|
92
|
+
["PangoCairo", "https://docs.gtk.org/PangoCairo/"],
|
|
93
|
+
// GTK 4
|
|
94
|
+
["Gtk-4.0", "https://docs.gtk.org/gtk4/"],
|
|
95
|
+
["Gdk-4.0", "https://docs.gtk.org/gdk4/"],
|
|
96
|
+
["Gsk-4.0", "https://docs.gtk.org/gsk4/"],
|
|
97
|
+
["GdkWayland-4.0", "https://docs.gtk.org/gdk4-wayland/"],
|
|
98
|
+
["GdkX11-4.0", "https://docs.gtk.org/gdk4-x11/"],
|
|
99
|
+
// GTK 3
|
|
100
|
+
["Gtk-3.0", "https://docs.gtk.org/gtk3/"],
|
|
101
|
+
["Gdk-3.0", "https://docs.gtk.org/gdk3/"],
|
|
102
|
+
// Libadwaita
|
|
103
|
+
["Adw-1", "https://gnome.pages.gitlab.gnome.org/libadwaita/doc/1-latest/"],
|
|
104
|
+
// GtkSourceView
|
|
105
|
+
["GtkSource-5", "https://gnome.pages.gitlab.gnome.org/gtksourceview/gtksourceview5/"],
|
|
106
|
+
]);
|
|
107
|
+
|
|
83
108
|
export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
84
109
|
log: Reporter;
|
|
85
110
|
dependencyManager: DependencyManager;
|
|
@@ -88,6 +113,14 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
88
113
|
config: OptionsGeneration;
|
|
89
114
|
moduleTemplateProcessor: TemplateProcessor;
|
|
90
115
|
|
|
116
|
+
readonly signalGenerator: SignalGenerator;
|
|
117
|
+
readonly moduleExporter: ModuleExporter;
|
|
118
|
+
|
|
119
|
+
/** Public accessor for the protected namespace from FormatGenerator */
|
|
120
|
+
get girNamespace() {
|
|
121
|
+
return this.namespace;
|
|
122
|
+
}
|
|
123
|
+
|
|
91
124
|
/**
|
|
92
125
|
* @param _config The config to use without the override config
|
|
93
126
|
*/
|
|
@@ -124,6 +157,82 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
124
157
|
girModule.transitiveDependencies,
|
|
125
158
|
this.config,
|
|
126
159
|
);
|
|
160
|
+
|
|
161
|
+
this.signalGenerator = new SignalGenerator(this);
|
|
162
|
+
this.moduleExporter = new ModuleExporter(this);
|
|
163
|
+
this._docContext = this.buildDocContext();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private readonly _docContext: GirDocContext;
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Build a GirDocContext that resolves C identifiers to TypeScript paths
|
|
170
|
+
* by searching the current namespace and all its dependencies.
|
|
171
|
+
*/
|
|
172
|
+
private buildDocContext(): GirDocContext {
|
|
173
|
+
const ns = this.namespace;
|
|
174
|
+
|
|
175
|
+
// Collect all available namespaces (current + dependencies)
|
|
176
|
+
const allNamespaces: GirModule[] = [ns];
|
|
177
|
+
for (const dep of ns.allDependencies) {
|
|
178
|
+
const imported = ns.getInstalledImport(dep.namespace);
|
|
179
|
+
if (imported) allNamespaces.push(imported);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Resolve base URL for gi-docgen content pages
|
|
183
|
+
const docBaseUrl = DOC_BASE_URLS.get(`${ns.namespace}-${ns.version}`) ?? DOC_BASE_URLS.get(ns.namespace);
|
|
184
|
+
|
|
185
|
+
// Pre-merge enum constants for O(1) lookup
|
|
186
|
+
const constantMap = new Map<string, string>();
|
|
187
|
+
for (const mod of allNamespaces) {
|
|
188
|
+
for (const [cId, result] of mod.enum_constants) {
|
|
189
|
+
if (!constantMap.has(cId)) {
|
|
190
|
+
constantMap.set(cId, `${mod.namespace}.${result[0]}.${result[1]}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Type resolution cache — populated lazily on first miss
|
|
196
|
+
const typeCache = new Map<string, string | null>();
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
docBaseUrl,
|
|
200
|
+
resolveType(cTypeName: string): string | null {
|
|
201
|
+
const cached = typeCache.get(cTypeName);
|
|
202
|
+
if (cached !== undefined) return cached;
|
|
203
|
+
|
|
204
|
+
let result: string | null = null;
|
|
205
|
+
// Strategy 1: _resolve_names lookup
|
|
206
|
+
for (const mod of allNamespaces) {
|
|
207
|
+
const member = mod.getMemberWithoutOverrides(cTypeName);
|
|
208
|
+
if (member && "name" in member) {
|
|
209
|
+
result = `${mod.namespace}.${member.name}`;
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// Strategy 2: C prefix stripping
|
|
214
|
+
if (!result) {
|
|
215
|
+
for (const mod of allNamespaces) {
|
|
216
|
+
for (const prefix of mod.c_prefixes) {
|
|
217
|
+
if (cTypeName.startsWith(prefix) && cTypeName.length > prefix.length) {
|
|
218
|
+
const girName = cTypeName.slice(prefix.length);
|
|
219
|
+
if (mod.hasSymbol(girName)) {
|
|
220
|
+
result = `${mod.namespace}.${girName}`;
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (result) break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
typeCache.set(cTypeName, result);
|
|
230
|
+
return result;
|
|
231
|
+
},
|
|
232
|
+
resolveConstant(cIdentifier: string): string | null {
|
|
233
|
+
return constantMap.get(cIdentifier) ?? null;
|
|
234
|
+
},
|
|
235
|
+
};
|
|
127
236
|
}
|
|
128
237
|
|
|
129
238
|
/**
|
|
@@ -311,14 +420,22 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
311
420
|
}
|
|
312
421
|
|
|
313
422
|
generateSignal(node: IntrospectedSignal, type: IntrospectedSignalType = IntrospectedSignalType.CONNECT): string[] {
|
|
423
|
+
let fn: IntrospectedClassFunction;
|
|
314
424
|
switch (type) {
|
|
315
425
|
case IntrospectedSignalType.CONNECT:
|
|
316
|
-
|
|
426
|
+
fn = node.asConnect(false);
|
|
427
|
+
break;
|
|
317
428
|
case IntrospectedSignalType.CONNECT_AFTER:
|
|
318
|
-
|
|
429
|
+
fn = node.asConnect(true);
|
|
430
|
+
break;
|
|
319
431
|
case IntrospectedSignalType.EMIT:
|
|
320
|
-
|
|
432
|
+
fn = node.asEmit();
|
|
433
|
+
break;
|
|
321
434
|
}
|
|
435
|
+
fn.doc = node.doc;
|
|
436
|
+
fn.metadata = node.metadata;
|
|
437
|
+
fn.signalOrigin = node.name;
|
|
438
|
+
return fn.asString(this);
|
|
322
439
|
}
|
|
323
440
|
|
|
324
441
|
generateStaticClassFunction(node: IntrospectedStaticClassFunction): string[] {
|
|
@@ -340,7 +457,11 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
340
457
|
generateProperty(tsProp: IntrospectedProperty, construct?: boolean, indentCount = 0) {
|
|
341
458
|
const desc: string[] = [];
|
|
342
459
|
|
|
343
|
-
|
|
460
|
+
const propTags = [...this.namespace.getTsDocMetadataTags(tsProp.metadata)];
|
|
461
|
+
if (tsProp.constructOnly) propTags.push({ tagName: "construct-only", paramName: "", text: "" });
|
|
462
|
+
else if (tsProp.readable && !tsProp.writable) propTags.push({ tagName: "read-only", paramName: "", text: "" });
|
|
463
|
+
else if (tsProp.writable && !tsProp.readable) propTags.push({ tagName: "write-only", paramName: "", text: "" });
|
|
464
|
+
desc.push(...this.addGirDocComment(tsProp.doc, propTags, indentCount));
|
|
344
465
|
|
|
345
466
|
const indent = generateIndent(indentCount);
|
|
346
467
|
const name = generateMemberName(tsProp);
|
|
@@ -421,7 +542,7 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
421
542
|
const desc: string[] = [];
|
|
422
543
|
const isStatic = tsProp.isStatic;
|
|
423
544
|
|
|
424
|
-
desc.push(...this.addGirDocComment(tsProp.doc,
|
|
545
|
+
desc.push(...this.addGirDocComment(tsProp.doc, this.namespace.getTsDocMetadataTags(tsProp.metadata), indentCount));
|
|
425
546
|
|
|
426
547
|
const indent = generateIndent(indentCount);
|
|
427
548
|
const name = generateMemberName(tsProp);
|
|
@@ -532,9 +653,9 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
532
653
|
return desc;
|
|
533
654
|
}
|
|
534
655
|
|
|
535
|
-
const text = tsDoc ? transformGirDocText(tsDoc) : null;
|
|
656
|
+
const text = tsDoc ? transformGirDocText(tsDoc, this._docContext) : null;
|
|
536
657
|
|
|
537
|
-
if (text) {
|
|
658
|
+
if (text || tags.length) {
|
|
538
659
|
desc.push(`${indent}/**`);
|
|
539
660
|
|
|
540
661
|
if (text) {
|
|
@@ -547,10 +668,13 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
547
668
|
}
|
|
548
669
|
|
|
549
670
|
for (const tag of tags) {
|
|
671
|
+
const tagText = tag.text ? transformGirDocTagTextWithContext(tag.text, this._docContext) : "";
|
|
550
672
|
if (tag.paramName) {
|
|
551
|
-
desc.push(`${indent} * @${tag.tagName} ${tag.paramName} ${
|
|
673
|
+
desc.push(`${indent} * @${tag.tagName} ${tag.paramName} ${tagText}`);
|
|
674
|
+
} else if (tagText) {
|
|
675
|
+
desc.push(`${indent} * @${tag.tagName} ${tagText}`);
|
|
552
676
|
} else {
|
|
553
|
-
desc.push(`${indent} * @${tag.tagName}
|
|
677
|
+
desc.push(`${indent} * @${tag.tagName}`);
|
|
554
678
|
}
|
|
555
679
|
}
|
|
556
680
|
desc.push(`${indent} */`);
|
|
@@ -558,6 +682,29 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
558
682
|
return desc;
|
|
559
683
|
}
|
|
560
684
|
|
|
685
|
+
private getGirTypeTags(
|
|
686
|
+
obj: IntrospectedClass | IntrospectedRecord | IntrospectedInterface | IntrospectedCallback | IntrospectedAlias,
|
|
687
|
+
): TsDocTag[] {
|
|
688
|
+
let girType: string;
|
|
689
|
+
|
|
690
|
+
if (obj instanceof IntrospectedRecord) {
|
|
691
|
+
if (obj.structFor) girType = "Class Struct";
|
|
692
|
+
else if (obj.isForeign()) girType = "Foreign Struct";
|
|
693
|
+
else girType = "Struct";
|
|
694
|
+
} else if (obj instanceof IntrospectedInterface) {
|
|
695
|
+
girType = "Interface";
|
|
696
|
+
} else if (obj instanceof IntrospectedClass) {
|
|
697
|
+
girType = "Class";
|
|
698
|
+
} else if (obj instanceof IntrospectedCallback) {
|
|
699
|
+
girType = "Callback";
|
|
700
|
+
} else {
|
|
701
|
+
// IntrospectedAlias
|
|
702
|
+
girType = "Alias";
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return [{ tagName: "gir-type", paramName: "", text: girType }];
|
|
706
|
+
}
|
|
707
|
+
|
|
561
708
|
/**
|
|
562
709
|
* Adds an info comment, is used for debugging the generated types
|
|
563
710
|
* @param comment
|
|
@@ -693,17 +840,23 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
693
840
|
|
|
694
841
|
const { parameters: inParams } = tsFunction;
|
|
695
842
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
843
|
+
def.push(
|
|
844
|
+
...this.addGirDocComment(
|
|
845
|
+
tsFunction.doc,
|
|
846
|
+
[
|
|
847
|
+
...this.namespace.getTsDocInParamTags(tsFunction.parameters),
|
|
848
|
+
...this.namespace.getTsDocReturnTags(tsFunction),
|
|
849
|
+
...this.namespace.getTsDocMetadataTags(tsFunction.metadata),
|
|
850
|
+
...(tsFunction instanceof IntrospectedVirtualClassFunction
|
|
851
|
+
? [{ tagName: "virtual", paramName: "", text: "" } as const]
|
|
852
|
+
: []),
|
|
853
|
+
...("signalOrigin" in tsFunction && tsFunction.signalOrigin
|
|
854
|
+
? [{ tagName: "signal", paramName: "", text: "" } as const]
|
|
855
|
+
: []),
|
|
856
|
+
],
|
|
857
|
+
indentCount,
|
|
858
|
+
),
|
|
859
|
+
);
|
|
707
860
|
|
|
708
861
|
const warning = tsFunction.getWarning();
|
|
709
862
|
if (warning) def.push(warning);
|
|
@@ -775,7 +928,11 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
775
928
|
) {
|
|
776
929
|
const def: string[] = [];
|
|
777
930
|
|
|
778
|
-
|
|
931
|
+
const callbackTags =
|
|
932
|
+
tsCallback instanceof IntrospectedCallback && !(tsCallback instanceof IntrospectedClassCallback)
|
|
933
|
+
? [...this.getGirTypeTags(tsCallback), ...this.namespace.getTsDocMetadataTags(tsCallback.metadata)]
|
|
934
|
+
: this.namespace.getTsDocMetadataTags(tsCallback.metadata);
|
|
935
|
+
def.push(...this.addGirDocComment(tsCallback.doc, callbackTags, indentCount));
|
|
779
936
|
|
|
780
937
|
const indent = generateIndent(indentCount);
|
|
781
938
|
const indentBody = generateIndent(indentCount + 1);
|
|
@@ -835,6 +992,8 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
835
992
|
}
|
|
836
993
|
|
|
837
994
|
if (girEnum.isRegistered) {
|
|
995
|
+
const nsTags = [{ tagName: "gir-type", paramName: "", text: girEnum.flags ? "Flags" : "Enum" } as const];
|
|
996
|
+
desc.push(...this.addGirDocComment(null, nsTags, indentCount));
|
|
838
997
|
desc.push(`export namespace ${name} {`);
|
|
839
998
|
const gtypeNamespace = namespace.namespace === "GObject" ? "" : "GObject.";
|
|
840
999
|
desc.push(` export const $gtype: ${gtypeNamespace}GType<${name}>;`);
|
|
@@ -842,7 +1001,11 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
842
1001
|
desc.push("");
|
|
843
1002
|
}
|
|
844
1003
|
|
|
845
|
-
|
|
1004
|
+
const enumTags = [
|
|
1005
|
+
{ tagName: "gir-type", paramName: "", text: girEnum.flags ? "Flags" : "Enum" } as const,
|
|
1006
|
+
...this.namespace.getTsDocMetadataTags(girEnum.metadata),
|
|
1007
|
+
];
|
|
1008
|
+
desc.push(...this.addGirDocComment(girEnum.doc, enumTags, indentCount));
|
|
846
1009
|
desc.push(this.generateExport("enum", name, "{", indentCount));
|
|
847
1010
|
if (girEnum.members) {
|
|
848
1011
|
for (const girEnumMember of girEnum.members.values()) {
|
|
@@ -875,7 +1038,9 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
875
1038
|
generateConst(tsConst: IntrospectedConstant, indentCount = 0) {
|
|
876
1039
|
const desc: string[] = [];
|
|
877
1040
|
|
|
878
|
-
desc.push(
|
|
1041
|
+
desc.push(
|
|
1042
|
+
...this.addGirDocComment(tsConst.doc, this.namespace.getTsDocMetadataTags(tsConst.metadata), indentCount),
|
|
1043
|
+
);
|
|
879
1044
|
|
|
880
1045
|
const indent = generateIndent(indentCount);
|
|
881
1046
|
const exp = !this.config.noNamespace ? "" : "export ";
|
|
@@ -892,6 +1057,14 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
892
1057
|
|
|
893
1058
|
const desc: string[] = [];
|
|
894
1059
|
|
|
1060
|
+
desc.push(
|
|
1061
|
+
...this.addGirDocComment(
|
|
1062
|
+
girAlias.doc,
|
|
1063
|
+
[...this.getGirTypeTags(girAlias), ...this.namespace.getTsDocMetadataTags(girAlias.metadata)],
|
|
1064
|
+
indentCount,
|
|
1065
|
+
),
|
|
1066
|
+
);
|
|
1067
|
+
|
|
895
1068
|
const indent = generateIndent(indentCount);
|
|
896
1069
|
|
|
897
1070
|
const genericList = girAlias.generics
|
|
@@ -1000,33 +1173,6 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
1000
1173
|
return def;
|
|
1001
1174
|
}
|
|
1002
1175
|
|
|
1003
|
-
generateClassSignalsProperty(girClass: IntrospectedClass | IntrospectedRecord, indentCount = 1) {
|
|
1004
|
-
const def: string[] = [];
|
|
1005
|
-
|
|
1006
|
-
// Add instance $signals property for type-safe signal access (compile-time only)
|
|
1007
|
-
const isGObjectObject = girClass.name === "Object" && girClass.namespace.namespace === "GObject";
|
|
1008
|
-
const hasGObjectParent =
|
|
1009
|
-
isGObjectObject ||
|
|
1010
|
-
girClass.someParent((p: IntrospectedBaseClass) => p.namespace.namespace === "GObject" && p.name === "Object");
|
|
1011
|
-
|
|
1012
|
-
if (hasGObjectParent) {
|
|
1013
|
-
def.push(
|
|
1014
|
-
"",
|
|
1015
|
-
`${generateIndent(indentCount)}/**`,
|
|
1016
|
-
`${generateIndent(indentCount)} * Compile-time signal type information.`,
|
|
1017
|
-
`${generateIndent(indentCount)} *`,
|
|
1018
|
-
`${generateIndent(indentCount)} * This instance property is generated only for TypeScript type checking.`,
|
|
1019
|
-
`${generateIndent(indentCount)} * It is not defined at runtime and should not be accessed in JS code.`,
|
|
1020
|
-
`${generateIndent(indentCount)} * @internal`,
|
|
1021
|
-
`${generateIndent(indentCount)} */`,
|
|
1022
|
-
`${generateIndent(indentCount)}$signals: ${girClass.name}.SignalSignatures;`,
|
|
1023
|
-
"",
|
|
1024
|
-
);
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
return def;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
1176
|
generateClassMemberFields(girClass: IntrospectedClass | IntrospectedRecord | IntrospectedInterface, indentCount = 1) {
|
|
1031
1177
|
const def: string[] = [];
|
|
1032
1178
|
|
|
@@ -1272,219 +1418,6 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
1272
1418
|
return `${method.name}(${params}):${returnType}`;
|
|
1273
1419
|
}
|
|
1274
1420
|
|
|
1275
|
-
generateClassSignalInterfaces(girClass: IntrospectedClass, indentCount = 0) {
|
|
1276
|
-
const def: string[] = [];
|
|
1277
|
-
const _tsSignals = girClass.signals;
|
|
1278
|
-
|
|
1279
|
-
// No separate callback interfaces generated. All callback types are inlined directly in SignalSignatures.
|
|
1280
|
-
|
|
1281
|
-
// Always generate SignalSignatures interface for proper inheritance
|
|
1282
|
-
def.push(...this.generateSignalSignatures(girClass, indentCount));
|
|
1283
|
-
|
|
1284
|
-
return def;
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
/**
|
|
1288
|
-
* Generate SignalSignatures interface for type-safe signal handling
|
|
1289
|
-
*
|
|
1290
|
-
* This creates a comprehensive mapping of signal names to their callback types,
|
|
1291
|
-
* enabling TypeScript to provide proper type checking and IntelliSense for
|
|
1292
|
-
* GObject signals using the centralized getAllSignals() method from the model.
|
|
1293
|
-
*/
|
|
1294
|
-
generateSignalSignatures(girClass: IntrospectedClass, indentCount = 0): string[] {
|
|
1295
|
-
const def: string[] = [];
|
|
1296
|
-
const indent = generateIndent(indentCount);
|
|
1297
|
-
|
|
1298
|
-
// Generate SignalSignatures interface to maintain type inheritance chain
|
|
1299
|
-
def.push(`${indent}// Signal signatures`);
|
|
1300
|
-
def.push(`${indent}interface SignalSignatures`);
|
|
1301
|
-
|
|
1302
|
-
// Build inheritance chain for signal signatures
|
|
1303
|
-
const parentSignatures: string[] = [];
|
|
1304
|
-
|
|
1305
|
-
// Inherit signal signatures from parent class
|
|
1306
|
-
const parentResolution = girClass.resolveParents().extends();
|
|
1307
|
-
if (parentResolution && parentResolution.node instanceof IntrospectedClass) {
|
|
1308
|
-
const parentClass = parentResolution.node as IntrospectedClass;
|
|
1309
|
-
const parentTypeIdentifier = parentResolution.identifier
|
|
1310
|
-
.resolveIdentifier(this.namespace, this.config)
|
|
1311
|
-
?.print(this.namespace, this.config);
|
|
1312
|
-
|
|
1313
|
-
// Include parent signals unless it's a template workaround class
|
|
1314
|
-
const hasSignalMethods = parentClass.signals?.length > 0;
|
|
1315
|
-
const isNotTemplateWorkaround = !(
|
|
1316
|
-
this.namespace.namespace === "Gimp" && ["ParamObject", "ParamItem", "ParamArray"].includes(parentClass.name)
|
|
1317
|
-
);
|
|
1318
|
-
|
|
1319
|
-
if (parentTypeIdentifier && (hasSignalMethods || isNotTemplateWorkaround)) {
|
|
1320
|
-
parentSignatures.push(`${parentTypeIdentifier}.SignalSignatures`);
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
|
|
1324
|
-
// Inherit signal signatures from implemented interfaces
|
|
1325
|
-
const interfaceSignatures = girClass
|
|
1326
|
-
.resolveParents()
|
|
1327
|
-
.implements()
|
|
1328
|
-
.filter((iface) => iface.node instanceof IntrospectedInterface)
|
|
1329
|
-
.filter((iface) => {
|
|
1330
|
-
// Only include interfaces that actually define signals
|
|
1331
|
-
const node = iface.node as unknown as { signals?: unknown[] };
|
|
1332
|
-
return node.signals && node.signals.length > 0;
|
|
1333
|
-
})
|
|
1334
|
-
.map((iface) => {
|
|
1335
|
-
const interfaceTypeIdentifier = iface.identifier
|
|
1336
|
-
.resolveIdentifier(this.namespace, this.config)
|
|
1337
|
-
?.print(this.namespace, this.config);
|
|
1338
|
-
return interfaceTypeIdentifier ? `${interfaceTypeIdentifier}.SignalSignatures` : null;
|
|
1339
|
-
})
|
|
1340
|
-
.filter((sig): sig is string => !!sig);
|
|
1341
|
-
|
|
1342
|
-
parentSignatures.push(...interfaceSignatures);
|
|
1343
|
-
|
|
1344
|
-
// Apply inheritance or fallback to base GObject signals
|
|
1345
|
-
if (parentSignatures.length > 0) {
|
|
1346
|
-
def.push(` extends ${parentSignatures.join(", ")} {`);
|
|
1347
|
-
} else {
|
|
1348
|
-
// Handle root GObject.Object class to avoid circular references
|
|
1349
|
-
const isGObjectObject = girClass.name === "Object" && girClass.namespace.namespace === "GObject";
|
|
1350
|
-
|
|
1351
|
-
if (isGObjectObject) {
|
|
1352
|
-
def.push(" {");
|
|
1353
|
-
} else {
|
|
1354
|
-
// All other classes inherit from GObject.Object's signal signatures as fallback
|
|
1355
|
-
const gobjectNamespace = this.namespace.assertInstalledImport("GObject");
|
|
1356
|
-
const gobjectObjectClass = gobjectNamespace.assertClass("Object");
|
|
1357
|
-
const gobjectRef = gobjectObjectClass
|
|
1358
|
-
.getType()
|
|
1359
|
-
.resolveIdentifier(this.namespace, this.config)
|
|
1360
|
-
?.print(this.namespace, this.config);
|
|
1361
|
-
|
|
1362
|
-
const fallbackRef = gobjectRef ? `${gobjectRef}.SignalSignatures` : "GObject.Object.SignalSignatures";
|
|
1363
|
-
def.push(` extends ${fallbackRef} {`);
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
// Use the centralized getAllSignals method from the model
|
|
1368
|
-
const allSignals = girClass.getAllSignals();
|
|
1369
|
-
|
|
1370
|
-
allSignals.forEach((signalInfo) => {
|
|
1371
|
-
// Ensure valid TypeScript property names by quoting invalid identifiers
|
|
1372
|
-
const signalKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(signalInfo.name) ? signalInfo.name : `"${signalInfo.name}"`;
|
|
1373
|
-
|
|
1374
|
-
let cbType: string;
|
|
1375
|
-
|
|
1376
|
-
if (signalInfo.isNotifySignal) {
|
|
1377
|
-
// Property notification signals have a standard signature
|
|
1378
|
-
const gobjectRef = this.namespace.namespace === "GObject" ? "" : "GObject.";
|
|
1379
|
-
cbType = `(pspec: ${gobjectRef}ParamSpec) => void`;
|
|
1380
|
-
} else if (signalInfo.signal) {
|
|
1381
|
-
// Regular signals - use the signal's parameters and return type
|
|
1382
|
-
const paramTypes = signalInfo.signal.parameters
|
|
1383
|
-
.map((p, idx) => `arg${idx}: ${this.generateType(p.type)}`)
|
|
1384
|
-
.join(", ");
|
|
1385
|
-
|
|
1386
|
-
// For boolean return types, allow boolean | void for flexibility
|
|
1387
|
-
let returnType = signalInfo.signal.return_type;
|
|
1388
|
-
if (signalInfo.signal.return_type.equals(BooleanType)) {
|
|
1389
|
-
returnType = new BinaryType(BooleanType, VoidType);
|
|
1390
|
-
}
|
|
1391
|
-
const returnTypeStr = this.generateType(returnType);
|
|
1392
|
-
|
|
1393
|
-
cbType = `(${paramTypes}) => ${returnTypeStr}`;
|
|
1394
|
-
} else {
|
|
1395
|
-
// Fallback for custom signal types
|
|
1396
|
-
const paramTypes = signalInfo.parameterTypes?.map((type, idx) => `arg${idx}: ${type}`) || [];
|
|
1397
|
-
const returnTypeStr = signalInfo.returnType || "void";
|
|
1398
|
-
cbType = `(${paramTypes.join(", ")}) => ${returnTypeStr}`;
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
|
-
def.push(`${indent} ${signalKey}: ${cbType};`);
|
|
1402
|
-
});
|
|
1403
|
-
|
|
1404
|
-
def.push(`${indent}}`);
|
|
1405
|
-
def.push("");
|
|
1406
|
-
|
|
1407
|
-
return def;
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
generateSignals(girClass: IntrospectedClass) {
|
|
1411
|
-
// Create IntrospectedClassFunction instances for the signal methods
|
|
1412
|
-
// These represent the GObject signal methods that we want to generate
|
|
1413
|
-
const signalFunctions = [
|
|
1414
|
-
new IntrospectedClassFunction({
|
|
1415
|
-
name: "connect",
|
|
1416
|
-
parent: girClass,
|
|
1417
|
-
parameters: [],
|
|
1418
|
-
return_type: NumberType,
|
|
1419
|
-
}),
|
|
1420
|
-
new IntrospectedClassFunction({
|
|
1421
|
-
name: "connect_after",
|
|
1422
|
-
parent: girClass,
|
|
1423
|
-
parameters: [],
|
|
1424
|
-
return_type: NumberType,
|
|
1425
|
-
}),
|
|
1426
|
-
new IntrospectedClassFunction({
|
|
1427
|
-
name: "emit",
|
|
1428
|
-
parent: girClass,
|
|
1429
|
-
parameters: [],
|
|
1430
|
-
return_type: VoidType,
|
|
1431
|
-
}),
|
|
1432
|
-
];
|
|
1433
|
-
|
|
1434
|
-
// Filter out signal methods that conflict with existing methods in the class or parent classes
|
|
1435
|
-
// For example, if a class already has a connect() method (like Camel.Service), we don't generate
|
|
1436
|
-
// the signal connect() method to avoid conflicts
|
|
1437
|
-
const filteredFunctions = filterConflicts(girClass.namespace, girClass, signalFunctions, FilterBehavior.DELETE);
|
|
1438
|
-
|
|
1439
|
-
// Get the names of methods that should be kept (non-conflicting)
|
|
1440
|
-
const allowedNames = new Set(filteredFunctions.map((f) => f.name));
|
|
1441
|
-
|
|
1442
|
-
const gobjectRef = this.namespace.namespace === "GObject" ? "" : "GObject.";
|
|
1443
|
-
|
|
1444
|
-
// Generate only the non-conflicting type-safe signal methods
|
|
1445
|
-
const methods: string[] = [];
|
|
1446
|
-
|
|
1447
|
-
if (allowedNames.has("connect")) {
|
|
1448
|
-
methods.push(
|
|
1449
|
-
// Type-safe overload for known signals
|
|
1450
|
-
`connect<K extends keyof ${girClass.name}.SignalSignatures>(signal: K, callback: ${gobjectRef}SignalCallback<this, ${girClass.name}.SignalSignatures[K]>): number;`,
|
|
1451
|
-
// Fallback overload for dynamic signals
|
|
1452
|
-
"connect(signal: string, callback: (...args: any[]) => any): number;",
|
|
1453
|
-
);
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
|
-
if (allowedNames.has("connect_after")) {
|
|
1457
|
-
methods.push(
|
|
1458
|
-
// Type-safe overload for known signals
|
|
1459
|
-
`connect_after<K extends keyof ${girClass.name}.SignalSignatures>(signal: K, callback: ${gobjectRef}SignalCallback<this, ${girClass.name}.SignalSignatures[K]>): number;`,
|
|
1460
|
-
// Fallback overload for dynamic signals
|
|
1461
|
-
"connect_after(signal: string, callback: (...args: any[]) => any): number;",
|
|
1462
|
-
);
|
|
1463
|
-
}
|
|
1464
|
-
|
|
1465
|
-
if (allowedNames.has("emit")) {
|
|
1466
|
-
// Fix: Use a conditional type to extract parameters from the signal signature
|
|
1467
|
-
methods.push(
|
|
1468
|
-
// Type-safe overload for known signals
|
|
1469
|
-
`emit<K extends keyof ${girClass.name}.SignalSignatures>(signal: K, ...args: ${gobjectRef}GjsParameters<${girClass.name}.SignalSignatures[K]> extends [any, ...infer Q] ? Q : never): void;`,
|
|
1470
|
-
// Fallback overload for dynamic signals
|
|
1471
|
-
"emit(signal: string, ...args: any[]): void;",
|
|
1472
|
-
);
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
return methods;
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
|
-
generateClassSignals(girClass: IntrospectedClass) {
|
|
1479
|
-
const def: string[] = [];
|
|
1480
|
-
|
|
1481
|
-
const signalDescs = this.generateSignals(girClass);
|
|
1482
|
-
|
|
1483
|
-
def.push(...mergeDescs(signalDescs, "Signals", 1));
|
|
1484
|
-
|
|
1485
|
-
return def;
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
1421
|
generateClassNamespaces(girClass: IntrospectedClass | IntrospectedRecord | IntrospectedInterface, indentCount = 0) {
|
|
1489
1422
|
const def: string[] = [];
|
|
1490
1423
|
const bodyDef: string[] = [];
|
|
@@ -1495,7 +1428,7 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
1495
1428
|
|
|
1496
1429
|
if (girClass instanceof IntrospectedClass) {
|
|
1497
1430
|
// Signal interfaces
|
|
1498
|
-
bodyDef.push(...this.generateClassSignalInterfaces(girClass, indentCount + 1));
|
|
1431
|
+
bodyDef.push(...this.signalGenerator.generateClassSignalInterfaces(girClass, indentCount + 1));
|
|
1499
1432
|
}
|
|
1500
1433
|
|
|
1501
1434
|
if (girClass instanceof IntrospectedInterface) {
|
|
@@ -1580,6 +1513,18 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
1580
1513
|
|
|
1581
1514
|
const ext = implementationNames.length ? ` extends ${implementationNames.join(", ")}` : "";
|
|
1582
1515
|
const interfaceHead = `${girClass.name}${genericParameters}${ext}`;
|
|
1516
|
+
|
|
1517
|
+
// Add @gir-type doc comment for interfaces (classes/records handle this in generateClass)
|
|
1518
|
+
if (girClass instanceof IntrospectedInterface) {
|
|
1519
|
+
def.push(
|
|
1520
|
+
...this.addGirDocComment(
|
|
1521
|
+
girClass.doc,
|
|
1522
|
+
[...this.getGirTypeTags(girClass), ...this.namespace.getTsDocMetadataTags(girClass.metadata)],
|
|
1523
|
+
0,
|
|
1524
|
+
),
|
|
1525
|
+
);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1583
1528
|
def.push(this.generateExport("interface", interfaceHead, "{"));
|
|
1584
1529
|
|
|
1585
1530
|
if (girClass.__ts__indexSignature) {
|
|
@@ -1745,7 +1690,13 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
1745
1690
|
|
|
1746
1691
|
def.push(...this.generateClassNamespaces(girClass));
|
|
1747
1692
|
|
|
1748
|
-
def.push(
|
|
1693
|
+
def.push(
|
|
1694
|
+
...this.addGirDocComment(
|
|
1695
|
+
girClass.doc,
|
|
1696
|
+
[...this.getGirTypeTags(girClass), ...this.namespace.getTsDocMetadataTags(girClass.metadata)],
|
|
1697
|
+
0,
|
|
1698
|
+
),
|
|
1699
|
+
);
|
|
1749
1700
|
|
|
1750
1701
|
const genericParameters = this.generateGenericParameters(girClass.generics);
|
|
1751
1702
|
const ext = this.extends(girClass);
|
|
@@ -1774,7 +1725,7 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
1774
1725
|
def.push(...this.generateClassProperties(girClass));
|
|
1775
1726
|
|
|
1776
1727
|
// $signals property (instance property for type-safe signal access)
|
|
1777
|
-
def.push(...this.generateClassSignalsProperty(girClass));
|
|
1728
|
+
def.push(...this.signalGenerator.generateClassSignalsProperty(girClass));
|
|
1778
1729
|
|
|
1779
1730
|
// Static and member Fields
|
|
1780
1731
|
def.push(...this.generateClassFields(girClass));
|
|
@@ -1784,7 +1735,7 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
1784
1735
|
|
|
1785
1736
|
if (girClass instanceof IntrospectedClass) {
|
|
1786
1737
|
// Signals
|
|
1787
|
-
def.push(...this.generateClassSignals(girClass));
|
|
1738
|
+
def.push(...this.signalGenerator.generateClassSignals(girClass));
|
|
1788
1739
|
}
|
|
1789
1740
|
|
|
1790
1741
|
// Static Methods
|
|
@@ -1797,27 +1748,45 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
1797
1748
|
def.push(...this.generateClassMethods(girClass));
|
|
1798
1749
|
|
|
1799
1750
|
if (girClass instanceof IntrospectedClass) {
|
|
1800
|
-
const
|
|
1801
|
-
const
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
const
|
|
1806
|
-
|
|
1807
|
-
girClass
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1751
|
+
const rawProperties = girClass.implementedProperties();
|
|
1752
|
+
const rawMethods = girClass.implementedMethods(rawProperties);
|
|
1753
|
+
const selfName = `${girClass.namespace.namespace}.${girClass.name}`;
|
|
1754
|
+
|
|
1755
|
+
// Group inherited properties by source interface
|
|
1756
|
+
const propsBySource = groupBySource(rawProperties);
|
|
1757
|
+
for (const [source, props] of propsBySource) {
|
|
1758
|
+
const copied = props.map((p) => p.copy({ parent: girClass }));
|
|
1759
|
+
for (const m of filterConflicts(girClass.namespace, girClass, copied)) {
|
|
1760
|
+
const memberLines = m.asString(this);
|
|
1761
|
+
if (memberLines.length > 0) {
|
|
1762
|
+
// Only tag as inherited if source is a different class
|
|
1763
|
+
if (source !== selfName) {
|
|
1764
|
+
injectInheritedTags(memberLines, source);
|
|
1765
|
+
}
|
|
1766
|
+
def.push(...memberLines);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1819
1770
|
|
|
1820
|
-
|
|
1771
|
+
// Group inherited methods by source interface
|
|
1772
|
+
const methodsBySource = groupBySource(rawMethods);
|
|
1773
|
+
for (const [source, methods] of methodsBySource) {
|
|
1774
|
+
const copied = methods.map((m) => m.copy({ parent: girClass }));
|
|
1775
|
+
const filtered = promisifyIfEnabled(
|
|
1776
|
+
this.options,
|
|
1777
|
+
filterFunctionConflict(girClass.namespace, girClass, copied, []),
|
|
1778
|
+
);
|
|
1779
|
+
for (const m of filtered) {
|
|
1780
|
+
const memberLines = m.asString(this);
|
|
1781
|
+
if (memberLines.length > 0) {
|
|
1782
|
+
// Only tag as inherited if source is a different class
|
|
1783
|
+
if (source !== selfName) {
|
|
1784
|
+
injectInheritedTags(memberLines, source);
|
|
1785
|
+
}
|
|
1786
|
+
def.push(...memberLines);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1821
1790
|
}
|
|
1822
1791
|
// END BODY
|
|
1823
1792
|
|
|
@@ -1901,126 +1870,6 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
1901
1870
|
}
|
|
1902
1871
|
}
|
|
1903
1872
|
|
|
1904
|
-
async exportModuleIndexJS(): Promise<void> {
|
|
1905
|
-
const template = "index.js";
|
|
1906
|
-
const target = "index.js";
|
|
1907
|
-
|
|
1908
|
-
if (this.config.outdir) {
|
|
1909
|
-
await this.moduleTemplateProcessor.create(template, this.config.outdir, target);
|
|
1910
|
-
} else {
|
|
1911
|
-
const { append, prepend } = await this.moduleTemplateProcessor.load(template);
|
|
1912
|
-
this.log.log(append + prepend);
|
|
1913
|
-
}
|
|
1914
|
-
}
|
|
1915
|
-
|
|
1916
|
-
async exportModuleIndexTS(): Promise<void> {
|
|
1917
|
-
const template = "index.d.ts";
|
|
1918
|
-
const target = "index.d.ts";
|
|
1919
|
-
|
|
1920
|
-
if (this.config.outdir) {
|
|
1921
|
-
await this.moduleTemplateProcessor.create(template, this.config.outdir, target);
|
|
1922
|
-
} else {
|
|
1923
|
-
const { append, prepend } = await this.moduleTemplateProcessor.load(template);
|
|
1924
|
-
this.log.log(append + prepend);
|
|
1925
|
-
}
|
|
1926
|
-
}
|
|
1927
|
-
|
|
1928
|
-
async exportModuleJS(girModule: GirModule): Promise<void> {
|
|
1929
|
-
const template = "module.js";
|
|
1930
|
-
const target = `${girModule.importName}.js`;
|
|
1931
|
-
|
|
1932
|
-
if (this.config.outdir) {
|
|
1933
|
-
await this.moduleTemplateProcessor.create(template, this.config.outdir, target);
|
|
1934
|
-
} else {
|
|
1935
|
-
const { append, prepend } = await this.moduleTemplateProcessor.load(template);
|
|
1936
|
-
this.log.log(append + prepend);
|
|
1937
|
-
}
|
|
1938
|
-
}
|
|
1939
|
-
|
|
1940
|
-
async exportModuleAmbientTS(girModule: GirModule): Promise<void> {
|
|
1941
|
-
const template = "module-ambient.d.ts";
|
|
1942
|
-
const target = `${girModule.importName}-ambient.d.ts`;
|
|
1943
|
-
|
|
1944
|
-
if (this.config.outdir) {
|
|
1945
|
-
await this.moduleTemplateProcessor.create(template, this.config.outdir, target);
|
|
1946
|
-
} else {
|
|
1947
|
-
const { append, prepend } = await this.moduleTemplateProcessor.load(template);
|
|
1948
|
-
this.log.log(append + prepend);
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
|
|
1952
|
-
protected async exportModuleAmbientJS(girModule: GirModule): Promise<void> {
|
|
1953
|
-
const template = "module-ambient.js";
|
|
1954
|
-
const target = `${girModule.importName}-ambient.js`;
|
|
1955
|
-
|
|
1956
|
-
if (this.config.outdir) {
|
|
1957
|
-
await this.moduleTemplateProcessor.create(template, this.config.outdir, target);
|
|
1958
|
-
} else {
|
|
1959
|
-
const { append, prepend } = await this.moduleTemplateProcessor.load(template);
|
|
1960
|
-
this.log.log(append + prepend);
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
|
|
1964
|
-
protected async exportModuleImportTS(girModule: GirModule): Promise<void> {
|
|
1965
|
-
const template = "module-import.d.ts";
|
|
1966
|
-
const target = `${girModule.importName}-import.d.ts`;
|
|
1967
|
-
|
|
1968
|
-
if (this.config.outdir) {
|
|
1969
|
-
await this.moduleTemplateProcessor.create(template, this.config.outdir, target);
|
|
1970
|
-
} else {
|
|
1971
|
-
const { append, prepend } = await this.moduleTemplateProcessor.load(template);
|
|
1972
|
-
this.log.log(append + prepend);
|
|
1973
|
-
}
|
|
1974
|
-
}
|
|
1975
|
-
|
|
1976
|
-
protected async exportModuleImportJS(girModule: GirModule): Promise<void> {
|
|
1977
|
-
const template = "module-import.js";
|
|
1978
|
-
const target = `${girModule.importName}-import.js`;
|
|
1979
|
-
|
|
1980
|
-
if (this.config.outdir) {
|
|
1981
|
-
await this.moduleTemplateProcessor.create(template, this.config.outdir, target);
|
|
1982
|
-
} else {
|
|
1983
|
-
const { append, prepend } = await this.moduleTemplateProcessor.load(template);
|
|
1984
|
-
this.log.log(append + prepend);
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
|
-
|
|
1988
|
-
async exportModuleTS(): Promise<void> {
|
|
1989
|
-
const { namespace: girModule } = this;
|
|
1990
|
-
const template = "module.d.ts";
|
|
1991
|
-
const explicitTemplate = `${girModule.importName}.d.ts`;
|
|
1992
|
-
const target = explicitTemplate;
|
|
1993
|
-
const output = await this.generateModule(girModule);
|
|
1994
|
-
|
|
1995
|
-
if (!output) {
|
|
1996
|
-
this.log.error("Failed to generate gir module");
|
|
1997
|
-
return;
|
|
1998
|
-
}
|
|
1999
|
-
|
|
2000
|
-
// Output is always an array now
|
|
2001
|
-
const outputArray = output;
|
|
2002
|
-
|
|
2003
|
-
// Extra interfaces if a template with the module name (e.g. '../templates/gobject-2-0.d.ts') is found
|
|
2004
|
-
// E.g. used for GObject-2.0 to help define GObject classes in js;
|
|
2005
|
-
// these aren't part of gi.
|
|
2006
|
-
if (await this.moduleTemplateProcessor.exists(explicitTemplate)) {
|
|
2007
|
-
const { append: appendExplicit, prepend: prependExplicit } =
|
|
2008
|
-
await this.moduleTemplateProcessor.load(explicitTemplate);
|
|
2009
|
-
outputArray.unshift(prependExplicit);
|
|
2010
|
-
outputArray.push(appendExplicit);
|
|
2011
|
-
}
|
|
2012
|
-
|
|
2013
|
-
const { append, prepend } = await this.moduleTemplateProcessor.load(template);
|
|
2014
|
-
outputArray.unshift(prepend);
|
|
2015
|
-
outputArray.push(append);
|
|
2016
|
-
|
|
2017
|
-
if (this.config.outdir) {
|
|
2018
|
-
await this.moduleTemplateProcessor.write(outputArray.join("\n"), this.config.outdir, target);
|
|
2019
|
-
} else {
|
|
2020
|
-
this.log.log(outputArray.join("\n"));
|
|
2021
|
-
}
|
|
2022
|
-
}
|
|
2023
|
-
|
|
2024
1873
|
async generateModule(girModule: GirModule): Promise<string[]> {
|
|
2025
1874
|
const out: string[] = [];
|
|
2026
1875
|
|
|
@@ -2153,32 +2002,45 @@ export class ModuleGenerator extends FormatGenerator<string[]> {
|
|
|
2153
2002
|
const result = await this.generateNamespace(girModule);
|
|
2154
2003
|
return result.join("\n");
|
|
2155
2004
|
}
|
|
2005
|
+
}
|
|
2156
2006
|
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2007
|
+
/**
|
|
2008
|
+
* Groups items by their source interface/class name (e.g. "Gtk.Accessible").
|
|
2009
|
+
* Must be called BEFORE copy({ parent: ... }) so the original parent is preserved.
|
|
2010
|
+
*/
|
|
2011
|
+
function groupBySource<T extends { parent: { namespace: { namespace: string }; name: string } }>(
|
|
2012
|
+
items: T[],
|
|
2013
|
+
): Map<string, T[]> {
|
|
2014
|
+
const groups = new Map<string, T[]>();
|
|
2015
|
+
for (const item of items) {
|
|
2016
|
+
const source = `${item.parent.namespace.namespace}.${item.parent.name}`;
|
|
2017
|
+
const list = groups.get(source);
|
|
2018
|
+
if (list) list.push(item);
|
|
2019
|
+
else groups.set(source, [item]);
|
|
2020
|
+
}
|
|
2021
|
+
return groups;
|
|
2022
|
+
}
|
|
2172
2023
|
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2024
|
+
/**
|
|
2025
|
+
* Injects a `@category` TSDoc tag into generated member strings.
|
|
2026
|
+
* Places the member in a subcategory "Inherited from X" within its kind group,
|
|
2027
|
+
* so inherited members appear grouped after own members.
|
|
2028
|
+
*/
|
|
2029
|
+
function injectInheritedTags(lines: string[], source: string): void {
|
|
2030
|
+
const category = `Inherited from ${source}`;
|
|
2031
|
+
// Search backwards — `*/` is typically on the last or second-to-last line
|
|
2032
|
+
let closingIdx = -1;
|
|
2033
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2034
|
+
if (lines[i].trimEnd().endsWith("*/")) {
|
|
2035
|
+
closingIdx = i;
|
|
2036
|
+
break;
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
if (closingIdx >= 0) {
|
|
2040
|
+
const indent = lines[closingIdx].match(/^(\s*)/)?.[1] ?? "";
|
|
2041
|
+
lines.splice(closingIdx, 0, `${indent} * @category ${category}`);
|
|
2042
|
+
} else {
|
|
2043
|
+
lines.unshift(`/** @category ${category} */`);
|
|
2182
2044
|
}
|
|
2183
2045
|
}
|
|
2184
2046
|
|
|
@@ -84,6 +84,9 @@ export class TypeDefinitionGenerator implements Generator {
|
|
|
84
84
|
await templateProcessor.create("gjs/console.d.ts", config.outdir, "console.d.ts");
|
|
85
85
|
await templateProcessor.create("gjs/console.js", config.outdir, "console.js");
|
|
86
86
|
|
|
87
|
+
await templateProcessor.create("gjs/gi.d.ts", config.outdir, "gi.d.ts");
|
|
88
|
+
await templateProcessor.create("gjs/gi.js", config.outdir, "gi.js");
|
|
89
|
+
|
|
87
90
|
// Import ambient types
|
|
88
91
|
await templateProcessor.create("gjs/gjs-ambient.d.ts", config.outdir, "gjs-ambient.d.ts");
|
|
89
92
|
await templateProcessor.create("gjs/gjs-ambient.js", config.outdir, "gjs-ambient.js");
|
|
@@ -114,6 +117,9 @@ export class TypeDefinitionGenerator implements Generator {
|
|
|
114
117
|
"console.d.ts",
|
|
115
118
|
);
|
|
116
119
|
|
|
120
|
+
const giContent = await templateProcessor.load("gjs/gi.d.ts");
|
|
121
|
+
await templateProcessor.write(`${giContent.prepend}\n${giContent.append}`, config.outdir, "gi.d.ts");
|
|
122
|
+
|
|
117
123
|
// Additional DOM types supported by GJS
|
|
118
124
|
const domContent = await templateProcessor.load("gjs/dom.d.ts");
|
|
119
125
|
await templateProcessor.write(`${domContent.prepend}\n${domContent.append}`, config.outdir, "dom.d.ts");
|
|
@@ -144,7 +150,7 @@ export class TypeDefinitionGenerator implements Generator {
|
|
|
144
150
|
|
|
145
151
|
public async generate(module: GirModule) {
|
|
146
152
|
const moduleGenerator = new ModuleGenerator(module, this.config, this.registry);
|
|
147
|
-
await moduleGenerator.exportModule(this.registry, module);
|
|
153
|
+
await moduleGenerator.moduleExporter.exportModule(this.registry, module);
|
|
148
154
|
}
|
|
149
155
|
|
|
150
156
|
public async start() {
|