@gtkx/codegen 1.0.0-rc.2 → 1.0.0-rc.3
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/analysis/descriptor-render.d.ts +2 -0
- package/dist/analysis/descriptor-render.d.ts.map +1 -1
- package/dist/analysis/descriptor-render.js +25 -4
- package/dist/analysis/descriptor-render.js.map +1 -1
- package/dist/analysis/ts-type.d.ts.map +1 -1
- package/dist/analysis/ts-type.js +4 -8
- package/dist/analysis/ts-type.js.map +1 -1
- package/dist/docs/api-reference.d.ts.map +1 -1
- package/dist/docs/api-reference.js +4 -8
- package/dist/docs/api-reference.js.map +1 -1
- package/dist/docs/element-page.d.ts.map +1 -1
- package/dist/docs/element-page.js +5 -4
- package/dist/docs/element-page.js.map +1 -1
- package/dist/docs/pipeline.d.ts +2 -0
- package/dist/docs/pipeline.d.ts.map +1 -1
- package/dist/docs/pipeline.js +2 -0
- package/dist/docs/pipeline.js.map +1 -1
- package/dist/fingerprint.d.ts +1 -0
- package/dist/fingerprint.d.ts.map +1 -1
- package/dist/fingerprint.js +3 -0
- package/dist/fingerprint.js.map +1 -1
- package/dist/gi.d.ts +7 -2
- package/dist/gi.d.ts.map +1 -1
- package/dist/gi.js +4 -2
- package/dist/gi.js.map +1 -1
- package/dist/gir/libraries.d.ts +4 -3
- package/dist/gir/libraries.d.ts.map +1 -1
- package/dist/gir/libraries.js +2 -1
- package/dist/gir/libraries.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/jsx.d.ts +2 -0
- package/dist/jsx.d.ts.map +1 -1
- package/dist/jsx.js +4 -0
- package/dist/jsx.js.map +1 -1
- package/dist/react/element-config.d.ts +3 -0
- package/dist/react/element-config.d.ts.map +1 -1
- package/dist/react/element-config.js +4 -1
- package/dist/react/element-config.js.map +1 -1
- package/dist/runner.d.ts +2 -0
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +4 -1
- package/dist/runner.js.map +1 -1
- package/dist/store/gi/callables.d.ts.map +1 -1
- package/dist/store/gi/callables.js +0 -5
- package/dist/store/gi/callables.js.map +1 -1
- package/dist/store/gi/function.d.ts.map +1 -1
- package/dist/store/gi/function.js +0 -2
- package/dist/store/gi/function.js.map +1 -1
- package/dist/store/gi/method.d.ts.map +1 -1
- package/dist/store/gi/method.js +1 -0
- package/dist/store/gi/method.js.map +1 -1
- package/dist/store/gi/record-constructor.d.ts.map +1 -1
- package/dist/store/gi/record-constructor.js +5 -4
- package/dist/store/gi/record-constructor.js.map +1 -1
- package/dist/store/gi/record-field-accessor.d.ts +2 -1
- package/dist/store/gi/record-field-accessor.d.ts.map +1 -1
- package/dist/store/gi/record-field-accessor.js +22 -6
- package/dist/store/gi/record-field-accessor.js.map +1 -1
- package/dist/store/gi/record.d.ts.map +1 -1
- package/dist/store/gi/record.js +0 -7
- package/dist/store/gi/record.js.map +1 -1
- package/dist/store/gi/value-marshalable.d.ts +15 -0
- package/dist/store/gi/value-marshalable.d.ts.map +1 -0
- package/dist/store/gi/value-marshalable.js +66 -0
- package/dist/store/gi/value-marshalable.js.map +1 -0
- package/dist/store/jsx/intrinsic-elements.d.ts +1 -2
- package/dist/store/jsx/intrinsic-elements.d.ts.map +1 -1
- package/dist/store/jsx/intrinsic-elements.js +1 -2
- package/dist/store/jsx/intrinsic-elements.js.map +1 -1
- package/dist/store/jsx/omitted-props.d.ts +10 -0
- package/dist/store/jsx/omitted-props.d.ts.map +1 -0
- package/dist/store/jsx/omitted-props.js +22 -0
- package/dist/store/jsx/omitted-props.js.map +1 -0
- package/dist/store/jsx/pipeline.d.ts +2 -0
- package/dist/store/jsx/pipeline.d.ts.map +1 -1
- package/dist/store/jsx/pipeline.js +2 -0
- package/dist/store/jsx/pipeline.js.map +1 -1
- package/dist/store/jsx/props.d.ts +1 -1
- package/dist/store/jsx/props.d.ts.map +1 -1
- package/dist/store/jsx/props.js +6 -11
- package/dist/store/jsx/props.js.map +1 -1
- package/package.json +3 -4
- package/src/analysis/descriptor-render.ts +35 -3
- package/src/analysis/ts-type.ts +4 -11
- package/src/docs/api-reference.ts +4 -9
- package/src/docs/element-page.ts +5 -9
- package/src/docs/pipeline.ts +3 -0
- package/src/fingerprint.ts +16 -2
- package/src/gi.ts +12 -3
- package/src/gir/libraries.ts +5 -4
- package/src/index.ts +1 -0
- package/src/jsx.ts +5 -0
- package/src/react/element-config.ts +8 -2
- package/src/runner.ts +8 -1
- package/src/store/gi/callables.ts +0 -6
- package/src/store/gi/function.ts +0 -2
- package/src/store/gi/method.ts +1 -0
- package/src/store/gi/record-constructor.ts +9 -4
- package/src/store/gi/record-field-accessor.ts +41 -6
- package/src/store/gi/record.ts +0 -9
- package/src/store/gi/value-marshalable.ts +96 -0
- package/src/store/jsx/intrinsic-elements.ts +0 -9
- package/src/store/jsx/omitted-props.ts +32 -0
- package/src/store/jsx/pipeline.ts +3 -0
- package/src/store/jsx/props.ts +6 -18
- package/dist/store/gi/class-struct-record.d.ts +0 -10
- package/dist/store/gi/class-struct-record.d.ts.map +0 -1
- package/dist/store/gi/class-struct-record.js +0 -52
- package/dist/store/gi/class-struct-record.js.map +0 -1
- package/src/store/gi/class-struct-record.ts +0 -72
package/src/analysis/ts-type.ts
CHANGED
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
type ListType,
|
|
10
10
|
type TypeId,
|
|
11
11
|
} from "../gir/type-id.js";
|
|
12
|
-
import { isClassStructRecord } from "../store/gi/class-struct-record.js";
|
|
13
12
|
import { gtypeTsType } from "../store/gi/gtype-binding.js";
|
|
14
13
|
|
|
15
14
|
type ReferenceName = {
|
|
@@ -25,18 +24,13 @@ type TsTypeTarget = {
|
|
|
25
24
|
renderGtype: () => string;
|
|
26
25
|
};
|
|
27
26
|
|
|
28
|
-
const willEmitEntity = (
|
|
27
|
+
const willEmitEntity = (type: EntityType): boolean => {
|
|
29
28
|
switch (type.kind) {
|
|
30
29
|
case "callback": {
|
|
31
30
|
return type.value.introspectable && type.value.name.length > 0;
|
|
32
31
|
}
|
|
33
32
|
case "record": {
|
|
34
|
-
return
|
|
35
|
-
type.value.introspectable &&
|
|
36
|
-
!type.value.isVtable &&
|
|
37
|
-
type.value.name.length > 0 &&
|
|
38
|
-
!isClassStructRecord(library, type.namespace.name, type.value)
|
|
39
|
-
);
|
|
33
|
+
return type.value.introspectable && type.value.name.length > 0;
|
|
40
34
|
}
|
|
41
35
|
case "alias":
|
|
42
36
|
case "class":
|
|
@@ -51,11 +45,10 @@ const renderPrimitiveType = (target: TsTypeTarget, type: Extract<GirType, { kind
|
|
|
51
45
|
type.category === "gtype" ? target.renderGtype() : PRIMITIVE_TS_TYPE[type.category];
|
|
52
46
|
|
|
53
47
|
const renderEntityType = (
|
|
54
|
-
library: Library,
|
|
55
48
|
target: TsTypeTarget,
|
|
56
49
|
type: EntityType,
|
|
57
50
|
name: ReferenceName | undefined,
|
|
58
|
-
): string => renderNamedType(target, type, willEmitEntity(
|
|
51
|
+
): string => renderNamedType(target, type, willEmitEntity(type) ? name : undefined);
|
|
59
52
|
|
|
60
53
|
const renderBaseType = (library: Library, target: TsTypeTarget, ref: TypeId | undefined): string => {
|
|
61
54
|
if (ref === undefined) {
|
|
@@ -82,7 +75,7 @@ const renderBaseType = (library: Library, target: TsTypeTarget, ref: TypeId | un
|
|
|
82
75
|
case "record":
|
|
83
76
|
case "enum":
|
|
84
77
|
case "alias": {
|
|
85
|
-
return renderEntityType(
|
|
78
|
+
return renderEntityType(target, type, name);
|
|
86
79
|
}
|
|
87
80
|
case "carray":
|
|
88
81
|
case "list":
|
|
@@ -5,7 +5,6 @@ import type { GirRecord } from "../gir/record.js";
|
|
|
5
5
|
import { Library } from "../gir/library.js";
|
|
6
6
|
import { type GirNamespace, namespaceDirectory } from "../gir/namespace.js";
|
|
7
7
|
import { dedupeCallables, isEmittableCallable } from "../store/gi/callables.js";
|
|
8
|
-
import { isClassStructRecord } from "../store/gi/class-struct-record.js";
|
|
9
8
|
import { namespaceFunctionExportName } from "../store/gi/function.js";
|
|
10
9
|
import { collectIntrinsicElementClasses, type GlibNamedClass } from "../store/jsx/intrinsic-elements.js";
|
|
11
10
|
import { createElementPageContext, type ElementPageContext, renderElementPage } from "./element-page.js";
|
|
@@ -136,23 +135,19 @@ const classEntries = (namespace: GirNamespace): GiSymbolEntry[] => {
|
|
|
136
135
|
return entries;
|
|
137
136
|
};
|
|
138
137
|
|
|
139
|
-
const recordEntry = (
|
|
138
|
+
const recordEntry = (namespace: GirNamespace, record: GirRecord): GiSymbolEntry | undefined => {
|
|
140
139
|
if (!record.introspectable || record.isVtable || record.name.length === 0) {
|
|
141
140
|
return undefined;
|
|
142
141
|
}
|
|
143
142
|
|
|
144
|
-
if (isClassStructRecord(library, namespace.name, record)) {
|
|
145
|
-
return undefined;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
143
|
return { kind: "record", namespace, name: record.name, doc: record.doc, record };
|
|
149
144
|
};
|
|
150
145
|
|
|
151
|
-
const recordEntries = (
|
|
146
|
+
const recordEntries = (namespace: GirNamespace): GiSymbolEntry[] => {
|
|
152
147
|
const entries: GiSymbolEntry[] = [];
|
|
153
148
|
|
|
154
149
|
for (const record of namespace.records) {
|
|
155
|
-
const entry = recordEntry(
|
|
150
|
+
const entry = recordEntry(namespace, record);
|
|
156
151
|
|
|
157
152
|
if (entry !== undefined) {
|
|
158
153
|
entries.push(entry);
|
|
@@ -322,7 +317,7 @@ class ApiReference {
|
|
|
322
317
|
private indexNamespace(namespace: GirNamespace): void {
|
|
323
318
|
const entries = [
|
|
324
319
|
...classEntries(namespace),
|
|
325
|
-
...recordEntries(
|
|
320
|
+
...recordEntries(namespace),
|
|
326
321
|
...valueEntries(namespace),
|
|
327
322
|
];
|
|
328
323
|
|
package/src/docs/element-page.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
newlyImplementedInterfaces,
|
|
14
14
|
signalHandlerName,
|
|
15
15
|
} from "../store/jsx/intrinsic-elements.js";
|
|
16
|
+
import { isOmittedProp } from "../store/jsx/omitted-props.js";
|
|
16
17
|
import { isObjectProp } from "../store/jsx/props.js";
|
|
17
18
|
import {
|
|
18
19
|
classMethodEntries,
|
|
@@ -120,12 +121,7 @@ const propertyEntry = (
|
|
|
120
121
|
property: GirProperty,
|
|
121
122
|
jsName: string,
|
|
122
123
|
): PropEntry => {
|
|
123
|
-
const isObject = isObjectProp(
|
|
124
|
-
{ library: context.library, klass: owner.klass, namespace: owner.namespace },
|
|
125
|
-
property,
|
|
126
|
-
jsName,
|
|
127
|
-
);
|
|
128
|
-
|
|
124
|
+
const isObject = isObjectProp(context.library, property);
|
|
129
125
|
const baseType = renderDocsType(context.library, property.type, false);
|
|
130
126
|
const type = isObject ? `${baseType} | ReactElement` : baseType;
|
|
131
127
|
const meta: string[] = [`\`${type}\``];
|
|
@@ -149,14 +145,14 @@ const propertyEntry = (
|
|
|
149
145
|
return { name: jsName, meta: meta.join(" · "), doc: docMarkdown(property.doc) };
|
|
150
146
|
};
|
|
151
147
|
|
|
152
|
-
const propJsName = (property: GirProperty, seen: Set<string>): string | undefined => {
|
|
148
|
+
const propJsName = (property: GirProperty, owner: MemberOwner, seen: Set<string>): string | undefined => {
|
|
153
149
|
if (!property.introspectable) {
|
|
154
150
|
return undefined;
|
|
155
151
|
}
|
|
156
152
|
|
|
157
153
|
const jsName = toCamelIdentifier(property.name);
|
|
158
154
|
|
|
159
|
-
if (seen.has(jsName)) {
|
|
155
|
+
if (seen.has(jsName) || isOmittedProp(owner.glibName, jsName)) {
|
|
160
156
|
return undefined;
|
|
161
157
|
}
|
|
162
158
|
|
|
@@ -169,7 +165,7 @@ const ownerPropEntries = (context: ElementPageContext, owner: MemberOwner, seen:
|
|
|
169
165
|
const entries: PropEntry[] = [];
|
|
170
166
|
|
|
171
167
|
for (const property of owner.klass.properties) {
|
|
172
|
-
const jsName = propJsName(property, seen);
|
|
168
|
+
const jsName = propJsName(property, owner, seen);
|
|
173
169
|
|
|
174
170
|
if (jsName !== undefined) {
|
|
175
171
|
entries.push(propertyEntry(context, owner, property, jsName));
|
package/src/docs/pipeline.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { Library } from "../gir/library.js";
|
|
|
6
6
|
import { namespaceDirectory } from "../gir/namespace.js";
|
|
7
7
|
import { type ElementProps, setElementProps } from "../store/jsx/element-prop-imports.js";
|
|
8
8
|
import { collectIntrinsicElementClasses, type GlibNamedClass } from "../store/jsx/intrinsic-elements.js";
|
|
9
|
+
import { type OmittedProps, setOmittedProps } from "../store/jsx/omitted-props.js";
|
|
9
10
|
import { createElementPageContext, type ElementPageContext, renderElementPage } from "./element-page.js";
|
|
10
11
|
import { elementSlug, firstSentence, namespaceOrder } from "./render.js";
|
|
11
12
|
|
|
@@ -27,6 +28,7 @@ type DocsOptions = {
|
|
|
27
28
|
outDir: string;
|
|
28
29
|
basePath?: string;
|
|
29
30
|
props?: ElementProps;
|
|
31
|
+
omitProps?: OmittedProps;
|
|
30
32
|
force?: boolean;
|
|
31
33
|
};
|
|
32
34
|
|
|
@@ -214,6 +216,7 @@ const writePages = (outDir: string, pages: Page[]): void => {
|
|
|
214
216
|
|
|
215
217
|
const writeDocs = (options: DocsOptions): DocsResult => {
|
|
216
218
|
setElementProps(options.props ?? {});
|
|
219
|
+
setOmittedProps(options.omitProps ?? {});
|
|
217
220
|
const basePath = options.basePath ?? "/reference";
|
|
218
221
|
const manifestPath = join(options.outDir, MANIFEST_FILENAME);
|
|
219
222
|
const cached = cachedDocsResult(options, manifestPath);
|
package/src/fingerprint.ts
CHANGED
|
@@ -21,6 +21,7 @@ type JsxFingerprintInput = {
|
|
|
21
21
|
components: Record<string, ModuleExport>;
|
|
22
22
|
lazyElements: string[];
|
|
23
23
|
props: Record<string, ModuleExport>;
|
|
24
|
+
omitProps: Record<string, string[]>;
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
/** The freshness sentinel of the `@gtkx/jsx` store, derived from the React element config. */
|
|
@@ -55,6 +56,7 @@ const hashGi = (girFiles: string[], libraries: string[], girPath: string[]): str
|
|
|
55
56
|
hash.update(sortAlpha(libraries));
|
|
56
57
|
hash.update("\n");
|
|
57
58
|
hash.update(sortAlpha(girPath));
|
|
59
|
+
hash.update("\n");
|
|
58
60
|
const hashedFiles = sortStrings([...girFiles, ...overrideFiles()]);
|
|
59
61
|
|
|
60
62
|
for (const file of hashedFiles) {
|
|
@@ -67,14 +69,22 @@ const hashGi = (girFiles: string[], libraries: string[], girPath: string[]): str
|
|
|
67
69
|
return hash.digest("hex");
|
|
68
70
|
};
|
|
69
71
|
|
|
70
|
-
const computeGiFingerprint = (
|
|
72
|
+
const computeGiFingerprint = (
|
|
73
|
+
girFiles: string[],
|
|
74
|
+
libraries: string[],
|
|
75
|
+
girPath: string[],
|
|
76
|
+
): GiFingerprint => ({
|
|
71
77
|
value: hashGi(girFiles, libraries, girPath),
|
|
72
78
|
girFiles,
|
|
73
79
|
libraries,
|
|
74
80
|
girPath,
|
|
75
81
|
});
|
|
76
82
|
|
|
77
|
-
const isGiStoreFresh = (
|
|
83
|
+
const isGiStoreFresh = (
|
|
84
|
+
giStoreDir: string,
|
|
85
|
+
libraries: string[],
|
|
86
|
+
girPath: string[],
|
|
87
|
+
): boolean => {
|
|
78
88
|
const sentinelPath = join(giStoreDir, FINGERPRINT_FILENAME);
|
|
79
89
|
|
|
80
90
|
if (!existsSync(sentinelPath)) {
|
|
@@ -106,6 +116,9 @@ const hasMatchingRecordedInputs = (sentinel: GiFingerprint, libraries: string[],
|
|
|
106
116
|
const serializeModuleExports = (map: Record<string, ModuleExport>): [string, string, string][] =>
|
|
107
117
|
sortStrings(Object.keys(map)).map((type) => [type, map[type]?.module ?? "", map[type]?.export ?? ""]);
|
|
108
118
|
|
|
119
|
+
const serializeOmitProps = (map: Record<string, string[]>): [string, string][] =>
|
|
120
|
+
sortStrings(Object.keys(map)).map((type) => [type, sortAlpha(map[type] ?? [])]);
|
|
121
|
+
|
|
109
122
|
const hashJsx = (input: JsxFingerprintInput): string =>
|
|
110
123
|
createHash("sha256")
|
|
111
124
|
.update(
|
|
@@ -115,6 +128,7 @@ const hashJsx = (input: JsxFingerprintInput): string =>
|
|
|
115
128
|
serializeModuleExports(input.components),
|
|
116
129
|
sortStrings(input.lazyElements),
|
|
117
130
|
serializeModuleExports(input.props),
|
|
131
|
+
serializeOmitProps(input.omitProps),
|
|
118
132
|
]),
|
|
119
133
|
)
|
|
120
134
|
.digest("hex");
|
package/src/gi.ts
CHANGED
|
@@ -4,23 +4,32 @@ import { namespaceDirectory } from "./gir/namespace.js";
|
|
|
4
4
|
import { type GiNamespaceInput, type GiStoreOptions, writeGiStore } from "./store/gi-store.js";
|
|
5
5
|
import { generateNamespaceModule } from "./store/gi/pipeline.js";
|
|
6
6
|
|
|
7
|
+
type GiCodegenOptions = {
|
|
8
|
+
gi: GiStoreOptions;
|
|
9
|
+
libraries: string[];
|
|
10
|
+
girPath: string[];
|
|
11
|
+
};
|
|
12
|
+
|
|
7
13
|
/**
|
|
8
14
|
* Generates the `@gtkx/gi` store from the loaded GIR library and links it into the project. This is the
|
|
9
15
|
* react-free half of codegen: it can run before `@gtkx/gi` (or `@gtkx/react`) exists, since it produces them.
|
|
10
16
|
*
|
|
11
17
|
* @returns The number of namespaces written.
|
|
12
18
|
*/
|
|
13
|
-
const runGiCodegen = (library: Library,
|
|
19
|
+
const runGiCodegen = (library: Library, options: GiCodegenOptions): number => {
|
|
20
|
+
const { gi, libraries, girPath } = options;
|
|
21
|
+
|
|
14
22
|
const namespaces: GiNamespaceInput[] = Array.from(library.namespaces.values(), (namespace) => ({
|
|
15
23
|
directory: namespaceDirectory(namespace),
|
|
16
24
|
rawSource: generateNamespaceModule(namespace, library),
|
|
17
25
|
}));
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
const fingerprint = computeGiFingerprint(library.girFiles, [...libraries], [...girPath]);
|
|
28
|
+
writeGiStore(gi, namespaces, fingerprint);
|
|
20
29
|
|
|
21
30
|
return library.namespaces.size;
|
|
22
31
|
};
|
|
23
32
|
|
|
24
33
|
export { isGiStoreFresh } from "./fingerprint.js";
|
|
25
34
|
export type { GiStoreOptions } from "./store/gi-store.js";
|
|
26
|
-
export { runGiCodegen };
|
|
35
|
+
export { runGiCodegen, type GiCodegenOptions };
|
package/src/gir/libraries.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import type { Config } from "@gtkx/config";
|
|
2
|
-
import { GIR_LIBRARY_PATTERN, LIBRARIES_WILDCARD } from "@gtkx/config/internal";
|
|
3
1
|
import { sortStrings } from "@gtkx/utils";
|
|
4
2
|
import { readdirSync } from "node:fs";
|
|
5
3
|
|
|
6
4
|
type GirNamespace = { name: string; version: string; identifier: string };
|
|
5
|
+
type LibrarySelection = typeof LIBRARIES_WILDCARD | string[] | undefined;
|
|
7
6
|
|
|
7
|
+
const LIBRARIES_WILDCARD = "*";
|
|
8
|
+
const GIR_LIBRARY_PATTERN = /^[A-Za-z][A-Za-z0-9]*-\d+(?:\.\d+)*$/;
|
|
8
9
|
const DEFAULT_LIBRARIES: string[] = ["Gtk-4.0"];
|
|
9
10
|
const GIR_FILE_SUFFIX = ".gir";
|
|
10
11
|
|
|
11
|
-
const resolveLibraries = (libraries:
|
|
12
|
+
const resolveLibraries = (libraries: LibrarySelection, girPath: string[]): string[] => {
|
|
12
13
|
if (libraries === undefined) {
|
|
13
14
|
return [...DEFAULT_LIBRARIES];
|
|
14
15
|
}
|
|
@@ -99,4 +100,4 @@ const compareVersions = (a: string, b: string): number => {
|
|
|
99
100
|
return 0;
|
|
100
101
|
};
|
|
101
102
|
|
|
102
|
-
export { resolveLibraries, discoverGirNamespaces };
|
|
103
|
+
export { resolveLibraries, discoverGirNamespaces, type LibrarySelection };
|
package/src/index.ts
CHANGED
package/src/jsx.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "./fingerprint.js";
|
|
8
8
|
import { type ModuleExport, readBuiltinElements } from "./react/element-config.js";
|
|
9
9
|
import { type JsxStoreOptions, writeJsxStore } from "./store/jsx-store.js";
|
|
10
|
+
import { mergeOmitProps, type OmittedProps } from "./store/jsx/omitted-props.js";
|
|
10
11
|
import { generateJsxFiles } from "./store/jsx/pipeline.js";
|
|
11
12
|
|
|
12
13
|
type RunJsxCodegenOptions = {
|
|
@@ -17,6 +18,7 @@ type RunJsxCodegenOptions = {
|
|
|
17
18
|
userComponents: Record<string, ModuleExport>;
|
|
18
19
|
userLazyElements: string[];
|
|
19
20
|
userProps: Record<string, ModuleExport>;
|
|
21
|
+
userOmitProps: OmittedProps;
|
|
20
22
|
giRegenerated: boolean;
|
|
21
23
|
force: boolean;
|
|
22
24
|
};
|
|
@@ -36,12 +38,14 @@ const runJsxCodegen = async (options: RunJsxCodegenOptions): Promise<RunJsxCodeg
|
|
|
36
38
|
const components = { ...builtin.components, ...options.userComponents };
|
|
37
39
|
const lazyElements = [...builtin.lazyElements, ...options.userLazyElements];
|
|
38
40
|
const props = { ...builtin.props, ...options.userProps };
|
|
41
|
+
const omitProps = mergeOmitProps(builtin.omitProps, options.userOmitProps);
|
|
39
42
|
|
|
40
43
|
const fingerprintInput: JsxFingerprintInput = {
|
|
41
44
|
reactVersion: options.jsx.version,
|
|
42
45
|
components,
|
|
43
46
|
lazyElements,
|
|
44
47
|
props,
|
|
48
|
+
omitProps,
|
|
45
49
|
};
|
|
46
50
|
|
|
47
51
|
if (!options.force && !options.giRegenerated) {
|
|
@@ -57,6 +61,7 @@ const runJsxCodegen = async (options: RunJsxCodegenOptions): Promise<RunJsxCodeg
|
|
|
57
61
|
components,
|
|
58
62
|
lazyElements,
|
|
59
63
|
props,
|
|
64
|
+
omitProps,
|
|
60
65
|
});
|
|
61
66
|
|
|
62
67
|
writeJsxStore(options.jsx, namespaces, metadata, {
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { ModuleExport } from "@gtkx/react/config";
|
|
2
2
|
import { readdirSync } from "node:fs";
|
|
3
|
+
import type { OmittedProps } from "../store/jsx/omitted-props.js";
|
|
3
4
|
|
|
4
5
|
/** The codegen-relevant subset of a React element config; behaviors are ignored at codegen time. */
|
|
5
|
-
type BuiltinElement = { component?: ModuleExport; lazy?: boolean; props?: ModuleExport };
|
|
6
|
+
type BuiltinElement = { component?: ModuleExport; lazy?: boolean; props?: ModuleExport; omitProps?: string[] };
|
|
6
7
|
|
|
7
8
|
/** The framework's built-in element config, split into the maps codegen consumes. */
|
|
8
9
|
type BuiltinElements = {
|
|
9
10
|
components: Record<string, ModuleExport>;
|
|
10
11
|
lazyElements: string[];
|
|
11
12
|
props: Record<string, ModuleExport>;
|
|
13
|
+
omitProps: OmittedProps;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
const CONFIG_ENTRYPOINT = "config";
|
|
@@ -59,6 +61,10 @@ const applyBuiltinElement = (target: BuiltinElements, type: string, config: Buil
|
|
|
59
61
|
target.props[type] = config.props;
|
|
60
62
|
}
|
|
61
63
|
|
|
64
|
+
if (config.omitProps !== undefined) {
|
|
65
|
+
target.omitProps[type] = [...(target.omitProps[type] ?? []), ...config.omitProps];
|
|
66
|
+
}
|
|
67
|
+
|
|
62
68
|
if (config.lazy === true) {
|
|
63
69
|
target.lazyElements.push(type);
|
|
64
70
|
}
|
|
@@ -78,7 +84,7 @@ const collectBuiltinElements = (target: BuiltinElements, elements: Record<string
|
|
|
78
84
|
const readBuiltinElements = async (reactSubexports: string[], giStoreDir: string): Promise<BuiltinElements> => {
|
|
79
85
|
const present = presentNamespaceDirs(giStoreDir);
|
|
80
86
|
const entrypoints = configEntrypoints(reactSubexports, present);
|
|
81
|
-
const result: BuiltinElements = { components: {}, lazyElements: [], props: {} };
|
|
87
|
+
const result: BuiltinElements = { components: {}, lazyElements: [], props: {}, omitProps: {} };
|
|
82
88
|
|
|
83
89
|
for (const entrypoint of entrypoints) {
|
|
84
90
|
collectBuiltinElements(result, await importBuiltinElements(entrypoint));
|
package/src/runner.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { mkdirSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import type { GiStoreOptions } from "./store/gi-store.js";
|
|
4
4
|
import type { JsxStoreOptions } from "./store/jsx-store.js";
|
|
5
|
+
import type { OmittedProps } from "./store/jsx/omitted-props.js";
|
|
5
6
|
import { checkModules } from "./compile.js";
|
|
6
7
|
import { isGiStoreFresh } from "./fingerprint.js";
|
|
7
8
|
import { runGiCodegen } from "./gi.js";
|
|
@@ -26,6 +27,7 @@ type CodegenRunnerOptions = {
|
|
|
26
27
|
userComponents?: Record<string, ModuleExport>;
|
|
27
28
|
userLazyElements?: string[];
|
|
28
29
|
userProps?: Record<string, ModuleExport>;
|
|
30
|
+
userOmitProps?: OmittedProps;
|
|
29
31
|
gl?: GlCodegenOptions;
|
|
30
32
|
force?: boolean;
|
|
31
33
|
};
|
|
@@ -95,11 +97,13 @@ const jsxUserOptions = (
|
|
|
95
97
|
userComponents: Record<string, ModuleExport>;
|
|
96
98
|
userLazyElements: string[];
|
|
97
99
|
userProps: Record<string, ModuleExport>;
|
|
100
|
+
userOmitProps: OmittedProps;
|
|
98
101
|
} => ({
|
|
99
102
|
reactSubexports: options.reactSubexports ?? [],
|
|
100
103
|
userComponents: options.userComponents ?? {},
|
|
101
104
|
userLazyElements: options.userLazyElements ?? [],
|
|
102
105
|
userProps: options.userProps ?? {},
|
|
106
|
+
userOmitProps: options.userOmitProps ?? {},
|
|
103
107
|
});
|
|
104
108
|
|
|
105
109
|
const emitJsxStore = async (input: {
|
|
@@ -140,7 +144,10 @@ const emitStoresWithConfig = async (config: {
|
|
|
140
144
|
let library: Library | undefined;
|
|
141
145
|
const loadLibrary = (): Library => (library ??= Library.load(libraries, girPath));
|
|
142
146
|
const isGiRegenerated = options.force === true || !isGiStoreFresh(gi.storeDir, libraries, girPath);
|
|
143
|
-
|
|
147
|
+
|
|
148
|
+
const namespaces = isGiRegenerated
|
|
149
|
+
? runGiCodegen(loadLibrary(), { gi, libraries, girPath })
|
|
150
|
+
: 0;
|
|
144
151
|
|
|
145
152
|
if (jsx === undefined) {
|
|
146
153
|
return { regenerated: isGiRegenerated, namespaces, intrinsicElements: 0 };
|
|
@@ -5,7 +5,6 @@ import { hasCallerAllocatedArrayLength } from "../../analysis/param-structure.js
|
|
|
5
5
|
import { renderJsDoc } from "../../writer/doc.js";
|
|
6
6
|
import { renderBlock } from "../../writer/emit.js";
|
|
7
7
|
import { matchAsyncFinish } from "./async.js";
|
|
8
|
-
import { hasClassStructReference } from "./class-struct-record.js";
|
|
9
8
|
import { renderFnExpression } from "./function.js";
|
|
10
9
|
import { gtypeMemberDeclaration } from "./gtype-binding.js";
|
|
11
10
|
import {
|
|
@@ -111,10 +110,6 @@ const renderBinding = (
|
|
|
111
110
|
return undefined;
|
|
112
111
|
}
|
|
113
112
|
|
|
114
|
-
if (hasClassStructReference(context, callable)) {
|
|
115
|
-
return undefined;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
113
|
const expression = renderFnExpression(context, callable);
|
|
119
114
|
|
|
120
115
|
if (expression === undefined) {
|
|
@@ -371,7 +366,6 @@ const isEmittableCallable = (context: ModuleContext, callable: GirFunction): boo
|
|
|
371
366
|
callable.introspectable &&
|
|
372
367
|
callable.shadowedBy === undefined &&
|
|
373
368
|
callable.cIdentifier !== undefined &&
|
|
374
|
-
!hasClassStructReference(context, callable) &&
|
|
375
369
|
!hasCallerAllocatedArrayLength(context.library, callable);
|
|
376
370
|
|
|
377
371
|
const constructorMemberName = (girName: string): string | undefined => {
|
package/src/store/gi/function.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { hasCallerAllocatedArrayLength } from "../../analysis/param-structure.js
|
|
|
7
7
|
import { renderJsDoc } from "../../writer/doc.js";
|
|
8
8
|
import { arrayLiteral, renderBlock } from "../../writer/emit.js";
|
|
9
9
|
import { matchAsyncFinish } from "./async.js";
|
|
10
|
-
import { hasClassStructReference } from "./class-struct-record.js";
|
|
11
10
|
import {
|
|
12
11
|
planCallArgs,
|
|
13
12
|
renderMethodBody,
|
|
@@ -63,7 +62,6 @@ const canEmitNamespaceFunction = (context: ModuleContext, fn: GirFunction): bool
|
|
|
63
62
|
!isMovedOntoEmittedMember(context, fn) &&
|
|
64
63
|
fn.shadowedBy === undefined &&
|
|
65
64
|
fn.cIdentifier !== undefined &&
|
|
66
|
-
!hasClassStructReference(context, fn) &&
|
|
67
65
|
!hasCallerAllocatedArrayLength(context.library, fn);
|
|
68
66
|
|
|
69
67
|
const generateNamespaceFunction = (context: ModuleContext, fn: GirFunction): void => {
|
package/src/store/gi/method.ts
CHANGED
|
@@ -315,6 +315,7 @@ const renderReturnDescriptor = (context: ModuleContext, fn: GirFunction): string
|
|
|
315
315
|
return renderDescriptor(context, fn.returnValue.type, fn.returnValue.transferOwnership, {
|
|
316
316
|
argIndexOffset: instanceOffset,
|
|
317
317
|
argIndexMap: emittedArgIndices(fn, instanceOffset),
|
|
318
|
+
isNewlyCreated: fn.instance === undefined,
|
|
318
319
|
});
|
|
319
320
|
};
|
|
320
321
|
|
|
@@ -7,8 +7,9 @@ import { renderDescriptor } from "../../analysis/descriptor-render.js";
|
|
|
7
7
|
import { renderTsType } from "../../analysis/ts-type.js";
|
|
8
8
|
import { renderBlock, renderBracedOrEmpty } from "../../writer/emit.js";
|
|
9
9
|
import { renderSourceGtype } from "./gtype-binding.js";
|
|
10
|
-
import { emitFieldWrite, isEmittableField } from "./record-field-accessor.js";
|
|
10
|
+
import { emitFieldWrite, isEmittableField, isInlineField } from "./record-field-accessor.js";
|
|
11
11
|
import { computeRecordFieldSlots, type RecordFieldSlot } from "./record-layout.js";
|
|
12
|
+
import { isConstructibleRecord } from "./value-marshalable.js";
|
|
12
13
|
|
|
13
14
|
type WritableFieldSlot = RecordFieldSlot & { field: GirField & { type: TypeId } };
|
|
14
15
|
|
|
@@ -24,7 +25,7 @@ const renderRecordConstructorPropsInterface = (
|
|
|
24
25
|
): string => {
|
|
25
26
|
const head = `export interface ${className}ConstructorProps`;
|
|
26
27
|
|
|
27
|
-
if (isOpaque(record)) {
|
|
28
|
+
if (isOpaque(record) || !isConstructibleRecord(context, context.namespace.name, record)) {
|
|
28
29
|
return renderBracedOrEmpty(head, "");
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -68,7 +69,7 @@ const renderRecordConstructor = (
|
|
|
68
69
|
): string => {
|
|
69
70
|
const superCall = isErrorSubclass ? ["super();"] : [];
|
|
70
71
|
|
|
71
|
-
if (isOpaque(record)) {
|
|
72
|
+
if (isOpaque(record) || !isConstructibleRecord(context, context.namespace.name, record)) {
|
|
72
73
|
return renderOpaqueConstructor(className, superCall);
|
|
73
74
|
}
|
|
74
75
|
|
|
@@ -103,7 +104,11 @@ const allocArgs = (context: ModuleContext, record: GirRecord, size: number): str
|
|
|
103
104
|
|
|
104
105
|
const renderFieldWrite = (context: ModuleContext, entry: WritableFieldSlot): string => {
|
|
105
106
|
context.addRuntimeImport("t");
|
|
106
|
-
|
|
107
|
+
|
|
108
|
+
const descriptor = context.hoistDescriptor(
|
|
109
|
+
renderDescriptor(context, entry.field.type, "none", { isInline: isInlineField(context, entry.field) }),
|
|
110
|
+
);
|
|
111
|
+
|
|
107
112
|
const name = toCamelIdentifier(entry.field.name);
|
|
108
113
|
|
|
109
114
|
const write = emitFieldWrite(context, {
|
|
@@ -9,9 +9,9 @@ import { tStruct } from "../../analysis/descriptor.js";
|
|
|
9
9
|
import { renderTsType } from "../../analysis/ts-type.js";
|
|
10
10
|
import { renderJsDoc } from "../../writer/doc.js";
|
|
11
11
|
import { indent, renderBlock } from "../../writer/emit.js";
|
|
12
|
-
import { isClassStructRef } from "./class-struct-record.js";
|
|
13
12
|
import { bitMask, computeRecordFieldSlots, mergeBitfield, type RecordFieldSlot } from "./record-layout.js";
|
|
14
13
|
import { wrapReturnValue } from "./return-wrap.js";
|
|
14
|
+
import { isValueMarshalable } from "./value-marshalable.js";
|
|
15
15
|
|
|
16
16
|
type FieldWriteSpec = {
|
|
17
17
|
descriptor: string;
|
|
@@ -91,18 +91,39 @@ type AccessorOptions = {
|
|
|
91
91
|
fieldType: TypeId;
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
+
// An inline record field is read and written through its own bytes, so it can only be exposed when
|
|
95
|
+
// those bytes are copyable; a pointer field is marshalled as a handle and is always fine.
|
|
96
|
+
const isMarshalableField = (context: ModuleContext, field: GirField): boolean => {
|
|
97
|
+
if (field.cType?.endsWith("*") === true) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const type = field.type === undefined ? undefined : context.library.typeFor(field.type);
|
|
102
|
+
|
|
103
|
+
if (type?.kind !== "record") {
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return isValueMarshalable(context, type.namespace.name, type.value);
|
|
108
|
+
};
|
|
109
|
+
|
|
94
110
|
const isEmittableField = (context: ModuleContext, field: GirField): field is GirField & { type: TypeId } =>
|
|
95
111
|
!field.private &&
|
|
96
112
|
field.type !== undefined &&
|
|
97
113
|
!isInlineCallbackRef(context.library, field.type) &&
|
|
98
|
-
|
|
114
|
+
isMarshalableField(context, field);
|
|
99
115
|
|
|
100
116
|
const emitFieldWrite = (context: ModuleContext, spec: FieldWriteSpec): string => {
|
|
101
117
|
const { descriptor, slot, targetExpr, valueExpr } = spec;
|
|
102
118
|
context.addRuntimeImport("write");
|
|
103
119
|
|
|
104
120
|
if (slot.bitWidth === undefined) {
|
|
105
|
-
|
|
121
|
+
context.addRuntimeImport("toNative");
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
`write(${targetExpr}, ${descriptor}, ${String(slot.byteOffset)}, ` +
|
|
125
|
+
`toNative(${descriptor}, ${valueExpr}));`
|
|
126
|
+
);
|
|
106
127
|
}
|
|
107
128
|
|
|
108
129
|
context.addRuntimeImport("read");
|
|
@@ -192,8 +213,10 @@ const renderRecordFieldAccessor = (
|
|
|
192
213
|
|
|
193
214
|
// A record field whose C type is the record itself rather than a pointer to it holds the value
|
|
194
215
|
// in place, so its own address is the value and no word may be loaded out of it.
|
|
195
|
-
const
|
|
196
|
-
|
|
216
|
+
const descriptor = context.hoistDescriptor(
|
|
217
|
+
renderDescriptor(context, field.type, "none", { isInline: isInlineField(context, field) }),
|
|
218
|
+
);
|
|
219
|
+
|
|
197
220
|
const tsType = renderTsType(context, field.type, false);
|
|
198
221
|
|
|
199
222
|
const accessorOptions: AccessorOptions = {
|
|
@@ -246,6 +269,11 @@ const isAccessorEligibleType = (context: ModuleContext, ref: TypeId): boolean =>
|
|
|
246
269
|
}
|
|
247
270
|
};
|
|
248
271
|
|
|
272
|
+
// A field whose record is embedded by value is read and written through its own bytes, so the
|
|
273
|
+
// constructor and the setter have to describe it the same way.
|
|
274
|
+
const isInlineField = (context: ModuleContext, field: GirField): boolean =>
|
|
275
|
+
field.type !== undefined && resolveInlineStructFields(context, field.type, field.cType) !== undefined;
|
|
276
|
+
|
|
249
277
|
const resolveInlineStructFields = (
|
|
250
278
|
context: ModuleContext,
|
|
251
279
|
ref: TypeId | undefined,
|
|
@@ -604,4 +632,11 @@ const setterBlock = (options: AccessorOptions): string =>
|
|
|
604
632
|
setterBody(options.context, options.descriptor, options.slot),
|
|
605
633
|
);
|
|
606
634
|
|
|
607
|
-
export {
|
|
635
|
+
export {
|
|
636
|
+
isEmittableField,
|
|
637
|
+
isInlineField,
|
|
638
|
+
emitFieldWrite,
|
|
639
|
+
resolveRecordFieldEntry,
|
|
640
|
+
renderRecordFieldAccessor,
|
|
641
|
+
type RecordFieldEntry,
|
|
642
|
+
};
|
package/src/store/gi/record.ts
CHANGED
|
@@ -3,7 +3,6 @@ import type { ModuleContext } from "../../writer/context.js";
|
|
|
3
3
|
import { renderJsDoc } from "../../writer/doc.js";
|
|
4
4
|
import { indentMembers } from "../../writer/emit.js";
|
|
5
5
|
import { type Callables, dedupeCallables, generateBindings, renderPlainTypeMembers } from "./callables.js";
|
|
6
|
-
import { isClassStructRecord } from "./class-struct-record.js";
|
|
7
6
|
import { renderSourceGtype } from "./gtype-binding.js";
|
|
8
7
|
import { renderRecordConstructor, renderRecordConstructorPropsInterface } from "./record-constructor.js";
|
|
9
8
|
import { renderRecordFieldAccessor } from "./record-field-accessor.js";
|
|
@@ -18,18 +17,10 @@ const generateRecord = (context: ModuleContext, record: GirRecord): void => {
|
|
|
18
17
|
return;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
if (record.isVtable) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
20
|
if (record.name.length === 0) {
|
|
26
21
|
return;
|
|
27
22
|
}
|
|
28
23
|
|
|
29
|
-
if (isClassStructRecord(context.library, context.namespace.name, record)) {
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
24
|
const className = record.name;
|
|
34
25
|
const isErrorSubclass = isGErrorRecord(context, record);
|
|
35
26
|
|