@gtkx/codegen 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +373 -0
- package/dist/dsl/context.d.ts +104 -0
- package/dist/dsl/context.d.ts.map +1 -0
- package/dist/dsl/context.js +140 -0
- package/dist/dsl/context.js.map +1 -0
- package/dist/dsl/emit.d.ts +28 -0
- package/dist/dsl/emit.d.ts.map +1 -0
- package/dist/dsl/emit.js +41 -0
- package/dist/dsl/emit.js.map +1 -0
- package/dist/dsl/identifier.d.ts +28 -0
- package/dist/dsl/identifier.d.ts.map +1 -0
- package/dist/dsl/identifier.js +45 -0
- package/dist/dsl/identifier.js.map +1 -0
- package/dist/dsl/imports.d.ts +58 -0
- package/dist/dsl/imports.d.ts.map +1 -0
- package/dist/dsl/imports.js +107 -0
- package/dist/dsl/imports.js.map +1 -0
- package/dist/dsl/module.d.ts +53 -0
- package/dist/dsl/module.d.ts.map +1 -0
- package/dist/dsl/module.js +76 -0
- package/dist/dsl/module.js.map +1 -0
- package/dist/ffi/pipeline.d.ts +19 -0
- package/dist/ffi/pipeline.d.ts.map +1 -0
- package/dist/ffi/pipeline.js +107 -0
- package/dist/ffi/pipeline.js.map +1 -0
- package/dist/fingerprint.d.ts +30 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +33 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/gi-store.d.ts +34 -0
- package/dist/gi-store.d.ts.map +1 -0
- package/dist/gi-store.js +154 -0
- package/dist/gi-store.js.map +1 -0
- package/dist/gir/boxed.d.ts +66 -0
- package/dist/gir/boxed.d.ts.map +1 -0
- package/dist/gir/boxed.js +49 -0
- package/dist/gir/boxed.js.map +1 -0
- package/dist/gir/callback.d.ts +15 -0
- package/dist/gir/callback.d.ts.map +1 -0
- package/dist/gir/callback.js +17 -0
- package/dist/gir/callback.js.map +1 -0
- package/dist/gir/class.d.ts +49 -0
- package/dist/gir/class.d.ts.map +1 -0
- package/dist/gir/class.js +42 -0
- package/dist/gir/class.js.map +1 -0
- package/dist/gir/enum.d.ts +39 -0
- package/dist/gir/enum.d.ts.map +1 -0
- package/dist/gir/enum.js +24 -0
- package/dist/gir/enum.js.map +1 -0
- package/dist/gir/field.d.ts +27 -0
- package/dist/gir/field.d.ts.map +1 -0
- package/dist/gir/field.js +19 -0
- package/dist/gir/field.js.map +1 -0
- package/dist/gir/function.d.ts +47 -0
- package/dist/gir/function.d.ts.map +1 -0
- package/dist/gir/function.js +28 -0
- package/dist/gir/function.js.map +1 -0
- package/dist/gir/namespace.d.ts +65 -0
- package/dist/gir/namespace.d.ts.map +1 -0
- package/dist/gir/namespace.js +60 -0
- package/dist/gir/namespace.js.map +1 -0
- package/dist/gir/parameter.d.ts +82 -0
- package/dist/gir/parameter.d.ts.map +1 -0
- package/dist/gir/parameter.js +66 -0
- package/dist/gir/parameter.js.map +1 -0
- package/dist/gir/parse.d.ts +78 -0
- package/dist/gir/parse.d.ts.map +1 -0
- package/dist/gir/parse.js +155 -0
- package/dist/gir/parse.js.map +1 -0
- package/dist/gir/primitives.d.ts +36 -0
- package/dist/gir/primitives.d.ts.map +1 -0
- package/dist/gir/primitives.js +109 -0
- package/dist/gir/primitives.js.map +1 -0
- package/dist/gir/property.d.ts +29 -0
- package/dist/gir/property.d.ts.map +1 -0
- package/dist/gir/property.js +16 -0
- package/dist/gir/property.js.map +1 -0
- package/dist/gir/qualified-name.d.ts +37 -0
- package/dist/gir/qualified-name.d.ts.map +1 -0
- package/dist/gir/qualified-name.js +39 -0
- package/dist/gir/qualified-name.js.map +1 -0
- package/dist/gir/qualify.d.ts +32 -0
- package/dist/gir/qualify.d.ts.map +1 -0
- package/dist/gir/qualify.js +60 -0
- package/dist/gir/qualify.js.map +1 -0
- package/dist/gir/repository.d.ts +82 -0
- package/dist/gir/repository.d.ts.map +1 -0
- package/dist/gir/repository.js +122 -0
- package/dist/gir/repository.js.map +1 -0
- package/dist/gir/signal.d.ts +22 -0
- package/dist/gir/signal.d.ts.map +1 -0
- package/dist/gir/signal.js +20 -0
- package/dist/gir/signal.js.map +1 -0
- package/dist/gir/size.d.ts +67 -0
- package/dist/gir/size.d.ts.map +1 -0
- package/dist/gir/size.js +87 -0
- package/dist/gir/size.js.map +1 -0
- package/dist/gir/type-ref.d.ts +79 -0
- package/dist/gir/type-ref.d.ts.map +1 -0
- package/dist/gir/type-ref.js +101 -0
- package/dist/gir/type-ref.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/jsx-store.d.ts +22 -0
- package/dist/jsx-store.d.ts.map +1 -0
- package/dist/jsx-store.js +40 -0
- package/dist/jsx-store.js.map +1 -0
- package/dist/react/compounds-meta.d.ts +18 -0
- package/dist/react/compounds-meta.d.ts.map +1 -0
- package/dist/react/compounds-meta.js +93 -0
- package/dist/react/compounds-meta.js.map +1 -0
- package/dist/react/compounds.d.ts +27 -0
- package/dist/react/compounds.d.ts.map +1 -0
- package/dist/react/compounds.js +164 -0
- package/dist/react/compounds.js.map +1 -0
- package/dist/react/internal.d.ts +14 -0
- package/dist/react/internal.d.ts.map +1 -0
- package/dist/react/internal.js +79 -0
- package/dist/react/internal.js.map +1 -0
- package/dist/react/jsx.d.ts +22 -0
- package/dist/react/jsx.d.ts.map +1 -0
- package/dist/react/jsx.js +134 -0
- package/dist/react/jsx.js.map +1 -0
- package/dist/react/pipeline.d.ts +13 -0
- package/dist/react/pipeline.d.ts.map +1 -0
- package/dist/react/pipeline.js +25 -0
- package/dist/react/pipeline.js.map +1 -0
- package/dist/react/props.d.ts +38 -0
- package/dist/react/props.d.ts.map +1 -0
- package/dist/react/props.js +231 -0
- package/dist/react/props.js.map +1 -0
- package/dist/react/slot-props.d.ts +10 -0
- package/dist/react/slot-props.d.ts.map +1 -0
- package/dist/react/slot-props.js +56 -0
- package/dist/react/slot-props.js.map +1 -0
- package/dist/react/widgets.d.ts +83 -0
- package/dist/react/widgets.d.ts.map +1 -0
- package/dist/react/widgets.js +152 -0
- package/dist/react/widgets.js.map +1 -0
- package/dist/runner.d.ts +55 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +60 -0
- package/dist/runner.js.map +1 -0
- package/dist/store-fs.d.ts +109 -0
- package/dist/store-fs.d.ts.map +1 -0
- package/dist/store-fs.js +98 -0
- package/dist/store-fs.js.map +1 -0
- package/dist/transpile.d.ts +32 -0
- package/dist/transpile.d.ts.map +1 -0
- package/dist/transpile.js +63 -0
- package/dist/transpile.js.map +1 -0
- package/dist/typecheck-store.d.ts +41 -0
- package/dist/typecheck-store.d.ts.map +1 -0
- package/dist/typecheck-store.js +102 -0
- package/dist/typecheck-store.js.map +1 -0
- package/dist/writers/alias.d.ts +13 -0
- package/dist/writers/alias.d.ts.map +1 -0
- package/dist/writers/alias.js +17 -0
- package/dist/writers/alias.js.map +1 -0
- package/dist/writers/async.d.ts +22 -0
- package/dist/writers/async.d.ts.map +1 -0
- package/dist/writers/async.js +48 -0
- package/dist/writers/async.js.map +1 -0
- package/dist/writers/boxed-constructor.d.ts +26 -0
- package/dist/writers/boxed-constructor.d.ts.map +1 -0
- package/dist/writers/boxed-constructor.js +85 -0
- package/dist/writers/boxed-constructor.js.map +1 -0
- package/dist/writers/boxed-field-accessor.d.ts +18 -0
- package/dist/writers/boxed-field-accessor.d.ts.map +1 -0
- package/dist/writers/boxed-field-accessor.js +260 -0
- package/dist/writers/boxed-field-accessor.js.map +1 -0
- package/dist/writers/boxed-layout.d.ts +27 -0
- package/dist/writers/boxed-layout.d.ts.map +1 -0
- package/dist/writers/boxed-layout.js +128 -0
- package/dist/writers/boxed-layout.js.map +1 -0
- package/dist/writers/boxed.d.ts +20 -0
- package/dist/writers/boxed.d.ts.map +1 -0
- package/dist/writers/boxed.js +71 -0
- package/dist/writers/boxed.js.map +1 -0
- package/dist/writers/callables.d.ts +124 -0
- package/dist/writers/callables.d.ts.map +1 -0
- package/dist/writers/callables.js +283 -0
- package/dist/writers/callables.js.map +1 -0
- package/dist/writers/callback.d.ts +16 -0
- package/dist/writers/callback.d.ts.map +1 -0
- package/dist/writers/callback.js +38 -0
- package/dist/writers/callback.js.map +1 -0
- package/dist/writers/class-struct-record.d.ts +31 -0
- package/dist/writers/class-struct-record.d.ts.map +1 -0
- package/dist/writers/class-struct-record.js +67 -0
- package/dist/writers/class-struct-record.js.map +1 -0
- package/dist/writers/class-struct.d.ts +22 -0
- package/dist/writers/class-struct.d.ts.map +1 -0
- package/dist/writers/class-struct.js +85 -0
- package/dist/writers/class-struct.js.map +1 -0
- package/dist/writers/class.d.ts +16 -0
- package/dist/writers/class.d.ts.map +1 -0
- package/dist/writers/class.js +307 -0
- package/dist/writers/class.js.map +1 -0
- package/dist/writers/constant.d.ts +15 -0
- package/dist/writers/constant.d.ts.map +1 -0
- package/dist/writers/constant.js +23 -0
- package/dist/writers/constant.js.map +1 -0
- package/dist/writers/constants.d.ts +15 -0
- package/dist/writers/constants.d.ts.map +1 -0
- package/dist/writers/constants.js +23 -0
- package/dist/writers/constants.js.map +1 -0
- package/dist/writers/constructor-props.d.ts +32 -0
- package/dist/writers/constructor-props.d.ts.map +1 -0
- package/dist/writers/constructor-props.js +110 -0
- package/dist/writers/constructor-props.js.map +1 -0
- package/dist/writers/enum.d.ts +21 -0
- package/dist/writers/enum.d.ts.map +1 -0
- package/dist/writers/enum.js +48 -0
- package/dist/writers/enum.js.map +1 -0
- package/dist/writers/function.d.ts +40 -0
- package/dist/writers/function.d.ts.map +1 -0
- package/dist/writers/function.js +127 -0
- package/dist/writers/function.js.map +1 -0
- package/dist/writers/gtype-binding.d.ts +19 -0
- package/dist/writers/gtype-binding.d.ts.map +1 -0
- package/dist/writers/gtype-binding.js +40 -0
- package/dist/writers/gtype-binding.js.map +1 -0
- package/dist/writers/inheritance.d.ts +48 -0
- package/dist/writers/inheritance.d.ts.map +1 -0
- package/dist/writers/inheritance.js +90 -0
- package/dist/writers/inheritance.js.map +1 -0
- package/dist/writers/interface.d.ts +16 -0
- package/dist/writers/interface.d.ts.map +1 -0
- package/dist/writers/interface.js +155 -0
- package/dist/writers/interface.js.map +1 -0
- package/dist/writers/method.d.ts +150 -0
- package/dist/writers/method.d.ts.map +1 -0
- package/dist/writers/method.js +510 -0
- package/dist/writers/method.js.map +1 -0
- package/dist/writers/param-classify.d.ts +93 -0
- package/dist/writers/param-classify.d.ts.map +1 -0
- package/dist/writers/param-classify.js +163 -0
- package/dist/writers/param-classify.js.map +1 -0
- package/dist/writers/property-accessor.d.ts +19 -0
- package/dist/writers/property-accessor.d.ts.map +1 -0
- package/dist/writers/property-accessor.js +117 -0
- package/dist/writers/property-accessor.js.map +1 -0
- package/dist/writers/registration.d.ts +27 -0
- package/dist/writers/registration.d.ts.map +1 -0
- package/dist/writers/registration.js +30 -0
- package/dist/writers/registration.js.map +1 -0
- package/dist/writers/return-wrap.d.ts +39 -0
- package/dist/writers/return-wrap.d.ts.map +1 -0
- package/dist/writers/return-wrap.js +152 -0
- package/dist/writers/return-wrap.js.map +1 -0
- package/dist/writers/runtime-override.d.ts +18 -0
- package/dist/writers/runtime-override.d.ts.map +1 -0
- package/dist/writers/runtime-override.js +44 -0
- package/dist/writers/runtime-override.js.map +1 -0
- package/dist/writers/signal.d.ts +21 -0
- package/dist/writers/signal.d.ts.map +1 -0
- package/dist/writers/signal.js +260 -0
- package/dist/writers/signal.js.map +1 -0
- package/dist/writers/ts-type.d.ts +50 -0
- package/dist/writers/ts-type.d.ts.map +1 -0
- package/dist/writers/ts-type.js +75 -0
- package/dist/writers/ts-type.js.map +1 -0
- package/dist/writers/types-ts.d.ts +27 -0
- package/dist/writers/types-ts.d.ts.map +1 -0
- package/dist/writers/types-ts.js +97 -0
- package/dist/writers/types-ts.js.map +1 -0
- package/dist/writers/value.d.ts +89 -0
- package/dist/writers/value.d.ts.map +1 -0
- package/dist/writers/value.js +402 -0
- package/dist/writers/value.js.map +1 -0
- package/package.json +55 -0
- package/src/dsl/context.ts +146 -0
- package/src/dsl/emit.ts +42 -0
- package/src/dsl/identifier.ts +52 -0
- package/src/dsl/imports.ts +109 -0
- package/src/dsl/module.ts +75 -0
- package/src/ffi/pipeline.ts +113 -0
- package/src/fingerprint.ts +50 -0
- package/src/gi-store.ts +199 -0
- package/src/gir/boxed.ts +94 -0
- package/src/gir/callback.ts +26 -0
- package/src/gir/class.ts +79 -0
- package/src/gir/enum.ts +55 -0
- package/src/gir/field.ts +40 -0
- package/src/gir/function.ts +67 -0
- package/src/gir/namespace.ts +118 -0
- package/src/gir/parameter.ts +120 -0
- package/src/gir/parse.ts +171 -0
- package/src/gir/primitives.ts +137 -0
- package/src/gir/property.ts +39 -0
- package/src/gir/qualified-name.ts +55 -0
- package/src/gir/qualify.ts +64 -0
- package/src/gir/repository.ts +149 -0
- package/src/gir/signal.ts +36 -0
- package/src/gir/size.ts +129 -0
- package/src/gir/type-ref.ts +180 -0
- package/src/index.ts +16 -0
- package/src/jsx-store.ts +53 -0
- package/src/react/compounds-meta.ts +99 -0
- package/src/react/compounds.ts +204 -0
- package/src/react/internal.ts +91 -0
- package/src/react/jsx.ts +155 -0
- package/src/react/pipeline.ts +29 -0
- package/src/react/props.ts +307 -0
- package/src/react/slot-props.ts +57 -0
- package/src/react/widgets.ts +185 -0
- package/src/runner.ts +89 -0
- package/src/store-fs.ts +157 -0
- package/src/transpile.ts +80 -0
- package/src/typecheck-store.ts +136 -0
- package/src/writers/alias.ts +19 -0
- package/src/writers/async.ts +44 -0
- package/src/writers/boxed-constructor.ts +105 -0
- package/src/writers/boxed-field-accessor.ts +306 -0
- package/src/writers/boxed-layout.ts +166 -0
- package/src/writers/boxed.ts +77 -0
- package/src/writers/callables.ts +339 -0
- package/src/writers/callback.ts +40 -0
- package/src/writers/class-struct-record.ts +75 -0
- package/src/writers/class-struct.ts +96 -0
- package/src/writers/class.ts +384 -0
- package/src/writers/constant.ts +29 -0
- package/src/writers/constructor-props.ts +130 -0
- package/src/writers/enum.ts +53 -0
- package/src/writers/function.ts +136 -0
- package/src/writers/gtype-binding.ts +48 -0
- package/src/writers/inheritance.ts +104 -0
- package/src/writers/interface.ts +168 -0
- package/src/writers/method.ts +678 -0
- package/src/writers/param-classify.ts +178 -0
- package/src/writers/property-accessor.ts +139 -0
- package/src/writers/registration.ts +42 -0
- package/src/writers/return-wrap.ts +180 -0
- package/src/writers/runtime-override.ts +59 -0
- package/src/writers/signal.ts +318 -0
- package/src/writers/ts-type.ts +115 -0
- package/src/writers/value.ts +485 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import { toCamelCase, toPascalCase } from "@gtkx/utils";
|
|
2
|
+
import type { ModuleContext } from "../dsl/context.js";
|
|
3
|
+
import { indent } from "../dsl/emit.js";
|
|
4
|
+
import type { GirClass } from "../gir/class.js";
|
|
5
|
+
import type { GirFunction } from "../gir/function.js";
|
|
6
|
+
import { splitQualifiedName } from "../gir/qualified-name.js";
|
|
7
|
+
import { qualifyFunction, qualifyTypeRef } from "../gir/qualify.js";
|
|
8
|
+
import type { GirTypeRef } from "../gir/type-ref.js";
|
|
9
|
+
import { matchAsyncFinishName } from "./async.js";
|
|
10
|
+
import {
|
|
11
|
+
appendMethodBinding,
|
|
12
|
+
appendShadowedAliases,
|
|
13
|
+
type Callables,
|
|
14
|
+
dedupeCallables,
|
|
15
|
+
emitBindings,
|
|
16
|
+
indexMethodsByName,
|
|
17
|
+
renderInstanceMethod,
|
|
18
|
+
renderStaticHead,
|
|
19
|
+
} from "./callables.js";
|
|
20
|
+
import { renderVfuncMetadata } from "./class-struct.js";
|
|
21
|
+
import { renderClassConstructor, renderConstructorPropsInterface } from "./constructor-props.js";
|
|
22
|
+
import { renderGetTypeReference } from "./gtype-binding.js";
|
|
23
|
+
import { collectInterfaceProperties, forEachAncestor, resolveImplementedInterface } from "./inheritance.js";
|
|
24
|
+
import { methodExportName, renderPromisifiedBody, renderPromisifiedSignature } from "./method.js";
|
|
25
|
+
import { inputParameters } from "./param-classify.js";
|
|
26
|
+
import { renderPropertyAccessor } from "./property-accessor.js";
|
|
27
|
+
import { appendNativeClassRegistration } from "./registration.js";
|
|
28
|
+
import { renderRuntimeOverride } from "./runtime-override.js";
|
|
29
|
+
import { renderSignalMembers } from "./signal.js";
|
|
30
|
+
import { renderTsType } from "./ts-type.js";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Emits a full class declaration for a `<class>` element.
|
|
34
|
+
*
|
|
35
|
+
* Walks the class's constructors, static functions, and methods, emitting
|
|
36
|
+
* each as a JS method body on the class declaration plus a hoisted `const`
|
|
37
|
+
* binding for the corresponding FFI symbol. Properties and signals are
|
|
38
|
+
* surfaced by their own writers; this function owns the class shape and
|
|
39
|
+
* the GType / class-struct registration calls.
|
|
40
|
+
*
|
|
41
|
+
* @param context - The module context
|
|
42
|
+
* @param klass - The class to emit
|
|
43
|
+
*/
|
|
44
|
+
export const emitClass = (context: ModuleContext, klass: GirClass): void => {
|
|
45
|
+
if (!klass.introspectable) return;
|
|
46
|
+
if (klass.name.length === 0) return;
|
|
47
|
+
const className = toPascalCase(klass.name);
|
|
48
|
+
const callables: Callables = {
|
|
49
|
+
constructors: dedupeCallables(klass.constructors),
|
|
50
|
+
functions: dedupeCallables(klass.functions),
|
|
51
|
+
methods: dedupeCallables(klass.methods),
|
|
52
|
+
};
|
|
53
|
+
emitBindings(context, callables);
|
|
54
|
+
|
|
55
|
+
const parentExpression = resolveParent(context, klass);
|
|
56
|
+
const extendsClause = parentExpression === undefined ? "" : ` extends ${parentExpression}`;
|
|
57
|
+
const members = renderClassMembers(context, klass, callables, parentExpression !== undefined);
|
|
58
|
+
const body = members.map((member) => indent(member, 1)).join("\n\n");
|
|
59
|
+
context.module.appendDeclaration(`export class ${className}${extendsClause} {\n${body}\n}`);
|
|
60
|
+
context.module.appendDeclaration(renderConstructorPropsInterface(context, klass, className));
|
|
61
|
+
|
|
62
|
+
const interfaceRefs = klass.implements
|
|
63
|
+
.map((name) => resolveImplementsReference(context, name))
|
|
64
|
+
.filter((entry): entry is string => entry !== undefined);
|
|
65
|
+
if (interfaceRefs.length > 0) {
|
|
66
|
+
context.module.appendDeclaration(`export interface ${className} extends ${interfaceRefs.join(", ")} {}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
appendClassRegistrations(context, klass, className);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const renderClassMembers = (
|
|
73
|
+
context: ModuleContext,
|
|
74
|
+
klass: GirClass,
|
|
75
|
+
callables: Callables,
|
|
76
|
+
hasParent: boolean,
|
|
77
|
+
): readonly string[] => {
|
|
78
|
+
const className = toPascalCase(klass.name);
|
|
79
|
+
const members: string[] = ["declare __gtype__: number;"];
|
|
80
|
+
const constructorBlock = renderClassConstructor(context, klass, className, hasParent);
|
|
81
|
+
if (constructorBlock !== undefined) members.push(constructorBlock);
|
|
82
|
+
const claimedNames = new Set<string>();
|
|
83
|
+
members.push(...renderStaticHead(context, callables, className, "gobject"));
|
|
84
|
+
const inherited = collectInheritedMethods(context, klass);
|
|
85
|
+
const methodByName = indexMethodsByName(callables.methods);
|
|
86
|
+
appendInstanceMethods({
|
|
87
|
+
context,
|
|
88
|
+
methods: callables.methods,
|
|
89
|
+
methodByName,
|
|
90
|
+
inherited,
|
|
91
|
+
members,
|
|
92
|
+
claimedNames,
|
|
93
|
+
className,
|
|
94
|
+
});
|
|
95
|
+
appendShadowedAliases({ context, methods: callables.methods, methodByName, members, claimedNames });
|
|
96
|
+
appendFlattenedInterfaceMethods({ context, klass, inheritedNames: inherited.names, members, claimedNames });
|
|
97
|
+
for (const property of klass.properties) {
|
|
98
|
+
const block = renderPropertyAccessor(context, property, claimedNames, methodByName);
|
|
99
|
+
if (block !== undefined) members.push(block);
|
|
100
|
+
}
|
|
101
|
+
for (const property of collectInterfaceProperties(context, klass)) {
|
|
102
|
+
const block = renderPropertyAccessor(context, property, claimedNames, methodByName);
|
|
103
|
+
if (block !== undefined) members.push(block);
|
|
104
|
+
}
|
|
105
|
+
members.push(...renderSignalMembers(context, klass));
|
|
106
|
+
return members;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
type AppendInstanceMethodsOptions = {
|
|
110
|
+
readonly context: ModuleContext;
|
|
111
|
+
readonly methods: readonly GirFunction[];
|
|
112
|
+
readonly methodByName: ReadonlyMap<string, GirFunction>;
|
|
113
|
+
readonly inherited: InheritedMethods;
|
|
114
|
+
readonly members: string[];
|
|
115
|
+
readonly claimedNames: Set<string>;
|
|
116
|
+
readonly className: string;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const appendInstanceMethods = (options: AppendInstanceMethodsOptions): void => {
|
|
120
|
+
const { context, methods, methodByName, inherited, members, claimedNames, className } = options;
|
|
121
|
+
for (const callable of methods) {
|
|
122
|
+
const rename = conflictRename(context, callable, inherited, className);
|
|
123
|
+
const block = renderClassInstanceMember(context, callable, methodByName, rename);
|
|
124
|
+
if (block === undefined) continue;
|
|
125
|
+
members.push(block);
|
|
126
|
+
claimedNames.add(rename ?? methodExportName(callable));
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The disambiguated name a colliding instance method is emitted under:
|
|
132
|
+
* `<lowerClassName><MethodName>` (e.g. `iconViewSetCursor`).
|
|
133
|
+
*/
|
|
134
|
+
const conflictingMethodName = (className: string, methodName: string): string => {
|
|
135
|
+
const camel = toCamelCase(className);
|
|
136
|
+
return `${camel.charAt(0).toLowerCase()}${camel.slice(1)}${toPascalCase(methodName)}`;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
type AppendFlattenedInterfaceMethodsOptions = {
|
|
140
|
+
readonly context: ModuleContext;
|
|
141
|
+
readonly klass: GirClass;
|
|
142
|
+
readonly inheritedNames: ReadonlySet<string>;
|
|
143
|
+
readonly members: string[];
|
|
144
|
+
readonly claimedNames: Set<string>;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Copies every interface method onto the implementing class.
|
|
149
|
+
*
|
|
150
|
+
* GObject interfaces are structural at the C level: a class that implements
|
|
151
|
+
* `Gdk.Paintable` can be passed to `gdk_paintable_snapshot`, but the JS
|
|
152
|
+
* wrapper only exposes that method if codegen emits it. Each implemented
|
|
153
|
+
* interface's methods are re-rooted to the interface's namespace, bound
|
|
154
|
+
* against the implementing class's shared library (the loader resolves the
|
|
155
|
+
* symbol through that library's dependency graph), and emitted as direct
|
|
156
|
+
* instance members. Names already provided by the class itself or inherited
|
|
157
|
+
* from an ancestor (or an ancestor-implemented interface) are skipped so the
|
|
158
|
+
* most-derived definition wins and no member is declared twice.
|
|
159
|
+
*
|
|
160
|
+
* @param options - {@link AppendFlattenedInterfaceMethodsOptions}
|
|
161
|
+
*/
|
|
162
|
+
const appendFlattenedInterfaceMethods = (options: AppendFlattenedInterfaceMethodsOptions): void => {
|
|
163
|
+
const { context, klass, inheritedNames, members, claimedNames } = options;
|
|
164
|
+
for (const implementName of klass.implements) {
|
|
165
|
+
const iface = resolveImplementedInterface(context, implementName);
|
|
166
|
+
if (iface === undefined) continue;
|
|
167
|
+
const methods = dedupeCallables(iface.klass.methods).map((method) =>
|
|
168
|
+
qualifyFunction(method, iface.namespaceName),
|
|
169
|
+
);
|
|
170
|
+
const methodByName = indexMethodsByName(methods);
|
|
171
|
+
for (const method of methods) {
|
|
172
|
+
const name = methodExportName(method);
|
|
173
|
+
if (name === "constructor") continue;
|
|
174
|
+
if (claimedNames.has(name) || inheritedNames.has(name)) continue;
|
|
175
|
+
const block = renderClassInstanceMember(context, method, methodByName);
|
|
176
|
+
if (block === undefined) continue;
|
|
177
|
+
appendMethodBinding(context, method);
|
|
178
|
+
members.push(block);
|
|
179
|
+
claimedNames.add(name);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const appendClassRegistrations = (context: ModuleContext, klass: GirClass, className: string): void => {
|
|
185
|
+
const getTypeRef =
|
|
186
|
+
klass.glibGetType === undefined
|
|
187
|
+
? undefined
|
|
188
|
+
: renderGetTypeReference(context, klass.glibGetType, klass.glibTypeName);
|
|
189
|
+
appendNativeClassRegistration(context, {
|
|
190
|
+
className,
|
|
191
|
+
role: "class",
|
|
192
|
+
getTypeRef,
|
|
193
|
+
vfuncs: renderVfuncMetadata(context, klass),
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const renderClassInstanceMember = (
|
|
198
|
+
context: ModuleContext,
|
|
199
|
+
callable: GirFunction,
|
|
200
|
+
siblings: ReadonlyMap<string, GirFunction>,
|
|
201
|
+
nameOverride?: string,
|
|
202
|
+
): string | undefined => {
|
|
203
|
+
if (!callable.introspectable || callable.shadowedBy !== undefined || callable.cIdentifier === undefined) {
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
const name = nameOverride ?? methodExportName(callable);
|
|
207
|
+
if (name === "constructor") return undefined;
|
|
208
|
+
const override = renderRuntimeOverride(callable, name);
|
|
209
|
+
if (override !== undefined) return override;
|
|
210
|
+
const promisified = renderPromisifiedMember(context, callable, siblings, name);
|
|
211
|
+
if (promisified !== undefined) return promisified;
|
|
212
|
+
return renderInstanceMethod(context, callable, nameOverride);
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const renderPromisifiedMember = (
|
|
216
|
+
context: ModuleContext,
|
|
217
|
+
callable: GirFunction,
|
|
218
|
+
siblings: ReadonlyMap<string, GirFunction>,
|
|
219
|
+
name: string,
|
|
220
|
+
): string | undefined => {
|
|
221
|
+
const finishName = matchAsyncFinishName(callable, [...siblings.values()]);
|
|
222
|
+
if (finishName === undefined) return undefined;
|
|
223
|
+
const finishFn = siblings.get(finishName);
|
|
224
|
+
if (finishFn === undefined) return undefined;
|
|
225
|
+
const cIdentifier = callable.cIdentifier;
|
|
226
|
+
if (cIdentifier === undefined) return undefined;
|
|
227
|
+
const { signature, returnType } = renderPromisifiedSignature(context, callable, finishFn);
|
|
228
|
+
const finishMember = methodExportName(finishFn);
|
|
229
|
+
const body = renderPromisifiedBody(context, callable, finishMember, cIdentifier);
|
|
230
|
+
return `${name}(${signature}): ${returnType} {\n${indent(body, 1)}\n}`;
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const resolveImplementsReference = (context: ModuleContext, name: string): string | undefined => {
|
|
234
|
+
const { namespaceName, typeName } = splitQualifiedName(name, context.namespace.name);
|
|
235
|
+
const resolved = context.repository.resolveNamed(namespaceName, typeName);
|
|
236
|
+
if (resolved === undefined || resolved.kind !== "interface") return undefined;
|
|
237
|
+
if (namespaceName === context.namespace.name) return toPascalCase(typeName);
|
|
238
|
+
const alias = context.addCrossNamespaceImport(namespaceName);
|
|
239
|
+
return `${alias}.${toPascalCase(typeName)}`;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
/** An ancestor method together with the namespace its type references resolve against. */
|
|
243
|
+
type InheritedMethod = {
|
|
244
|
+
readonly method: GirFunction;
|
|
245
|
+
readonly namespaceName: string;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
type InheritedMethods = {
|
|
249
|
+
/** camelCase method name → its TypeScript return type, for ancestor class methods. */
|
|
250
|
+
readonly returnTypes: ReadonlyMap<string, string>;
|
|
251
|
+
/** camelCase method name → the nearest ancestor definition it overrides. */
|
|
252
|
+
readonly definitions: ReadonlyMap<string, InheritedMethod>;
|
|
253
|
+
/** camelCase names of every method reachable through ancestors and the interfaces they implement. */
|
|
254
|
+
readonly names: ReadonlySet<string>;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
/** Mutable accumulator threaded through ancestor traversal. */
|
|
258
|
+
type InheritedMethodsAccumulator = {
|
|
259
|
+
readonly returnTypes: Map<string, string>;
|
|
260
|
+
readonly definitions: Map<string, InheritedMethod>;
|
|
261
|
+
readonly names: Set<string>;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const collectInheritedMethods = (context: ModuleContext, klass: GirClass): InheritedMethods => {
|
|
265
|
+
const accumulator: InheritedMethodsAccumulator = {
|
|
266
|
+
returnTypes: new Map<string, string>(),
|
|
267
|
+
definitions: new Map<string, InheritedMethod>(),
|
|
268
|
+
names: new Set<string>(),
|
|
269
|
+
};
|
|
270
|
+
forEachAncestor(context, klass, (ancestor) => {
|
|
271
|
+
absorbInheritedMethods(context, ancestor, accumulator);
|
|
272
|
+
absorbInheritedInterfaceMethodNames(context, ancestor, accumulator.names);
|
|
273
|
+
});
|
|
274
|
+
return accumulator;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const absorbInheritedMethods = (
|
|
278
|
+
context: ModuleContext,
|
|
279
|
+
resolved: { readonly klass: GirClass; readonly namespaceName: string },
|
|
280
|
+
accumulator: InheritedMethodsAccumulator,
|
|
281
|
+
): void => {
|
|
282
|
+
const { returnTypes, definitions, names } = accumulator;
|
|
283
|
+
for (const method of resolved.klass.methods) {
|
|
284
|
+
if (!method.introspectable) continue;
|
|
285
|
+
const name = toCamelCase(method.name);
|
|
286
|
+
names.add(name);
|
|
287
|
+
if (returnTypes.has(name)) continue;
|
|
288
|
+
definitions.set(name, { method, namespaceName: resolved.namespaceName });
|
|
289
|
+
const qualifiedType = qualifyTypeRef(method.returnValue.type, resolved.namespaceName);
|
|
290
|
+
returnTypes.set(name, renderTsType(context, qualifiedType, method.returnValue.nullable));
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
const absorbInheritedInterfaceMethodNames = (
|
|
295
|
+
context: ModuleContext,
|
|
296
|
+
ancestor: { readonly klass: GirClass; readonly namespaceName: string },
|
|
297
|
+
names: Set<string>,
|
|
298
|
+
): void => {
|
|
299
|
+
for (const implementName of ancestor.klass.implements) {
|
|
300
|
+
const iface = resolveImplementedInterface(context, implementName, ancestor.namespaceName);
|
|
301
|
+
if (iface === undefined) continue;
|
|
302
|
+
for (const method of iface.klass.methods) {
|
|
303
|
+
if (!method.introspectable) continue;
|
|
304
|
+
names.add(toCamelCase(method.name));
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* The disambiguated name an instance method is emitted under when it collides
|
|
311
|
+
* incompatibly with an inherited method, or `undefined` when no rename applies.
|
|
312
|
+
*
|
|
313
|
+
* A collision is incompatible when the inherited method of the same name has a
|
|
314
|
+
* different return type, a distinct enum at a parameter position, or a
|
|
315
|
+
* different input-parameter arity — each of which would make the override
|
|
316
|
+
* structurally unassignable to its base. The colliding method is renamed so
|
|
317
|
+
* both it and the inherited method stay callable.
|
|
318
|
+
*/
|
|
319
|
+
const conflictRename = (
|
|
320
|
+
context: ModuleContext,
|
|
321
|
+
callable: GirFunction,
|
|
322
|
+
inherited: InheritedMethods,
|
|
323
|
+
className: string,
|
|
324
|
+
): string | undefined => {
|
|
325
|
+
if (!callable.introspectable) return undefined;
|
|
326
|
+
const name = methodExportName(callable);
|
|
327
|
+
const inheritedReturn = inherited.returnTypes.get(name);
|
|
328
|
+
const inheritedMethod = inherited.definitions.get(name);
|
|
329
|
+
if (inheritedReturn === undefined || inheritedMethod === undefined) return undefined;
|
|
330
|
+
const ownReturn = renderTsType(context, callable.returnValue.type, callable.returnValue.nullable);
|
|
331
|
+
const conflicts =
|
|
332
|
+
inheritedReturn !== ownReturn ||
|
|
333
|
+
hasParameterEnumConflict(context, callable, inheritedMethod) ||
|
|
334
|
+
inputParameters(callable).length !== inputParameters(inheritedMethod.method).length;
|
|
335
|
+
return conflicts ? conflictingMethodName(className, callable.name) : undefined;
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Whether an override pairs a distinct enum against the inherited method at
|
|
340
|
+
* any input-parameter position.
|
|
341
|
+
*
|
|
342
|
+
* Numeric enums are mutually assignable with `number`, so a `number`/enum
|
|
343
|
+
* pairing is compatible; two *different* enums are not, which would make the
|
|
344
|
+
* derived class structurally unassignable to its base. Such an override is
|
|
345
|
+
* dropped so the inherited signature stands.
|
|
346
|
+
*
|
|
347
|
+
* @param context - The module context
|
|
348
|
+
* @param own - The override declared on the derived class
|
|
349
|
+
* @param inherited - The nearest ancestor definition of the same name
|
|
350
|
+
*/
|
|
351
|
+
const hasParameterEnumConflict = (context: ModuleContext, own: GirFunction, inherited: InheritedMethod): boolean => {
|
|
352
|
+
const ownParams = inputParameters(own);
|
|
353
|
+
const inheritedParams = inputParameters(inherited.method);
|
|
354
|
+
const count = Math.min(ownParams.length, inheritedParams.length);
|
|
355
|
+
for (let index = 0; index < count; index += 1) {
|
|
356
|
+
const ownParam = ownParams[index];
|
|
357
|
+
const inheritedParam = inheritedParams[index];
|
|
358
|
+
if (ownParam === undefined || inheritedParam === undefined) continue;
|
|
359
|
+
const ownEnum = enumIdentity(context, ownParam.parameter.type, context.namespace.name);
|
|
360
|
+
const inheritedEnum = enumIdentity(context, inheritedParam.parameter.type, inherited.namespaceName);
|
|
361
|
+
if (ownEnum !== undefined && inheritedEnum !== undefined && ownEnum !== inheritedEnum) return true;
|
|
362
|
+
}
|
|
363
|
+
return false;
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const enumIdentity = (
|
|
367
|
+
context: ModuleContext,
|
|
368
|
+
ref: GirTypeRef | undefined,
|
|
369
|
+
defaultNamespace: string,
|
|
370
|
+
): string | undefined => {
|
|
371
|
+
const qualified = qualifyTypeRef(ref, defaultNamespace);
|
|
372
|
+
if (qualified === undefined || qualified.kind !== "named") return undefined;
|
|
373
|
+
const resolved = context.repository.resolveNamed(qualified.namespaceName ?? defaultNamespace, qualified.typeName);
|
|
374
|
+
if (resolved === undefined || resolved.kind !== "enum") return undefined;
|
|
375
|
+
return `${resolved.namespace.name}.${resolved.value.name}`;
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
const resolveParent = (context: ModuleContext, klass: GirClass): string | undefined => {
|
|
379
|
+
if (klass.parent === undefined) return undefined;
|
|
380
|
+
const { namespaceName, typeName } = splitQualifiedName(klass.parent, context.namespace.name);
|
|
381
|
+
if (namespaceName === context.namespace.name) return toPascalCase(typeName);
|
|
382
|
+
const alias = context.addCrossNamespaceImport(namespaceName);
|
|
383
|
+
return `${alias}.${toPascalCase(typeName)}`;
|
|
384
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { quote } from "@gtkx/utils";
|
|
2
|
+
import type { ModuleContext } from "../dsl/context.js";
|
|
3
|
+
import type { GirConstant } from "../gir/namespace.js";
|
|
4
|
+
import type { GirTypeRef } from "../gir/type-ref.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Emits an `export const NAME = value` declaration for a namespace constant.
|
|
8
|
+
*
|
|
9
|
+
* A constant whose declared type marshals to a string (`utf8` / `filename` /
|
|
10
|
+
* `gchararray`) is always quoted — even when its value is all digits, as with
|
|
11
|
+
* `G_CSET_DIGITS = "0123456789"`. Other constants keep the bare-numeral form
|
|
12
|
+
* when the value is a plain number and are quoted otherwise.
|
|
13
|
+
*
|
|
14
|
+
* @param context - The module context
|
|
15
|
+
* @param constant - The constant
|
|
16
|
+
*/
|
|
17
|
+
export const emitConstant = (context: ModuleContext, constant: GirConstant): void => {
|
|
18
|
+
context.module.appendDeclaration(`export const ${constant.name} = ${constantLiteral(constant)};`);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const constantLiteral = (constant: GirConstant): string => {
|
|
22
|
+
if (isStringTyped(constant.type)) return quote(constant.value);
|
|
23
|
+
return isNumericLiteral(constant.value) ? constant.value : quote(constant.value);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const isStringTyped = (type: GirTypeRef | undefined): boolean =>
|
|
27
|
+
type?.kind === "primitive" && type.category === "string";
|
|
28
|
+
|
|
29
|
+
const isNumericLiteral = (value: string): boolean => /^-?(?:\d+|\d*\.\d+)$/.test(value);
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { quote, toCamelCase, toIdentifier, toPascalCase } from "@gtkx/utils";
|
|
2
|
+
import type { ModuleContext } from "../dsl/context.js";
|
|
3
|
+
import { indent } from "../dsl/emit.js";
|
|
4
|
+
import type { GirClass } from "../gir/class.js";
|
|
5
|
+
import type { GirProperty } from "../gir/property.js";
|
|
6
|
+
import { splitQualifiedName } from "../gir/qualified-name.js";
|
|
7
|
+
import { collectInterfaceProperties } from "./inheritance.js";
|
|
8
|
+
import { renderTsType } from "./ts-type.js";
|
|
9
|
+
import { renderFfiType } from "./value.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Whether a property can be passed to a constructor: writable, construct, or
|
|
13
|
+
* construct-only properties only. Read-only properties cannot be set through
|
|
14
|
+
* `g_object_new_with_properties`.
|
|
15
|
+
*
|
|
16
|
+
* @param property - The GIR property
|
|
17
|
+
*/
|
|
18
|
+
const isConstructable = (property: GirProperty): boolean =>
|
|
19
|
+
property.writable || property.construct || property.constructOnly;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The constructable properties a class introduces: its own writable/construct
|
|
23
|
+
* properties plus those it inherits from directly-implemented interfaces,
|
|
24
|
+
* deduplicated by camelCase JS name (the class's own declaration wins).
|
|
25
|
+
*
|
|
26
|
+
* Ancestor class properties are intentionally excluded — each class translates
|
|
27
|
+
* only the props it introduces and forwards the rest up the `super(...)` chain.
|
|
28
|
+
*
|
|
29
|
+
* @param context - The module context
|
|
30
|
+
* @param klass - The class whose construct props to collect
|
|
31
|
+
*/
|
|
32
|
+
const collectConstructableProps = (context: ModuleContext, klass: GirClass): readonly GirProperty[] => {
|
|
33
|
+
const all = [...klass.properties, ...collectInterfaceProperties(context, klass)].filter(isConstructable);
|
|
34
|
+
const seen = new Set<string>();
|
|
35
|
+
const result: GirProperty[] = [];
|
|
36
|
+
for (const property of all) {
|
|
37
|
+
const jsName = toIdentifier(toCamelCase(property.name));
|
|
38
|
+
if (seen.has(jsName)) continue;
|
|
39
|
+
seen.add(jsName);
|
|
40
|
+
result.push(property);
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Renders the `export interface <Class>ConstructorProps` declaration.
|
|
47
|
+
*
|
|
48
|
+
* Each interface lists the camelCase, optional, nullable form of every
|
|
49
|
+
* property the class introduces and `extends` its parent's interface, so the
|
|
50
|
+
* full inherited prop set is available when typing `new <Class>(props)`.
|
|
51
|
+
*
|
|
52
|
+
* @param context - The module context
|
|
53
|
+
* @param klass - The class to render props for
|
|
54
|
+
* @param className - The local PascalCase class name
|
|
55
|
+
*/
|
|
56
|
+
export const renderConstructorPropsInterface = (context: ModuleContext, klass: GirClass, className: string): string => {
|
|
57
|
+
const parentRef = resolveParentPropsReference(context, klass);
|
|
58
|
+
const extendsClause = parentRef === undefined ? "" : ` extends ${parentRef}`;
|
|
59
|
+
const lines = collectConstructableProps(context, klass).map(
|
|
60
|
+
(property) => `${toIdentifier(toCamelCase(property.name))}?: ${renderTsType(context, property.type, true)};`,
|
|
61
|
+
);
|
|
62
|
+
const body = lines.length === 0 ? "" : `\n${indent(lines.join("\n"), 1)}\n`;
|
|
63
|
+
return `export interface ${className}ConstructorProps${extendsClause} {${body}}`;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Renders the constructor for a class, or `undefined` when none is needed.
|
|
68
|
+
*
|
|
69
|
+
* The parentless root of a GObject hierarchy gets the canonical base
|
|
70
|
+
* constructor that hands the assembled `GValue` record to
|
|
71
|
+
* `constructGObjectInstance`. A class that introduces constructable props gets
|
|
72
|
+
* a constructor that destructures and translates those props into `GValue`s,
|
|
73
|
+
* spreads the untranslated remainder, and forwards everything to `super`. A
|
|
74
|
+
* class that introduces no props gets no constructor and inherits its nearest
|
|
75
|
+
* typed ancestor's, keeping the chain free of redundant hops.
|
|
76
|
+
*
|
|
77
|
+
* @param context - The module context
|
|
78
|
+
* @param klass - The class being emitted
|
|
79
|
+
* @param className - The local PascalCase class name
|
|
80
|
+
* @param hasParent - Whether the class declares an `extends` clause
|
|
81
|
+
*/
|
|
82
|
+
export const renderClassConstructor = (
|
|
83
|
+
context: ModuleContext,
|
|
84
|
+
klass: GirClass,
|
|
85
|
+
className: string,
|
|
86
|
+
hasParent: boolean,
|
|
87
|
+
): string | undefined => {
|
|
88
|
+
if (!hasParent) return renderRootConstructor(context);
|
|
89
|
+
const props = collectConstructableProps(context, klass);
|
|
90
|
+
if (props.length === 0) return undefined;
|
|
91
|
+
return renderTranslatingConstructor(context, props, className);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/** The forwarding record of marshalled `GValue`s a constructor hands to `super`. */
|
|
95
|
+
const GVALUE_RECORD = "Record<string, GValue | undefined>";
|
|
96
|
+
|
|
97
|
+
const renderRootConstructor = (context: ModuleContext): string => {
|
|
98
|
+
context.addConstructGObjectInstanceImport();
|
|
99
|
+
context.addRuntimeImport("GValue");
|
|
100
|
+
const body = "constructGObjectInstance(this, props);";
|
|
101
|
+
return `constructor(props: ${GVALUE_RECORD} = {}) {\n${indent(body, 1)}\n}`;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const renderTranslatingConstructor = (
|
|
105
|
+
context: ModuleContext,
|
|
106
|
+
props: readonly GirProperty[],
|
|
107
|
+
className: string,
|
|
108
|
+
): string => {
|
|
109
|
+
context.addValueFromFfiOptionalImport();
|
|
110
|
+
context.addRuntimeImport("GValue");
|
|
111
|
+
const destructured = props.map((property) => toIdentifier(toCamelCase(property.name)));
|
|
112
|
+
const pattern = `{ ${[...destructured, "...rest"].join(", ")} }`;
|
|
113
|
+
const entries = props.map(
|
|
114
|
+
(property) =>
|
|
115
|
+
`${quote(property.name)}: valueFromFfiOptional(${renderFfiType(context, property.type, property.transferOwnership)}, ${toIdentifier(toCamelCase(property.name))}),`,
|
|
116
|
+
);
|
|
117
|
+
const recordLiteral = `{\n${indent(entries.join("\n"), 1)}\n}`;
|
|
118
|
+
const lines = [`const props: ${GVALUE_RECORD} = ${recordLiteral};`, "super({ ...props, ...rest });"];
|
|
119
|
+
const body = lines.join("\n");
|
|
120
|
+
return `constructor(${pattern}: ${className}ConstructorProps = {}) {\n${indent(body, 1)}\n}`;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const resolveParentPropsReference = (context: ModuleContext, klass: GirClass): string | undefined => {
|
|
124
|
+
if (klass.parent === undefined) return undefined;
|
|
125
|
+
const { namespaceName, typeName } = splitQualifiedName(klass.parent, context.namespace.name);
|
|
126
|
+
const propsName = `${toPascalCase(typeName)}ConstructorProps`;
|
|
127
|
+
if (namespaceName === context.namespace.name) return propsName;
|
|
128
|
+
const alias = context.addCrossNamespaceImport(namespaceName);
|
|
129
|
+
return `${alias}.${propsName}`;
|
|
130
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { quote } from "@gtkx/utils";
|
|
2
|
+
import type { ModuleContext } from "../dsl/context.js";
|
|
3
|
+
import type { GirEnum } from "../gir/enum.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Emits the runtime declaration for an `<enumeration>` or `<bitfield>`.
|
|
7
|
+
*
|
|
8
|
+
* Two branches:
|
|
9
|
+
* - `glib:error-domain` present → `export type Name = number` plus
|
|
10
|
+
* `createErrorDomain(quark, members)` typed as `ErrorDomain<{ members }>`,
|
|
11
|
+
* since the domain object must stay `instanceof`-matchable.
|
|
12
|
+
* - GLib-registered enum/flags or C-only enum (no GLib registration) →
|
|
13
|
+
* a TypeScript `enum Name { members }` declaration.
|
|
14
|
+
*
|
|
15
|
+
* Member names are derived from the GIR `name` attribute, uppercased with
|
|
16
|
+
* hyphens normalized to underscores. Names that would start with a digit
|
|
17
|
+
* (e.g. `5_6_5` → `_5_6_5`) are prefixed with an underscore.
|
|
18
|
+
*
|
|
19
|
+
* @param context - The module context
|
|
20
|
+
* @param enumeration - The enum to emit
|
|
21
|
+
*/
|
|
22
|
+
export const emitEnum = (context: ModuleContext, enumeration: GirEnum): void => {
|
|
23
|
+
if (!enumeration.introspectable) return;
|
|
24
|
+
const name = enumeration.name;
|
|
25
|
+
const memberKeys = enumeration.members.map((member) => memberKey(member.name));
|
|
26
|
+
if (enumeration.errorDomain !== undefined) {
|
|
27
|
+
const memberEntries = enumeration.members.map((member, index) => `${memberKeys[index]}: ${member.value}`);
|
|
28
|
+
const typeFields = memberKeys.map((key) => `readonly ${key}: number`).join("; ");
|
|
29
|
+
context.addRuntimeImport("createErrorDomain");
|
|
30
|
+
context.addRuntimeImport("ErrorDomain");
|
|
31
|
+
const quarkExpression = renderQuarkExpression(context, enumeration.errorDomain);
|
|
32
|
+
context.module.appendDeclaration(`export type ${name} = number;`);
|
|
33
|
+
context.module.appendDeclaration(
|
|
34
|
+
`export const ${name}: ErrorDomain<{ ${typeFields} }> = createErrorDomain(${quarkExpression}, { ${memberEntries.join(", ")} });`,
|
|
35
|
+
);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const memberDeclarations = enumeration.members.map((member, index) => `${memberKeys[index]} = ${member.value}`);
|
|
39
|
+
context.module.appendDeclaration(`export enum ${name} { ${memberDeclarations.join(", ")} }`);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const memberKey = (name: string): string => {
|
|
43
|
+
const upper = name.toUpperCase().replaceAll("-", "_");
|
|
44
|
+
return /^[0-9]/.test(upper) ? `_${upper}` : upper;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const renderQuarkExpression = (context: ModuleContext, errorDomain: string): string => {
|
|
48
|
+
if (context.namespace.name === "GLib") {
|
|
49
|
+
return `() => quarkFromString(${quote(errorDomain)})`;
|
|
50
|
+
}
|
|
51
|
+
const alias = context.addCrossNamespaceImport("GLib");
|
|
52
|
+
return `() => ${alias}.quarkFromString(${quote(errorDomain)})`;
|
|
53
|
+
};
|