@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,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Container-slot mapping for compound widgets — JSX element name to the
|
|
3
|
+
* camelCase GTK method names that append a child onto the widget.
|
|
4
|
+
*
|
|
5
|
+
* Each method name doubles as a `ReactNode` prop on the compound and as the
|
|
6
|
+
* `<ContainerSlot id="…">` identifier the reconciler dispatches to. For
|
|
7
|
+
* example `packStart` on `AdwHeaderBar` maps to `adw_header_bar_pack_start(…)`;
|
|
8
|
+
* `<AdwHeaderBar packStart={…} />` packs the value at the start.
|
|
9
|
+
*
|
|
10
|
+
* Unlike setter slots ({@link BUILT_IN_SLOT_PROPS}), a container slot accepts
|
|
11
|
+
* multiple children and appends rather than replaces.
|
|
12
|
+
*
|
|
13
|
+
* The list is small and stable; defining it in code keeps the lookup
|
|
14
|
+
* fast and avoids one more GIR-introspection pass.
|
|
15
|
+
*/
|
|
16
|
+
const CONTAINER_SLOTS: Readonly<Record<string, readonly string[]>> = Object.freeze({
|
|
17
|
+
AdwActionRow: ["addPrefix", "addSuffix"],
|
|
18
|
+
AdwEntryRow: ["addPrefix", "addSuffix"],
|
|
19
|
+
AdwExpanderRow: ["addPrefix", "addSuffix", "addRow", "addAction"],
|
|
20
|
+
AdwHeaderBar: ["packStart", "packEnd"],
|
|
21
|
+
AdwToolbarView: ["addTopBar", "addBottomBar"],
|
|
22
|
+
GtkActionBar: ["packStart", "packEnd"],
|
|
23
|
+
GtkHeaderBar: ["packStart", "packEnd"],
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns the container-slot method names for a JSX element name, or an
|
|
28
|
+
* empty array if no container slots exist.
|
|
29
|
+
*
|
|
30
|
+
* @param jsxName - The JSX element name (PascalCase / GLib type name)
|
|
31
|
+
*/
|
|
32
|
+
export const containerSlotsFor = (jsxName: string): readonly string[] => CONTAINER_SLOTS[jsxName] ?? [];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Virtual-child subcomponent mapping — JSX element name to the map of
|
|
36
|
+
* subcomponent property → virtual JSX intrinsic name it routes through.
|
|
37
|
+
*
|
|
38
|
+
* For example `GtkConstraintLayout.Constraint` renders the
|
|
39
|
+
* `<Constraint>` virtual intrinsic so the reconciler can attach a
|
|
40
|
+
* GtkConstraint to the layout. Unlike container slots, virtual
|
|
41
|
+
* subcomponents render an existing intrinsic element rather than
|
|
42
|
+
* invoking a setter on the parent widget.
|
|
43
|
+
*/
|
|
44
|
+
const VIRTUAL_SUBCOMPONENTS: Readonly<
|
|
45
|
+
Record<string, ReadonlyArray<{ readonly child: string; readonly intrinsic: string }>>
|
|
46
|
+
> = Object.freeze({
|
|
47
|
+
GtkShortcutController: [{ child: "Shortcut", intrinsic: "Shortcut" }],
|
|
48
|
+
GtkConstraintLayout: [
|
|
49
|
+
{ child: "Constraint", intrinsic: "Constraint" },
|
|
50
|
+
{ child: "Guide", intrinsic: "ConstraintGuide" },
|
|
51
|
+
{ child: "Widget", intrinsic: "ConstraintLayoutWidget" },
|
|
52
|
+
{ child: "Vfl", intrinsic: "ConstraintVfl" },
|
|
53
|
+
],
|
|
54
|
+
GtkStack: [{ child: "Page", intrinsic: "StackPage" }],
|
|
55
|
+
AdwViewStack: [{ child: "Page", intrinsic: "StackPage" }],
|
|
56
|
+
GtkNotebook: [
|
|
57
|
+
{ child: "Page", intrinsic: "NotebookPage" },
|
|
58
|
+
{ child: "PageTab", intrinsic: "NotebookPageTab" },
|
|
59
|
+
],
|
|
60
|
+
GtkGrid: [{ child: "Child", intrinsic: "GridChild" }],
|
|
61
|
+
GtkFixed: [{ child: "Child", intrinsic: "FixedChild" }],
|
|
62
|
+
GtkOverlay: [{ child: "Child", intrinsic: "OverlayChild" }],
|
|
63
|
+
GtkColumnView: [{ child: "Column", intrinsic: "ColumnViewColumn" }],
|
|
64
|
+
GtkTextView: [
|
|
65
|
+
{ child: "Tag", intrinsic: "TextTag" },
|
|
66
|
+
{ child: "Anchor", intrinsic: "TextAnchor" },
|
|
67
|
+
{ child: "Paintable", intrinsic: "TextPaintable" },
|
|
68
|
+
],
|
|
69
|
+
GtkSourceView: [
|
|
70
|
+
{ child: "Tag", intrinsic: "TextTag" },
|
|
71
|
+
{ child: "Anchor", intrinsic: "TextAnchor" },
|
|
72
|
+
{ child: "Paintable", intrinsic: "TextPaintable" },
|
|
73
|
+
],
|
|
74
|
+
GtkMenuButton: [
|
|
75
|
+
{ child: "MenuItem", intrinsic: "MenuItem" },
|
|
76
|
+
{ child: "MenuSection", intrinsic: "MenuSection" },
|
|
77
|
+
{ child: "MenuSubmenu", intrinsic: "MenuSubmenu" },
|
|
78
|
+
],
|
|
79
|
+
GtkPopoverMenu: [
|
|
80
|
+
{ child: "Item", intrinsic: "MenuItem" },
|
|
81
|
+
{ child: "Section", intrinsic: "MenuSection" },
|
|
82
|
+
{ child: "Submenu", intrinsic: "MenuSubmenu" },
|
|
83
|
+
],
|
|
84
|
+
GtkPopoverMenuBar: [
|
|
85
|
+
{ child: "Item", intrinsic: "MenuItem" },
|
|
86
|
+
{ child: "Section", intrinsic: "MenuSection" },
|
|
87
|
+
{ child: "Submenu", intrinsic: "MenuSubmenu" },
|
|
88
|
+
],
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Returns the virtual-subcomponent record for a JSX element name, or an
|
|
93
|
+
* empty array if no virtual subcomponents exist.
|
|
94
|
+
*
|
|
95
|
+
* @param jsxName - The JSX element name (PascalCase / GLib type name)
|
|
96
|
+
*/
|
|
97
|
+
export const virtualSubcomponentsFor = (
|
|
98
|
+
jsxName: string,
|
|
99
|
+
): ReadonlyArray<{ readonly child: string; readonly intrinsic: string }> => VIRTUAL_SUBCOMPONENTS[jsxName] ?? [];
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { quote } from "@gtkx/utils";
|
|
2
|
+
import { indent } from "../dsl/emit.js";
|
|
3
|
+
import type { GirRepository } from "../gir/repository.js";
|
|
4
|
+
import { containerSlotsFor, virtualSubcomponentsFor } from "./compounds-meta.js";
|
|
5
|
+
import { mergeSlotProps } from "./slot-props.js";
|
|
6
|
+
import { ancestorGlibNames, isWidgetClass, iterateClassesWithGlibName, type WidgetCandidate } from "./widgets.js";
|
|
7
|
+
|
|
8
|
+
type CompoundAccumulator = {
|
|
9
|
+
readonly elementNames: Set<string>;
|
|
10
|
+
readonly compoundPropTypes: Set<string>;
|
|
11
|
+
readonly virtualPropTypes: Set<string>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generates `compounds.tsx` source — one statically-compiled React
|
|
16
|
+
* component per widget that has slot props, container-slot props, or
|
|
17
|
+
* virtual-child sub-components.
|
|
18
|
+
*
|
|
19
|
+
* Each component is plain JSX: it destructures its slot and container-slot
|
|
20
|
+
* props, forwards the rest to the host intrinsic, and wraps each non-null
|
|
21
|
+
* slot value in a `<Slot>` (setter) or `<ContainerSlot>` (append) element.
|
|
22
|
+
* Virtual-child sub-components (e.g. `GtkStack.Page`) are attached with
|
|
23
|
+
* `Object.assign` under an explicit intersection type so isolated
|
|
24
|
+
* declaration emit succeeds.
|
|
25
|
+
*
|
|
26
|
+
* A host intrinsic is referenced through a module-local
|
|
27
|
+
* `${Name}Element = "Name"` const so the JSX tag does not collide with the
|
|
28
|
+
* exported component of the same name. Widgets with no slots, container
|
|
29
|
+
* slots, or virtual children remain bare string-constant intrinsics in
|
|
30
|
+
* `jsx.ts`.
|
|
31
|
+
*
|
|
32
|
+
* @param repository - The loaded GIR repository
|
|
33
|
+
* @param userSlotProps - User-supplied slot-prop overrides
|
|
34
|
+
*/
|
|
35
|
+
export const generateCompounds = (
|
|
36
|
+
repository: GirRepository,
|
|
37
|
+
userSlotProps: Readonly<Record<string, readonly string[]>> | undefined,
|
|
38
|
+
): { readonly source: string; readonly exportedNames: ReadonlySet<string> } => {
|
|
39
|
+
const slotPropMap = mergeSlotProps(userSlotProps);
|
|
40
|
+
const exportedNames = new Set<string>();
|
|
41
|
+
const exportLines: string[] = [];
|
|
42
|
+
const accumulator: CompoundAccumulator = {
|
|
43
|
+
elementNames: new Set<string>(),
|
|
44
|
+
compoundPropTypes: new Set<string>(),
|
|
45
|
+
virtualPropTypes: new Set<string>(),
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
for (const { glibName, klass, namespace } of collectCompoundCandidates(repository)) {
|
|
49
|
+
const slots = resolveInheritedSlots(slotPropMap, klass, namespace, repository);
|
|
50
|
+
const containers = containerSlotsFor(glibName);
|
|
51
|
+
const virtuals = virtualSubcomponentsFor(glibName);
|
|
52
|
+
if (slots.length === 0 && containers.length === 0 && virtuals.length === 0) continue;
|
|
53
|
+
|
|
54
|
+
exportLines.push(renderCompound({ glibName, slots, containers, virtuals }, accumulator));
|
|
55
|
+
exportedNames.add(glibName);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const sections = [
|
|
59
|
+
renderImportLines(accumulator).join("\n"),
|
|
60
|
+
renderElementConsts(accumulator),
|
|
61
|
+
exportLines.join("\n\n"),
|
|
62
|
+
];
|
|
63
|
+
const source = `${sections.filter((section) => section.length > 0).join("\n\n")}\n`;
|
|
64
|
+
return { source, exportedNames };
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
type CompoundShape = {
|
|
68
|
+
readonly glibName: string;
|
|
69
|
+
readonly slots: readonly string[];
|
|
70
|
+
readonly containers: readonly string[];
|
|
71
|
+
readonly virtuals: ReadonlyArray<{ readonly child: string; readonly intrinsic: string }>;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const renderCompound = (shape: CompoundShape, accumulator: CompoundAccumulator): string => {
|
|
75
|
+
const { glibName, slots, containers, virtuals } = shape;
|
|
76
|
+
const propsType = `${glibName}Props`;
|
|
77
|
+
accumulator.compoundPropTypes.add(propsType);
|
|
78
|
+
accumulator.elementNames.add(glibName);
|
|
79
|
+
if (slots.length > 0) accumulator.elementNames.add("Slot");
|
|
80
|
+
if (containers.length > 0) accumulator.elementNames.add("ContainerSlot");
|
|
81
|
+
|
|
82
|
+
const component = renderComponentFunction(glibName, propsType, slots, containers);
|
|
83
|
+
if (virtuals.length === 0) {
|
|
84
|
+
return `export const ${glibName} = ${component};`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const subcomponents = virtuals.map((virtual) => renderVirtualSubcomponent(virtual, accumulator));
|
|
88
|
+
const fields = subcomponents.map((sub) => ` ${sub.field};`).join("\n");
|
|
89
|
+
const impls = subcomponents.map((sub) => indent(`${sub.impl},`, 1)).join("\n");
|
|
90
|
+
return [
|
|
91
|
+
`export const ${glibName}: ((props: ${propsType}) => ReactNode) & {`,
|
|
92
|
+
fields,
|
|
93
|
+
`} = Object.assign(`,
|
|
94
|
+
`${indent(component, 1)},`,
|
|
95
|
+
" {",
|
|
96
|
+
impls,
|
|
97
|
+
" },",
|
|
98
|
+
");",
|
|
99
|
+
].join("\n");
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const renderComponentFunction = (
|
|
103
|
+
glibName: string,
|
|
104
|
+
propsType: string,
|
|
105
|
+
slots: readonly string[],
|
|
106
|
+
containers: readonly string[],
|
|
107
|
+
): string => {
|
|
108
|
+
const host = `${glibName}Element`;
|
|
109
|
+
if (slots.length === 0 && containers.length === 0) {
|
|
110
|
+
return [
|
|
111
|
+
`(props: ${propsType}): ReactNode => {`,
|
|
112
|
+
" const { children, ...rest } = props;",
|
|
113
|
+
` return <${host} {...rest}>{children}</${host}>;`,
|
|
114
|
+
"}",
|
|
115
|
+
].join("\n");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const destructured = [...slots, ...containers, "children"].join(", ");
|
|
119
|
+
const slotChildren = [
|
|
120
|
+
...slots.map((slot) => renderSlotChild("SlotElement", slot)),
|
|
121
|
+
...containers.map((container) => renderSlotChild("ContainerSlotElement", container)),
|
|
122
|
+
];
|
|
123
|
+
return [
|
|
124
|
+
`(props: ${propsType}): ReactNode => {`,
|
|
125
|
+
` const { ${destructured}, ...rest } = props;`,
|
|
126
|
+
" return (",
|
|
127
|
+
` <${host} {...rest}>`,
|
|
128
|
+
" {children}",
|
|
129
|
+
...slotChildren.map((child) => ` ${child}`),
|
|
130
|
+
` </${host}>`,
|
|
131
|
+
" );",
|
|
132
|
+
"}",
|
|
133
|
+
].join("\n");
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const renderSlotChild = (elementConst: string, slot: string): string =>
|
|
137
|
+
`{${slot} != null && <${elementConst} id=${quote(slot)}>{${slot}}</${elementConst}>}`;
|
|
138
|
+
|
|
139
|
+
const renderVirtualSubcomponent = (
|
|
140
|
+
virtual: { readonly child: string; readonly intrinsic: string },
|
|
141
|
+
accumulator: CompoundAccumulator,
|
|
142
|
+
): { readonly field: string; readonly impl: string } => {
|
|
143
|
+
const { child, intrinsic } = virtual;
|
|
144
|
+
const propsType = `${intrinsic}Props`;
|
|
145
|
+
accumulator.virtualPropTypes.add(propsType);
|
|
146
|
+
accumulator.elementNames.add(intrinsic);
|
|
147
|
+
return {
|
|
148
|
+
field: `${child}: (props: ${propsType}) => ReactNode`,
|
|
149
|
+
impl: `${child}: (props: ${propsType}): ReactNode => <${intrinsic}Element {...props} />`,
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const renderElementConsts = (accumulator: CompoundAccumulator): string =>
|
|
154
|
+
[...accumulator.elementNames]
|
|
155
|
+
.sort((a, b) => a.localeCompare(b))
|
|
156
|
+
.map((name) => `const ${name}Element = ${quote(name)} as const;`)
|
|
157
|
+
.join("\n");
|
|
158
|
+
|
|
159
|
+
const renderImportLines = (accumulator: CompoundAccumulator): readonly string[] => {
|
|
160
|
+
const lines = ['import type { ReactNode } from "react";'];
|
|
161
|
+
const compoundPropTypes = [...accumulator.compoundPropTypes].sort((a, b) => a.localeCompare(b));
|
|
162
|
+
if (compoundPropTypes.length > 0) {
|
|
163
|
+
lines.push(`import type { ${compoundPropTypes.join(", ")} } from "./jsx.js";`);
|
|
164
|
+
}
|
|
165
|
+
const virtualPropTypes = [...accumulator.virtualPropTypes].sort((a, b) => a.localeCompare(b));
|
|
166
|
+
if (virtualPropTypes.length > 0) {
|
|
167
|
+
lines.push(`import type { ${virtualPropTypes.join(", ")} } from "@gtkx/react";`);
|
|
168
|
+
}
|
|
169
|
+
return lines;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const resolveInheritedSlots = (
|
|
173
|
+
slotPropMap: Readonly<Record<string, readonly string[]>>,
|
|
174
|
+
klass: WidgetCandidate["klass"],
|
|
175
|
+
namespace: WidgetCandidate["namespace"],
|
|
176
|
+
repository: GirRepository,
|
|
177
|
+
): readonly string[] => {
|
|
178
|
+
const seen = new Set<string>();
|
|
179
|
+
const slots: string[] = [];
|
|
180
|
+
for (const glibName of ancestorGlibNames(klass, namespace, repository)) {
|
|
181
|
+
for (const slot of slotPropMap[glibName] ?? []) {
|
|
182
|
+
if (seen.has(slot)) continue;
|
|
183
|
+
seen.add(slot);
|
|
184
|
+
slots.push(slot);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return slots;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const collectCompoundCandidates = (repository: GirRepository): readonly WidgetCandidate[] => {
|
|
191
|
+
const seen = new Set<string>();
|
|
192
|
+
const candidates: WidgetCandidate[] = [];
|
|
193
|
+
for (const candidate of iterateClassesWithGlibName(repository)) {
|
|
194
|
+
const { glibName, klass, namespace } = candidate;
|
|
195
|
+
const isWidget = isWidgetClass(klass, namespace, repository);
|
|
196
|
+
const hasContainerSlot = containerSlotsFor(glibName).length > 0;
|
|
197
|
+
const hasVirtualChild = virtualSubcomponentsFor(glibName).length > 0;
|
|
198
|
+
if (!isWidget && !hasContainerSlot && !hasVirtualChild) continue;
|
|
199
|
+
if (seen.has(glibName)) continue;
|
|
200
|
+
seen.add(glibName);
|
|
201
|
+
candidates.push(candidate);
|
|
202
|
+
}
|
|
203
|
+
return candidates.sort((a, b) => a.glibName.localeCompare(b.glibName));
|
|
204
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { quote, toCamelCase, toIdentifier } from "@gtkx/utils";
|
|
2
|
+
import type { GirClass } from "../gir/class.js";
|
|
3
|
+
import type { GirRepository } from "../gir/repository.js";
|
|
4
|
+
import { implementedInterfaces, isReactNodeClass, iterateClassesWithGlibName, signalHandlerName } from "./widgets.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Generates `internal.ts` source — the `SIGNALS` and `CONSTRUCT_ONLY_PROPS`
|
|
8
|
+
* tables consumed by the React metadata resolver.
|
|
9
|
+
*
|
|
10
|
+
* Walks every widget across every loaded namespace and emits one entry
|
|
11
|
+
* per GLib type name. Signals are mapped from kebab-case to `onCamelCase`;
|
|
12
|
+
* construct-only properties are surfaced as a `Set` for the runtime to
|
|
13
|
+
* consult on mount.
|
|
14
|
+
*
|
|
15
|
+
* @param repository - The loaded GIR repository
|
|
16
|
+
*/
|
|
17
|
+
export const generateInternal = (repository: GirRepository): string => {
|
|
18
|
+
const widgets = collectWidgets(repository);
|
|
19
|
+
const signalsEntries = widgets.map(
|
|
20
|
+
({ glibName, signals }) => ` "${glibName}": ${renderSignalsObject(signals)},`,
|
|
21
|
+
);
|
|
22
|
+
const constructOnlyEntries = widgets
|
|
23
|
+
.filter(({ constructOnly }) => constructOnly.length > 0)
|
|
24
|
+
.map(({ glibName, constructOnly }) => ` "${glibName}": new Set([${constructOnly.map(quote).join(",")}]),`);
|
|
25
|
+
|
|
26
|
+
const namespaces = [...new Set(widgets.map((widget) => widget.namespace))].sort((a, b) => a.localeCompare(b));
|
|
27
|
+
const preamble = namespaces.map((name) => `import "@gtkx/gi/${name.toLowerCase()}";`).join("\n");
|
|
28
|
+
|
|
29
|
+
return `${[
|
|
30
|
+
preamble,
|
|
31
|
+
`export const SIGNALS: Readonly<Record<string, Readonly<Record<string, string>>>> = {\n${signalsEntries.join("\n")}\n};`,
|
|
32
|
+
`export const CONSTRUCT_ONLY_PROPS: Readonly<Record<string, ReadonlySet<string>>> = {\n${constructOnlyEntries.join("\n")}\n};`,
|
|
33
|
+
].join("\n\n")}\n`;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type WidgetEntry = {
|
|
37
|
+
readonly glibName: string;
|
|
38
|
+
readonly namespace: string;
|
|
39
|
+
readonly signals: ReadonlyArray<readonly [string, string]>;
|
|
40
|
+
readonly constructOnly: readonly string[];
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const collectWidgets = (repository: GirRepository): readonly WidgetEntry[] => {
|
|
44
|
+
const entries: WidgetEntry[] = [];
|
|
45
|
+
for (const { glibName, klass, namespace } of iterateClassesWithGlibName(repository)) {
|
|
46
|
+
if (!isReactNodeClass(klass, namespace, repository)) continue;
|
|
47
|
+
const sources = [klass, ...implementedInterfaces(klass, namespace, repository).map((entry) => entry.klass)];
|
|
48
|
+
entries.push({
|
|
49
|
+
glibName,
|
|
50
|
+
namespace: namespace.name,
|
|
51
|
+
signals: collectSignals(sources),
|
|
52
|
+
constructOnly: collectConstructOnly(sources),
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return entries.sort((a, b) => a.glibName.localeCompare(b.glibName));
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const collectSignals = (sources: readonly GirClass[]): ReadonlyArray<readonly [string, string]> => {
|
|
59
|
+
const seen = new Set<string>();
|
|
60
|
+
const signals: Array<readonly [string, string]> = [];
|
|
61
|
+
for (const source of sources) {
|
|
62
|
+
for (const signal of source.signals) {
|
|
63
|
+
const handlerName = signalHandlerName(signal.name);
|
|
64
|
+
if (seen.has(handlerName)) continue;
|
|
65
|
+
seen.add(handlerName);
|
|
66
|
+
signals.push([handlerName, signal.name] as const);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return signals;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const collectConstructOnly = (sources: readonly GirClass[]): readonly string[] => {
|
|
73
|
+
const seen = new Set<string>();
|
|
74
|
+
const names: string[] = [];
|
|
75
|
+
for (const source of sources) {
|
|
76
|
+
for (const property of source.properties) {
|
|
77
|
+
if (!property.constructOnly) continue;
|
|
78
|
+
const jsName = toIdentifier(toCamelCase(property.name));
|
|
79
|
+
if (seen.has(jsName)) continue;
|
|
80
|
+
seen.add(jsName);
|
|
81
|
+
names.push(jsName);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return names;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const renderSignalsObject = (entries: ReadonlyArray<readonly [string, string]>): string => {
|
|
88
|
+
if (entries.length === 0) return "{}";
|
|
89
|
+
const lines = entries.map(([key, value]) => ` ${quote(key)}: ${quote(value)}`);
|
|
90
|
+
return `{\n${lines.join(",\n")}\n }`;
|
|
91
|
+
};
|
package/src/react/jsx.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { quote } from "@gtkx/utils";
|
|
2
|
+
import type { GirClass } from "../gir/class.js";
|
|
3
|
+
import { splitQualifiedName } from "../gir/qualified-name.js";
|
|
4
|
+
import type { GirRepository } from "../gir/repository.js";
|
|
5
|
+
import { containerSlotsFor } from "./compounds-meta.js";
|
|
6
|
+
import { buildWidgetPropsEntries } from "./props.js";
|
|
7
|
+
import { isReactNodeClass, iterateClassesWithGlibName, type WidgetCandidate } from "./widgets.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generates `jsx.ts` source — one `export const Name = "Name"` per JSX
|
|
11
|
+
* intrinsic element (every GtkWidget descendant plus event controllers
|
|
12
|
+
* and layout managers) that is NOT exported as a slot-widget compound,
|
|
13
|
+
* plus an `export type NameProps` permissive prop bag per intrinsic and
|
|
14
|
+
* a `WidgetSlotNames` index of every widget's allowed slot names.
|
|
15
|
+
*
|
|
16
|
+
* Compounds.ts owns its widgets' runtime exports. To prevent ambiguous
|
|
17
|
+
* re-exports when consumers wildcard both `compounds.js` and `jsx.js`,
|
|
18
|
+
* widgets covered by `excludeNames` are skipped here and the consumer
|
|
19
|
+
* sees a single function-typed export from compounds instead of the
|
|
20
|
+
* string-literal constant. Prop types are still emitted for every
|
|
21
|
+
* widget — they live in the type namespace and never clash with
|
|
22
|
+
* compounds.
|
|
23
|
+
*
|
|
24
|
+
* @param repository - The loaded GIR repository
|
|
25
|
+
* @param excludeNames - Widgets already exported by `compounds.ts`
|
|
26
|
+
* @param slotPropMap - Per-widget slot prop name overrides
|
|
27
|
+
*/
|
|
28
|
+
export const generateJsx = (
|
|
29
|
+
repository: GirRepository,
|
|
30
|
+
excludeNames: ReadonlySet<string> = new Set(),
|
|
31
|
+
slotPropMap: Readonly<Record<string, readonly string[]>> = {},
|
|
32
|
+
): string => {
|
|
33
|
+
const widgets = collectWidgets(repository);
|
|
34
|
+
const intrinsicWidgets = widgets.filter((entry) => !excludeNames.has(entry.glibName));
|
|
35
|
+
const constLines = intrinsicWidgets.map(
|
|
36
|
+
(entry) => `export const ${entry.glibName} = ${quote(entry.glibName)} as const;`,
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const widgetByGlibName = new Map(widgets.map((entry) => [entry.glibName, entry]));
|
|
40
|
+
const isWidgetAncestor = (candidate: GirClass): boolean => {
|
|
41
|
+
const candidateGlib = candidate.glibTypeName ?? candidate.cType;
|
|
42
|
+
return candidateGlib !== undefined && widgetByGlibName.has(candidateGlib);
|
|
43
|
+
};
|
|
44
|
+
const namespaceImports = new Map<string, string>();
|
|
45
|
+
const propBlocks: string[] = ["export interface WidgetProps {\n name?: string;\n}"];
|
|
46
|
+
for (const entry of widgets) {
|
|
47
|
+
const block = renderPropBlock(repository, entry, {
|
|
48
|
+
slotPropMap,
|
|
49
|
+
isWidgetAncestor,
|
|
50
|
+
widgetByGlibName,
|
|
51
|
+
namespaceImports,
|
|
52
|
+
});
|
|
53
|
+
propBlocks.push(block);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const importLines = renderImportLines(namespaceImports);
|
|
57
|
+
const slotEntries = widgets.map((entry) => ` readonly ${quote(entry.glibName)}: string`);
|
|
58
|
+
const slotNamesLine = `export type WidgetSlotNames = {\n${slotEntries.join(";\n")};\n};`;
|
|
59
|
+
const intrinsicEntries = widgets.map((entry) => ` ${entry.glibName}: ${entry.glibName}Props;`);
|
|
60
|
+
const jsxAugmentation = [
|
|
61
|
+
"declare global {",
|
|
62
|
+
" namespace React.JSX {",
|
|
63
|
+
" interface IntrinsicElements {",
|
|
64
|
+
...intrinsicEntries,
|
|
65
|
+
" }",
|
|
66
|
+
" }",
|
|
67
|
+
"}",
|
|
68
|
+
].join("\n");
|
|
69
|
+
const body = [
|
|
70
|
+
importLines.join("\n"),
|
|
71
|
+
"",
|
|
72
|
+
constLines.join("\n"),
|
|
73
|
+
"",
|
|
74
|
+
propBlocks.join("\n\n"),
|
|
75
|
+
"",
|
|
76
|
+
slotNamesLine,
|
|
77
|
+
"",
|
|
78
|
+
jsxAugmentation,
|
|
79
|
+
].join("\n");
|
|
80
|
+
return `${body}\n`;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
type RenderPropBlockContext = {
|
|
84
|
+
readonly slotPropMap: Readonly<Record<string, readonly string[]>>;
|
|
85
|
+
readonly isWidgetAncestor: (candidate: GirClass) => boolean;
|
|
86
|
+
readonly widgetByGlibName: ReadonlyMap<string, WidgetCandidate>;
|
|
87
|
+
readonly namespaceImports: Map<string, string>;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const renderPropBlock = (
|
|
91
|
+
repository: GirRepository,
|
|
92
|
+
entry: WidgetCandidate,
|
|
93
|
+
context: RenderPropBlockContext,
|
|
94
|
+
): string => {
|
|
95
|
+
const slotPropNames = new Set(context.slotPropMap[entry.glibName] ?? []);
|
|
96
|
+
const { propLines, imports } = buildWidgetPropsEntries({
|
|
97
|
+
repository,
|
|
98
|
+
klass: entry.klass,
|
|
99
|
+
slotPropNames,
|
|
100
|
+
isWidgetAncestor: context.isWidgetAncestor,
|
|
101
|
+
});
|
|
102
|
+
for (const [namespace, alias] of imports) context.namespaceImports.set(namespace, alias);
|
|
103
|
+
context.namespaceImports.set(entry.namespace.name, entry.namespace.name);
|
|
104
|
+
const widgetTypeRef = `${entry.namespace.name}.${entry.klass.name} | null`;
|
|
105
|
+
const ownerLines = [
|
|
106
|
+
" children?: ReactNode;",
|
|
107
|
+
` ref?: Ref<${widgetTypeRef}>;`,
|
|
108
|
+
...propLines.map((line) => ` ${line}`),
|
|
109
|
+
...containerSlotsFor(entry.glibName).map((method) => ` ${method}?: ReactNode | null;`),
|
|
110
|
+
];
|
|
111
|
+
const parentExtends = resolveParentPropsExtension(repository, entry, context.widgetByGlibName);
|
|
112
|
+
return `export interface ${entry.glibName}Props extends ${parentExtends} {\n${ownerLines.join("\n")}\n}`;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const renderImportLines = (namespaceImports: ReadonlyMap<string, string>): readonly string[] => {
|
|
116
|
+
const lines = ['import type { ReactNode, Ref } from "react";'];
|
|
117
|
+
for (const [namespaceName, alias] of namespaceImports) {
|
|
118
|
+
if (namespaceName === "") continue;
|
|
119
|
+
const directory = namespaceName.toLowerCase();
|
|
120
|
+
lines.push(`import type * as ${alias} from "${ffiImportPath(directory)}";`);
|
|
121
|
+
}
|
|
122
|
+
return lines;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const collectWidgets = (repository: GirRepository): readonly WidgetCandidate[] => {
|
|
126
|
+
const entries: WidgetCandidate[] = [];
|
|
127
|
+
const seen = new Set<string>();
|
|
128
|
+
for (const candidate of iterateClassesWithGlibName(repository)) {
|
|
129
|
+
const { glibName, klass, namespace } = candidate;
|
|
130
|
+
if (!isReactNodeClass(klass, namespace, repository)) continue;
|
|
131
|
+
if (seen.has(glibName)) continue;
|
|
132
|
+
seen.add(glibName);
|
|
133
|
+
entries.push(candidate);
|
|
134
|
+
}
|
|
135
|
+
return entries.sort((a, b) => a.glibName.localeCompare(b.glibName));
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const ffiImportPath = (directory: string): string => `@gtkx/gi/${directory}`;
|
|
139
|
+
|
|
140
|
+
const resolveParentPropsExtension = (
|
|
141
|
+
repository: GirRepository,
|
|
142
|
+
entry: WidgetCandidate,
|
|
143
|
+
widgetByGlibName: ReadonlyMap<string, WidgetCandidate>,
|
|
144
|
+
): string => {
|
|
145
|
+
const parent = entry.klass.parent;
|
|
146
|
+
if (parent === undefined) return "WidgetProps";
|
|
147
|
+
const { namespaceName, typeName } = splitQualifiedName(parent, entry.namespace.name);
|
|
148
|
+
const resolved = repository.resolveNamed(namespaceName, typeName);
|
|
149
|
+
if (resolved === undefined) return "WidgetProps";
|
|
150
|
+
if (resolved.kind !== "class" && resolved.kind !== "interface") return "WidgetProps";
|
|
151
|
+
const parentGlib = resolved.value.glibTypeName ?? resolved.value.cType;
|
|
152
|
+
if (parentGlib === undefined) return "WidgetProps";
|
|
153
|
+
if (!widgetByGlibName.has(parentGlib)) return "WidgetProps";
|
|
154
|
+
return `${parentGlib}Props`;
|
|
155
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { GirRepository } from "../gir/repository.js";
|
|
2
|
+
import { generateCompounds } from "./compounds.js";
|
|
3
|
+
import { generateInternal } from "./internal.js";
|
|
4
|
+
import { generateJsx } from "./jsx.js";
|
|
5
|
+
import { mergeSlotProps } from "./slot-props.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Produces the three React generated files (`jsx.ts`, `internal.ts`,
|
|
9
|
+
* `compounds.tsx`) plus a summary widget count for the runner result.
|
|
10
|
+
*
|
|
11
|
+
* @param repository - The loaded GIR repository
|
|
12
|
+
* @param userSlotProps - Slot-prop overrides from `gtkx.config.ts`
|
|
13
|
+
*/
|
|
14
|
+
export const generateReactFiles = (
|
|
15
|
+
repository: GirRepository,
|
|
16
|
+
userSlotProps: Readonly<Record<string, readonly string[]>> | undefined,
|
|
17
|
+
): { readonly files: Map<string, string>; readonly widgetCount: number } => {
|
|
18
|
+
const slotPropMap = mergeSlotProps(userSlotProps);
|
|
19
|
+
const compounds = generateCompounds(repository, userSlotProps);
|
|
20
|
+
const jsx = generateJsx(repository, compounds.exportedNames, slotPropMap);
|
|
21
|
+
const internal = generateInternal(repository);
|
|
22
|
+
const files = new Map<string, string>([
|
|
23
|
+
["jsx.ts", jsx],
|
|
24
|
+
["internal.ts", internal],
|
|
25
|
+
["compounds.tsx", compounds.source],
|
|
26
|
+
]);
|
|
27
|
+
const widgetCount = jsx.split("\n").filter((line) => line.startsWith("export const ")).length;
|
|
28
|
+
return { files, widgetCount };
|
|
29
|
+
};
|