@gtkx/gir 0.18.0 → 0.18.2
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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/normalizer.d.ts +1 -0
- package/dist/internal/normalizer.d.ts.map +1 -0
- package/dist/internal/normalizer.js +1 -0
- package/dist/internal/normalizer.js.map +1 -0
- package/dist/internal/parser.d.ts +1 -0
- package/dist/internal/parser.d.ts.map +1 -0
- package/dist/internal/parser.js +1 -0
- package/dist/internal/parser.js.map +1 -0
- package/dist/internal/raw-types.d.ts +1 -0
- package/dist/internal/raw-types.d.ts.map +1 -0
- package/dist/internal/raw-types.js +1 -0
- package/dist/internal/raw-types.js.map +1 -0
- package/dist/intrinsics.d.ts +1 -0
- package/dist/intrinsics.d.ts.map +1 -0
- package/dist/intrinsics.js +1 -0
- package/dist/intrinsics.js.map +1 -0
- package/dist/repository.d.ts +1 -0
- package/dist/repository.d.ts.map +1 -0
- package/dist/repository.js +1 -0
- package/dist/repository.js.map +1 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +1 -0
- package/dist/utils.js.map +1 -0
- package/package.json +4 -2
- package/src/index.ts +64 -0
- package/src/internal/normalizer.ts +551 -0
- package/src/internal/parser.ts +633 -0
- package/src/internal/raw-types.ts +268 -0
- package/src/intrinsics.ts +129 -0
- package/src/repository.ts +406 -0
- package/src/types.ts +1192 -0
- package/src/utils.ts +12 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GIR Repository - Central entry point for GIR data.
|
|
3
|
+
*
|
|
4
|
+
* Loads, normalizes, and provides query access to GIR namespaces.
|
|
5
|
+
* All type references are normalized to fully qualified names.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { type NormalizerContext, normalizeNamespace } from "./internal/normalizer.js";
|
|
11
|
+
import { type ParserOptions, RawGirParser } from "./internal/parser.js";
|
|
12
|
+
import type { RawNamespace } from "./internal/raw-types.js";
|
|
13
|
+
import { isIntrinsicType } from "./intrinsics.js";
|
|
14
|
+
import {
|
|
15
|
+
type GirAlias,
|
|
16
|
+
type GirCallback,
|
|
17
|
+
type GirClass,
|
|
18
|
+
type GirConstant,
|
|
19
|
+
type GirEnumeration,
|
|
20
|
+
type GirFunction,
|
|
21
|
+
type GirInterface,
|
|
22
|
+
type GirNamespace,
|
|
23
|
+
type GirRecord,
|
|
24
|
+
type GirType,
|
|
25
|
+
parseQualifiedName,
|
|
26
|
+
type QualifiedName,
|
|
27
|
+
type TypeKind,
|
|
28
|
+
} from "./types.js";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Central repository for GIR data.
|
|
32
|
+
*
|
|
33
|
+
* Loads, normalizes, and provides query access to GIR namespaces.
|
|
34
|
+
* All type references are normalized to fully qualified names.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const repo = new GirRepository();
|
|
39
|
+
* await repo.loadFromDirectory("./girs");
|
|
40
|
+
* repo.resolve();
|
|
41
|
+
*
|
|
42
|
+
* const gtkNs = repo.getNamespace("Gtk");
|
|
43
|
+
* const buttonClass = repo.resolveClass("Gtk.Button");
|
|
44
|
+
* const chain = buttonClass.getInheritanceChain();
|
|
45
|
+
* // ["Gtk.Button", "Gtk.Widget", "GObject.InitiallyUnowned", "GObject.Object"]
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export type RepositoryOptions = ParserOptions;
|
|
49
|
+
|
|
50
|
+
export class GirRepository {
|
|
51
|
+
private rawNamespaces = new Map<string, RawNamespace>();
|
|
52
|
+
private normalizedNamespaces = new Map<string, GirNamespace>();
|
|
53
|
+
private parser: RawGirParser;
|
|
54
|
+
private resolved = false;
|
|
55
|
+
|
|
56
|
+
constructor(options: RepositoryOptions = {}) {
|
|
57
|
+
this.parser = new RawGirParser(options);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Loads a single GIR file from XML content.
|
|
62
|
+
*/
|
|
63
|
+
loadFromXml(xml: string): void {
|
|
64
|
+
const raw = this.parser.parse(xml);
|
|
65
|
+
this.rawNamespaces.set(raw.name, raw);
|
|
66
|
+
this.resolved = false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Loads a GIR file from disk.
|
|
71
|
+
*/
|
|
72
|
+
async loadFromFile(path: string): Promise<void> {
|
|
73
|
+
const xml = await readFile(path, "utf-8");
|
|
74
|
+
this.loadFromXml(xml);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Loads all .gir files from a directory.
|
|
79
|
+
*/
|
|
80
|
+
async loadFromDirectory(dirPath: string): Promise<void> {
|
|
81
|
+
const files = await readdir(dirPath);
|
|
82
|
+
const girFiles = files.filter((f) => f.endsWith(".gir"));
|
|
83
|
+
|
|
84
|
+
await Promise.all(girFiles.map((file) => this.loadFromFile(join(dirPath, file))));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* After loading all raw GIR files, normalize and resolve all references.
|
|
89
|
+
* This must be called after loading and before querying.
|
|
90
|
+
*/
|
|
91
|
+
resolve(): void {
|
|
92
|
+
if (this.resolved) return;
|
|
93
|
+
|
|
94
|
+
const ctx: NormalizerContext = {
|
|
95
|
+
rawNamespaces: this.rawNamespaces,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
for (const [name, raw] of this.rawNamespaces) {
|
|
99
|
+
const normalized = normalizeNamespace(raw, ctx);
|
|
100
|
+
this.normalizedNamespaces.set(name, normalized);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
for (const ns of this.normalizedNamespaces.values()) {
|
|
104
|
+
for (const cls of ns.classes.values()) {
|
|
105
|
+
cls._setRepository(this);
|
|
106
|
+
}
|
|
107
|
+
for (const iface of ns.interfaces.values()) {
|
|
108
|
+
iface._setRepository(this);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.resolved = true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Gets all loaded namespace names.
|
|
117
|
+
*/
|
|
118
|
+
getNamespaceNames(): string[] {
|
|
119
|
+
this.ensureResolved();
|
|
120
|
+
return [...this.normalizedNamespaces.keys()];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Gets a normalized namespace by name.
|
|
125
|
+
*/
|
|
126
|
+
getNamespace(name: string): GirNamespace | null {
|
|
127
|
+
this.ensureResolved();
|
|
128
|
+
return this.normalizedNamespaces.get(name) ?? null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Gets all normalized namespaces.
|
|
133
|
+
*/
|
|
134
|
+
getAllNamespaces(): Map<string, GirNamespace> {
|
|
135
|
+
this.ensureResolved();
|
|
136
|
+
return this.normalizedNamespaces;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Resolves a class by qualified name.
|
|
141
|
+
* @example repo.resolveClass("Gtk.Button" as QualifiedName)
|
|
142
|
+
*/
|
|
143
|
+
resolveClass(qualifiedName: QualifiedName): GirClass | null {
|
|
144
|
+
this.ensureResolved();
|
|
145
|
+
const { namespace, name } = parseQualifiedName(qualifiedName);
|
|
146
|
+
return this.normalizedNamespaces.get(namespace)?.classes.get(name) ?? null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Resolves an interface by qualified name.
|
|
151
|
+
* @example repo.resolveInterface("Gio.ListModel" as QualifiedName)
|
|
152
|
+
*/
|
|
153
|
+
resolveInterface(qualifiedName: QualifiedName): GirInterface | null {
|
|
154
|
+
this.ensureResolved();
|
|
155
|
+
const { namespace, name } = parseQualifiedName(qualifiedName);
|
|
156
|
+
return this.normalizedNamespaces.get(namespace)?.interfaces.get(name) ?? null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Resolves a record (boxed type) by qualified name.
|
|
161
|
+
* @example repo.resolveRecord("Gdk.Rectangle" as QualifiedName)
|
|
162
|
+
*/
|
|
163
|
+
resolveRecord(qualifiedName: QualifiedName): GirRecord | null {
|
|
164
|
+
this.ensureResolved();
|
|
165
|
+
const { namespace, name } = parseQualifiedName(qualifiedName);
|
|
166
|
+
return this.normalizedNamespaces.get(namespace)?.records.get(name) ?? null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Resolves an enumeration by qualified name.
|
|
171
|
+
* @example repo.resolveEnum("Gtk.Orientation" as QualifiedName)
|
|
172
|
+
*/
|
|
173
|
+
resolveEnum(qualifiedName: QualifiedName): GirEnumeration | null {
|
|
174
|
+
this.ensureResolved();
|
|
175
|
+
const { namespace, name } = parseQualifiedName(qualifiedName);
|
|
176
|
+
return this.normalizedNamespaces.get(namespace)?.enumerations.get(name) ?? null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Resolves a bitfield (flags) by qualified name.
|
|
181
|
+
* @example repo.resolveFlags("Gdk.ModifierType" as QualifiedName)
|
|
182
|
+
*/
|
|
183
|
+
resolveFlags(qualifiedName: QualifiedName): GirEnumeration | null {
|
|
184
|
+
this.ensureResolved();
|
|
185
|
+
const { namespace, name } = parseQualifiedName(qualifiedName);
|
|
186
|
+
return this.normalizedNamespaces.get(namespace)?.bitfields.get(name) ?? null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Resolves a callback type by qualified name.
|
|
191
|
+
* @example repo.resolveCallback("Gio.AsyncReadyCallback" as QualifiedName)
|
|
192
|
+
*/
|
|
193
|
+
resolveCallback(qualifiedName: QualifiedName): GirCallback | null {
|
|
194
|
+
this.ensureResolved();
|
|
195
|
+
const { namespace, name } = parseQualifiedName(qualifiedName);
|
|
196
|
+
return this.normalizedNamespaces.get(namespace)?.callbacks.get(name) ?? null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Resolves a constant by qualified name.
|
|
201
|
+
* @example repo.resolveConstant("Gtk.MAJOR_VERSION" as QualifiedName)
|
|
202
|
+
*/
|
|
203
|
+
resolveConstant(qualifiedName: QualifiedName): GirConstant | null {
|
|
204
|
+
this.ensureResolved();
|
|
205
|
+
const { namespace, name } = parseQualifiedName(qualifiedName);
|
|
206
|
+
return this.normalizedNamespaces.get(namespace)?.constants.get(name) ?? null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Resolves a standalone function by qualified name.
|
|
211
|
+
* @example repo.resolveFunction("Gtk.init" as QualifiedName)
|
|
212
|
+
*/
|
|
213
|
+
resolveFunction(qualifiedName: QualifiedName): GirFunction | null {
|
|
214
|
+
this.ensureResolved();
|
|
215
|
+
const { namespace, name } = parseQualifiedName(qualifiedName);
|
|
216
|
+
return this.normalizedNamespaces.get(namespace)?.functions.get(name) ?? null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Resolves an alias by qualified name.
|
|
221
|
+
* @example repo.resolveAlias("Pango.LayoutRun" as QualifiedName)
|
|
222
|
+
*/
|
|
223
|
+
resolveAlias(qualifiedName: QualifiedName): GirAlias | null {
|
|
224
|
+
this.ensureResolved();
|
|
225
|
+
const { namespace, name } = parseQualifiedName(qualifiedName);
|
|
226
|
+
return this.normalizedNamespaces.get(namespace)?.aliases.get(name) ?? null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Resolves a type name through aliases to get the target type.
|
|
231
|
+
* If the type is an alias, returns the target type.
|
|
232
|
+
* If not an alias, returns null.
|
|
233
|
+
* @example repo.resolveTypeAlias("Pango.LayoutRun" as QualifiedName) // returns GirType pointing to Pango.GlyphItem
|
|
234
|
+
*/
|
|
235
|
+
resolveTypeAlias(qualifiedName: QualifiedName): GirType | null {
|
|
236
|
+
const alias = this.resolveAlias(qualifiedName);
|
|
237
|
+
return alias?.targetType ?? null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Gets the kind of a type (class, interface, record, enum, flags, callback).
|
|
242
|
+
* Returns null for intrinsic types or unknown types.
|
|
243
|
+
*/
|
|
244
|
+
getTypeKind(qualifiedName: QualifiedName): TypeKind | null {
|
|
245
|
+
this.ensureResolved();
|
|
246
|
+
|
|
247
|
+
if (isIntrinsicType(qualifiedName)) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const { namespace, name } = parseQualifiedName(qualifiedName);
|
|
252
|
+
const ns = this.normalizedNamespaces.get(namespace);
|
|
253
|
+
if (!ns) return null;
|
|
254
|
+
|
|
255
|
+
if (ns.classes.has(name)) return "class";
|
|
256
|
+
if (ns.interfaces.has(name)) return "interface";
|
|
257
|
+
if (ns.records.has(name)) return "record";
|
|
258
|
+
if (ns.enumerations.has(name)) return "enum";
|
|
259
|
+
if (ns.bitfields.has(name)) return "flags";
|
|
260
|
+
if (ns.callbacks.has(name)) return "callback";
|
|
261
|
+
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Gets the full inheritance chain for a class.
|
|
267
|
+
* Returns array from most derived to base.
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* getInheritanceChain("Gtk.Button" as QualifiedName)
|
|
271
|
+
* // ["Gtk.Button", "Gtk.Widget", "GObject.InitiallyUnowned", "GObject.Object"]
|
|
272
|
+
*/
|
|
273
|
+
getInheritanceChain(qualifiedName: QualifiedName): QualifiedName[] {
|
|
274
|
+
const cls = this.resolveClass(qualifiedName);
|
|
275
|
+
return cls?.getInheritanceChain() ?? [];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Gets all interfaces implemented by a class (including inherited).
|
|
280
|
+
*/
|
|
281
|
+
getImplementedInterfaces(qualifiedName: QualifiedName): QualifiedName[] {
|
|
282
|
+
const cls = this.resolveClass(qualifiedName);
|
|
283
|
+
return cls?.getAllImplementedInterfaces() ?? [];
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Gets all classes that derive from a given class.
|
|
288
|
+
*/
|
|
289
|
+
getDerivedClasses(qualifiedName: QualifiedName): QualifiedName[] {
|
|
290
|
+
this.ensureResolved();
|
|
291
|
+
const derived: QualifiedName[] = [];
|
|
292
|
+
|
|
293
|
+
for (const ns of this.normalizedNamespaces.values()) {
|
|
294
|
+
for (const cls of ns.classes.values()) {
|
|
295
|
+
if (cls.qualifiedName !== qualifiedName && cls.isSubclassOf(qualifiedName)) {
|
|
296
|
+
derived.push(cls.qualifiedName);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return derived;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Gets all classes that implement a given interface.
|
|
306
|
+
*/
|
|
307
|
+
getImplementors(interfaceName: QualifiedName): QualifiedName[] {
|
|
308
|
+
this.ensureResolved();
|
|
309
|
+
const implementors: QualifiedName[] = [];
|
|
310
|
+
|
|
311
|
+
for (const ns of this.normalizedNamespaces.values()) {
|
|
312
|
+
for (const cls of ns.classes.values()) {
|
|
313
|
+
if (cls.implementsInterface(interfaceName)) {
|
|
314
|
+
implementors.push(cls.qualifiedName);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return implementors;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Checks if a type is a GObject (class with GType).
|
|
324
|
+
*/
|
|
325
|
+
isGObject(qualifiedName: QualifiedName): boolean {
|
|
326
|
+
const cls = this.resolveClass(qualifiedName);
|
|
327
|
+
return cls?.hasGType() ?? false;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Checks if a type is a boxed type (record with GType).
|
|
332
|
+
*/
|
|
333
|
+
isBoxed(qualifiedName: QualifiedName): boolean {
|
|
334
|
+
const record = this.resolveRecord(qualifiedName);
|
|
335
|
+
return record?.isBoxed() ?? false;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Checks if a type is a primitive (intrinsic).
|
|
340
|
+
*/
|
|
341
|
+
isPrimitive(typeName: string): boolean {
|
|
342
|
+
return isIntrinsicType(typeName);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Finds all classes matching a predicate across all namespaces.
|
|
347
|
+
*/
|
|
348
|
+
findClasses(predicate: (cls: GirClass) => boolean): GirClass[] {
|
|
349
|
+
this.ensureResolved();
|
|
350
|
+
const results: GirClass[] = [];
|
|
351
|
+
|
|
352
|
+
for (const ns of this.normalizedNamespaces.values()) {
|
|
353
|
+
for (const cls of ns.classes.values()) {
|
|
354
|
+
if (predicate(cls)) {
|
|
355
|
+
results.push(cls);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return results;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Finds all interfaces matching a predicate across all namespaces.
|
|
365
|
+
*/
|
|
366
|
+
findInterfaces(predicate: (iface: GirInterface) => boolean): GirInterface[] {
|
|
367
|
+
this.ensureResolved();
|
|
368
|
+
const results: GirInterface[] = [];
|
|
369
|
+
|
|
370
|
+
for (const ns of this.normalizedNamespaces.values()) {
|
|
371
|
+
for (const iface of ns.interfaces.values()) {
|
|
372
|
+
if (predicate(iface)) {
|
|
373
|
+
results.push(iface);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return results;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Finds all records matching a predicate across all namespaces.
|
|
383
|
+
*/
|
|
384
|
+
findRecords(predicate: (record: GirRecord) => boolean): GirRecord[] {
|
|
385
|
+
this.ensureResolved();
|
|
386
|
+
const results: GirRecord[] = [];
|
|
387
|
+
|
|
388
|
+
for (const ns of this.normalizedNamespaces.values()) {
|
|
389
|
+
for (const record of ns.records.values()) {
|
|
390
|
+
if (predicate(record)) {
|
|
391
|
+
results.push(record);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return results;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
private ensureResolved(): void {
|
|
400
|
+
if (!this.resolved) {
|
|
401
|
+
throw new Error(
|
|
402
|
+
"GirRepository.resolve() must be called before querying. Call resolve() after loading all GIR files.",
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|