dslinter 0.1.5 → 0.2.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/CHANGELOG.md +112 -0
- package/README.md +54 -27
- package/bin/dslinter.mjs +26 -5
- package/bin/lib/config-hide-component.mjs +44 -0
- package/bin/lib/config-hide-component.test.mjs +33 -0
- package/bin/lib/constants.mjs +20 -0
- package/bin/lib/dev-banner.mjs +16 -51
- package/bin/lib/dev-banner.test.mjs +20 -18
- package/bin/lib/enrich-playgrounds-from-ts.mjs +201 -0
- package/bin/lib/enrich-playgrounds-from-ts.test.mjs +74 -0
- package/bin/lib/enrich-report-cli.mjs +14 -0
- package/bin/lib/env.mjs +20 -0
- package/bin/lib/infer-prop-types-from-ts.mjs +381 -0
- package/bin/lib/infer-prop-types-from-ts.test.mjs +174 -0
- package/bin/lib/parse-args.mjs +13 -1
- package/bin/lib/parse-args.test.mjs +7 -1
- package/bin/lib/paths.mjs +8 -0
- package/bin/lib/project-root.mjs +92 -24
- package/bin/lib/project-root.test.mjs +52 -0
- package/bin/lib/prompt.mjs +31 -0
- package/bin/lib/resolve-project.mjs +78 -0
- package/bin/lib/resolve-project.test.mjs +74 -0
- package/bin/lib/run-scanner.mjs +40 -6
- package/bin/lib/scaffold-config.mjs +163 -0
- package/bin/lib/scaffold-config.test.mjs +43 -0
- package/bin/lib/scan-host.mjs +44 -0
- package/bin/lib/scan-host.test.mjs +41 -0
- package/bin/lib/setup-readiness.mjs +153 -0
- package/bin/lib/setup-readiness.test.mjs +32 -0
- package/bin/modes/build.mjs +31 -6
- package/bin/modes/dev.mjs +56 -13
- package/bin/modes/init.mjs +35 -47
- package/bin/modes/init.test.mjs +16 -0
- package/bin/modes/mcp.mjs +49 -0
- package/bin/modes/report.mjs +29 -4
- package/bin/modes/watch.mjs +85 -0
- package/dashboard-dist/assets/DashboardLayoutAuto-Bja3BuZZ.css +1 -0
- package/dashboard-dist/assets/DashboardLayoutAuto-h0gP_iKd.js +1 -0
- package/dashboard-dist/assets/axe-DDaE9JTN.js +20 -0
- package/dashboard-dist/assets/index-B9sZ6wHm.css +1 -0
- package/dashboard-dist/assets/index-DIDBt5ed.js +218 -0
- package/dashboard-dist/index.html +2 -2
- package/index.cjs +53 -52
- package/index.d.ts +3 -0
- package/package.json +18 -12
- package/shared/env.ts +15 -0
- package/shared/paths.ts +8 -0
- package/shared/reportPath.test.ts +19 -0
- package/shared/reportPath.ts +12 -0
- package/shared/servePort.ts +16 -0
- package/src/components/ComponentInspectPane.tsx +67 -19
- package/src/components/ComponentPlaygroundPane.tsx +262 -113
- package/src/components/DashboardCommandPalette.tsx +6 -11
- package/src/components/GovernancePane.tsx +2 -2
- package/src/components/HideFromCatalogButton.tsx +44 -0
- package/src/components/OpenInEditorButton.tsx +36 -0
- package/src/components/PlaygroundA11yAndCode.tsx +53 -53
- package/src/components/PlaygroundAppThemeWrapper.tsx +82 -0
- package/src/components/PlaygroundControls.tsx +5 -11
- package/src/components/PlaygroundPreviewErrorBoundary.tsx +54 -0
- package/src/components/PlaygroundUsageCode.tsx +6 -4
- package/src/components/PlaygroundVariantMatrix.tsx +101 -34
- package/src/components/Section.tsx +5 -2
- package/src/components/Sidebar.tsx +131 -46
- package/src/components/TruncatedPath.tsx +44 -0
- package/src/components/controlApiTable.test.ts +29 -0
- package/src/components/controlApiTable.ts +3 -0
- package/src/components/playgroundUsageHighlight.ts +14 -3
- package/src/components/ui/badge.tsx +1 -1
- package/src/components/ui/table.tsx +2 -2
- package/src/dashboard/ComponentCatalog.tsx +16 -23
- package/src/dashboard/ComponentUsageDetails.tsx +6 -15
- package/src/dashboard/DashboardBody.tsx +0 -35
- package/src/dashboard/FindingsList.tsx +65 -55
- package/src/dashboard/ScannedTokenWall.tsx +3 -3
- package/src/dashboard/aggregate.test.ts +74 -0
- package/src/dashboard/aggregate.ts +145 -21
- package/src/dashboard/catalogVisibility.test.ts +93 -0
- package/src/dashboard/catalogVisibility.ts +108 -0
- package/src/dashboard/editorLink.test.ts +57 -0
- package/src/dashboard/editorLink.ts +71 -0
- package/src/dashboard/paths.test.ts +49 -0
- package/src/dashboard/paths.ts +51 -3
- package/src/dashboard/updateDslintConfig.ts +22 -0
- package/src/dashboard/useWorkspaceReport.ts +21 -17
- package/src/index.ts +26 -0
- package/src/mcp/agent-context.ts +148 -0
- package/src/mcp/agent-query.test.ts +89 -0
- package/src/mcp/agent-query.ts +373 -0
- package/src/mcp/config.ts +53 -0
- package/src/mcp/index.ts +18 -0
- package/src/mcp/normalize-paths.ts +65 -0
- package/src/mcp/report-cache.ts +209 -0
- package/src/mcp/rule-catalog.json +156 -0
- package/src/mcp/rule-catalog.ts +33 -0
- package/src/mcp/schemas.ts +54 -0
- package/src/mcp/server.test.ts +44 -0
- package/src/mcp/server.ts +343 -0
- package/src/mcp/start.ts +29 -0
- package/src/mcp/verify-loop.test.ts +49 -0
- package/src/mcp/verify-loop.ts +149 -0
- package/src/playground/appPreviewTheme.test.ts +148 -0
- package/src/playground/appPreviewTheme.ts +137 -0
- package/src/playground/buildCompoundPlaygroundEntries.test.ts +348 -0
- package/src/playground/buildCompoundPlaygroundEntries.ts +625 -0
- package/src/playground/buildPlaygroundEntriesFromReport.test.ts +420 -6
- package/src/playground/buildPlaygroundEntriesFromReport.ts +206 -285
- package/src/playground/catalogIdFromPlaygroundExport.test.ts +15 -0
- package/src/playground/catalogIdFromPlaygroundExport.ts +8 -0
- package/src/playground/collectDefinedPlaygrounds.test.ts +59 -0
- package/src/playground/collectDefinedPlaygrounds.ts +68 -0
- package/src/playground/controls.ts +177 -0
- package/src/playground/createPlaygroundRegistry.ts +1 -1
- package/src/playground/definePlayground.tsx +88 -16
- package/src/playground/definePlaygroundFromKit.ts +17 -0
- package/src/playground/embedGlobKey.ts +8 -0
- package/src/playground/enrichKitControls.test.ts +25 -0
- package/src/playground/enrichKitControls.ts +197 -0
- package/src/playground/expandPlaygroundControls.test.ts +50 -0
- package/src/playground/expandPlaygroundControls.ts +97 -0
- package/src/playground/inferKitJsx.test.ts +77 -0
- package/src/playground/inferKitJsx.ts +165 -0
- package/src/playground/inferKitParams.test.ts +41 -0
- package/src/playground/inferKitParams.ts +113 -0
- package/src/playground/inferPropTypesFromTs.d.mts +47 -0
- package/src/playground/inferPropTypesFromTs.mjs +343 -0
- package/src/playground/inferPropTypesFromTs.test.ts +227 -0
- package/src/playground/inferPropTypesFromTs.ts +17 -0
- package/src/playground/mergePlaygroundEntries.test.ts +32 -0
- package/src/playground/mergePlaygroundEntries.ts +28 -0
- package/src/playground/playgroundJoin.test.ts +79 -19
- package/src/playground/playgroundJoin.ts +47 -22
- package/src/playground/playgroundModuleExport.test.ts +42 -0
- package/src/playground/playgroundModuleExport.ts +22 -0
- package/src/playground/playgroundSpecsKey.ts +8 -0
- package/src/playground/propCoerce.ts +91 -0
- package/src/playground/scanVariantA11y.test.ts +46 -0
- package/src/playground/scanVariantA11y.ts +107 -0
- package/src/playground/snippet.ts +83 -0
- package/src/playground/usePlaygroundFromReport.test.ts +18 -8
- package/src/playground/usePlaygroundFromReport.ts +3 -1
- package/src/report/a11yForModule.ts +2 -7
- package/src/report/a11yScoring.test.ts +24 -0
- package/src/report/a11yScoring.ts +17 -0
- package/src/report/index.ts +6 -0
- package/src/shell/DashboardLayout.tsx +71 -45
- package/src/shell/DashboardLayoutAuto.tsx +0 -4
- package/src/shell/hashRoute.test.ts +7 -15
- package/src/shell/hashRoute.ts +31 -31
- package/src/shell/useHashRoute.ts +38 -13
- package/src/styles/dashboard-theme.css +18 -7
- package/src/types/controls.ts +11 -0
- package/src/types/playground.ts +4 -0
- package/src/types/report.ts +32 -9
- package/templates/playground/buildRegistry.ts +1 -1
- package/templates/vite.dslinter.snippet.ts +15 -4
- package/vite/collectScanModules.test.ts +51 -3
- package/vite/collectScanModules.ts +85 -29
- package/vite/consumer.config.mjs +6 -3
- package/vite/consumerAlias.test.ts +47 -0
- package/vite/consumerAlias.ts +114 -0
- package/vite/embedTailwindSources.test.ts +74 -0
- package/vite/embedTailwindSources.ts +97 -0
- package/vite/loadConsumerAliases.test.ts +131 -0
- package/vite/loadConsumerAliases.ts +155 -0
- package/vite/openFileInEditor.mjs +196 -0
- package/vite/openFileInEditor.test.mjs +87 -0
- package/vite/plugin.resolve.test.ts +72 -0
- package/vite/plugin.ts +216 -19
- package/vite/reportPath.test.ts +19 -0
- package/vite/resolveWayfinderImport.ts +56 -0
- package/vite/shims/inertia-react.tsx +85 -0
- package/vite/shims/wayfinder-actions.ts +33 -0
- package/vite/shims/wayfinder-routes.ts +30 -0
- package/vite/shims/ziggy-js.ts +12 -0
- package/dashboard-dist/assets/DashboardLayoutAuto-BPPtPsYh.css +0 -1
- package/dashboard-dist/assets/DashboardLayoutAuto-Dp3bAQxH.js +0 -1
- package/dashboard-dist/assets/index-DsjwnDdX.js +0 -206
- package/dashboard-dist/assets/index-jaCmZJlW.css +0 -1
- package/src/components/playgroundUsageTwoslash.ts +0 -69
- package/templates/vite.dslint-scan-alias.snippet.ts +0 -4
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { createElement } from "react";
|
|
2
|
+
import type { DefinedPlayground } from "./definePlayground";
|
|
3
|
+
import type { PlaygroundEntry } from "../types/playground";
|
|
4
|
+
import type { PlaygroundPreviewComponent } from "../types/preview";
|
|
5
|
+
import type { PlaygroundArgs } from "../types/controls";
|
|
6
|
+
import type { BuildPlaygroundModules } from "./buildPlaygroundEntriesFromReport";
|
|
7
|
+
import { catalogIdFromPlaygroundExport } from "./catalogIdFromPlaygroundExport";
|
|
8
|
+
|
|
9
|
+
function isDefinedPlayground(value: unknown): value is DefinedPlayground {
|
|
10
|
+
if (!value || typeof value !== "object") return false;
|
|
11
|
+
const o = value as Record<string, unknown>;
|
|
12
|
+
if (typeof o.playgroundMeta !== "object" || o.playgroundMeta === null) return false;
|
|
13
|
+
const meta = o.playgroundMeta as { id?: unknown; title?: unknown; group?: unknown };
|
|
14
|
+
return (
|
|
15
|
+
typeof meta.id === "string" &&
|
|
16
|
+
typeof meta.title === "string" &&
|
|
17
|
+
(meta.group === undefined || typeof meta.group === "string") &&
|
|
18
|
+
Array.isArray(o.playgroundControls) &&
|
|
19
|
+
typeof o.PlaygroundPreview === "function"
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function resolveDefinedMeta(
|
|
24
|
+
defined: DefinedPlayground,
|
|
25
|
+
exportName: string | undefined,
|
|
26
|
+
): DefinedPlayground["playgroundMeta"] | undefined {
|
|
27
|
+
const meta = defined.playgroundMeta;
|
|
28
|
+
if (meta.id) return meta;
|
|
29
|
+
if (!exportName) return undefined;
|
|
30
|
+
const id = catalogIdFromPlaygroundExport(exportName);
|
|
31
|
+
if (!id) return undefined;
|
|
32
|
+
return { ...meta, id, title: meta.title || id };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function definedPlaygroundToEntry(defined: DefinedPlayground, modulePath: string): PlaygroundEntry {
|
|
36
|
+
const { playgroundMeta, playgroundControls, PlaygroundPreview } = defined;
|
|
37
|
+
const Preview = PlaygroundPreview as PlaygroundPreviewComponent;
|
|
38
|
+
const renderPreview = (values: PlaygroundArgs) => createElement(Preview, { values });
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
id: playgroundMeta.id,
|
|
42
|
+
meta: playgroundMeta,
|
|
43
|
+
modulePath,
|
|
44
|
+
controls: playgroundControls,
|
|
45
|
+
renderPreview,
|
|
46
|
+
Preview,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Collect manual playground entries from `definePlayground()` exports in eager modules. */
|
|
51
|
+
export function collectDefinedPlaygrounds(modules: BuildPlaygroundModules): PlaygroundEntry[] {
|
|
52
|
+
const out: PlaygroundEntry[] = [];
|
|
53
|
+
for (const [modulePath, mod] of Object.entries(modules)) {
|
|
54
|
+
if (!mod || typeof mod !== "object") continue;
|
|
55
|
+
for (const [exportName, value] of Object.entries(mod)) {
|
|
56
|
+
if (!isDefinedPlayground(value)) continue;
|
|
57
|
+
const meta = resolveDefinedMeta(value, exportName);
|
|
58
|
+
if (!meta) continue;
|
|
59
|
+
out.push(
|
|
60
|
+
definedPlaygroundToEntry(
|
|
61
|
+
meta === value.playgroundMeta ? value : { ...value, playgroundMeta: meta },
|
|
62
|
+
modulePath,
|
|
63
|
+
),
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { PlaygroundControl } from "../types/controls";
|
|
2
|
+
import type { DeclaredPropKind, UsageSummary } from "../types/report";
|
|
3
|
+
|
|
4
|
+
export const CHILDREN_SLOT_DEFAULT = "Example";
|
|
5
|
+
|
|
6
|
+
/** Never surfaced as playground controls or preview props. */
|
|
7
|
+
export const SKIP_PLAYGROUND_PROPS = new Set(["key", "ref", "props"]);
|
|
8
|
+
|
|
9
|
+
/** Styling props kept in the panel but without generated placeholder defaults. */
|
|
10
|
+
export const PASSTHROUGH_STRING_PROPS = new Set(["className", "style"]);
|
|
11
|
+
|
|
12
|
+
export function isPassthroughStringProp(key: string): boolean {
|
|
13
|
+
return PASSTHROUGH_STRING_PROPS.has(key);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function stringDefaultForProp(key: string): string {
|
|
17
|
+
if (isPassthroughStringProp(key)) return "";
|
|
18
|
+
return defaultStringForProp(key);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type PlaygroundStringControl = Extract<PlaygroundControl, { type: "string" }>;
|
|
22
|
+
|
|
23
|
+
/** Parts like BreadcrumbSeparator default to an icon when children is omitted. */
|
|
24
|
+
export function usesIconChildrenFallback(exportName: string): boolean {
|
|
25
|
+
return exportName.endsWith("Separator");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function childrenControl(exportName?: string): PlaygroundStringControl {
|
|
29
|
+
if (exportName && usesIconChildrenFallback(exportName)) {
|
|
30
|
+
return {
|
|
31
|
+
key: "children",
|
|
32
|
+
label: "children",
|
|
33
|
+
type: "string",
|
|
34
|
+
default: "",
|
|
35
|
+
placeholder: "Custom separator (chevron when empty)",
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
key: "children",
|
|
40
|
+
label: "children",
|
|
41
|
+
type: "string",
|
|
42
|
+
default: CHILDREN_SLOT_DEFAULT,
|
|
43
|
+
placeholder: "Slot content",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function childrenPropForPreview(
|
|
48
|
+
exportName: string | undefined,
|
|
49
|
+
raw: unknown,
|
|
50
|
+
): unknown | undefined {
|
|
51
|
+
if (exportName && usesIconChildrenFallback(exportName)) {
|
|
52
|
+
if (raw === undefined || raw === null || String(raw).length === 0) return undefined;
|
|
53
|
+
return String(raw);
|
|
54
|
+
}
|
|
55
|
+
if (raw === undefined || raw === null) return CHILDREN_SLOT_DEFAULT;
|
|
56
|
+
return String(raw);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function componentAcceptsChildren(
|
|
60
|
+
declaredProps: string[],
|
|
61
|
+
usage?: UsageSummary,
|
|
62
|
+
): boolean {
|
|
63
|
+
if (declaredProps.includes("children")) return true;
|
|
64
|
+
if (declaredProps.includes("asChild")) return true;
|
|
65
|
+
if ((usage?.prop_frequencies?.children ?? 0) > 0) return true;
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function ensureChildrenControl(
|
|
70
|
+
controls: PlaygroundControl[],
|
|
71
|
+
acceptsChildren: boolean,
|
|
72
|
+
exportName?: string,
|
|
73
|
+
): PlaygroundControl[] {
|
|
74
|
+
if (!acceptsChildren || controls.some((c) => c.key === "children")) {
|
|
75
|
+
return controls;
|
|
76
|
+
}
|
|
77
|
+
return [...controls, childrenControl(exportName)];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function isLikelyBooleanProp(name: string): boolean {
|
|
81
|
+
const n = name.toLowerCase();
|
|
82
|
+
if (n === "disabled" || n === "loading" || n === "aschild") return true;
|
|
83
|
+
if (n.startsWith("is") || n.startsWith("has")) return true;
|
|
84
|
+
if (n.startsWith("show") || n.startsWith("hide")) return true;
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function defaultStringForProp(key: string): string {
|
|
89
|
+
if (key === "href") return "/governance";
|
|
90
|
+
const k = key.toLowerCase();
|
|
91
|
+
if (
|
|
92
|
+
k === "title" ||
|
|
93
|
+
k === "label" ||
|
|
94
|
+
k === "text" ||
|
|
95
|
+
k === "name" ||
|
|
96
|
+
k === "heading"
|
|
97
|
+
) {
|
|
98
|
+
return "Label";
|
|
99
|
+
}
|
|
100
|
+
return key;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function controlsFromDeclaredProps(
|
|
104
|
+
declaredProps: string[],
|
|
105
|
+
propKinds?: Partial<Record<string, DeclaredPropKind>>,
|
|
106
|
+
propOptions?: Record<string, string[]>,
|
|
107
|
+
propDefaults?: Record<string, string>,
|
|
108
|
+
exportName?: string,
|
|
109
|
+
): PlaygroundControl[] {
|
|
110
|
+
const out: PlaygroundControl[] = [];
|
|
111
|
+
for (const key of declaredProps) {
|
|
112
|
+
if (SKIP_PLAYGROUND_PROPS.has(key)) continue;
|
|
113
|
+
if (key === "children") {
|
|
114
|
+
out.push(childrenControl(exportName));
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const options = propOptions?.[key];
|
|
118
|
+
if (options && options.length >= 2) {
|
|
119
|
+
const defaultVal =
|
|
120
|
+
propDefaults?.[key] ??
|
|
121
|
+
(options.includes("default") ? "default" : options[0]!);
|
|
122
|
+
out.push({
|
|
123
|
+
key,
|
|
124
|
+
label: key,
|
|
125
|
+
type: "select",
|
|
126
|
+
default: defaultVal,
|
|
127
|
+
options: options.map((value) => ({ value, label: value })),
|
|
128
|
+
});
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const kind = propKinds?.[key];
|
|
132
|
+
if (kind === "boolean") {
|
|
133
|
+
out.push({ key, label: key, type: "boolean", default: false });
|
|
134
|
+
} else if (kind === "number") {
|
|
135
|
+
out.push({ key, label: key, type: "number", default: 0 });
|
|
136
|
+
} else if (kind === "string") {
|
|
137
|
+
out.push({
|
|
138
|
+
key,
|
|
139
|
+
label: key,
|
|
140
|
+
type: "string",
|
|
141
|
+
default: stringDefaultForProp(key),
|
|
142
|
+
placeholder: isPassthroughStringProp(key) ? undefined : key,
|
|
143
|
+
});
|
|
144
|
+
} else if (isLikelyBooleanProp(key)) {
|
|
145
|
+
out.push({ key, label: key, type: "boolean", default: false });
|
|
146
|
+
} else {
|
|
147
|
+
out.push({
|
|
148
|
+
key,
|
|
149
|
+
label: key,
|
|
150
|
+
type: "string",
|
|
151
|
+
default: stringDefaultForProp(key),
|
|
152
|
+
placeholder: isPassthroughStringProp(key) ? undefined : key,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function controlsForSpec(
|
|
160
|
+
catalogId: string,
|
|
161
|
+
declaredProps: string[],
|
|
162
|
+
propKinds: Partial<Record<string, DeclaredPropKind>> | undefined,
|
|
163
|
+
propOptions: Record<string, string[]> | undefined,
|
|
164
|
+
propDefaults: Record<string, string> | undefined,
|
|
165
|
+
controlOverrides: Record<string, PlaygroundControl[]>,
|
|
166
|
+
exportName?: string,
|
|
167
|
+
): PlaygroundControl[] {
|
|
168
|
+
const override = controlOverrides[catalogId];
|
|
169
|
+
if (override) return override;
|
|
170
|
+
return controlsFromDeclaredProps(
|
|
171
|
+
declaredProps,
|
|
172
|
+
propKinds,
|
|
173
|
+
propOptions,
|
|
174
|
+
propDefaults,
|
|
175
|
+
exportName,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
@@ -51,7 +51,7 @@ export function createPlaygroundRegistry(
|
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
/** Convenience when you only need entries
|
|
54
|
+
/** Convenience when you only need entries. */
|
|
55
55
|
export function createPlaygroundRegistryEntriesOnly(
|
|
56
56
|
modules: BuildPlaygroundModules,
|
|
57
57
|
options: CreatePlaygroundRegistryOptions = {},
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { ComponentType, ReactNode } from "react";
|
|
2
2
|
import type { PlaygroundArgs, PlaygroundControl } from "../types/controls";
|
|
3
|
+
import { buildKitControls, type PlaygroundKitHints } from "./enrichKitControls";
|
|
4
|
+
import {
|
|
5
|
+
expandPlaygroundControls,
|
|
6
|
+
propsFromControls,
|
|
7
|
+
type PlaygroundControlsInput,
|
|
8
|
+
} from "./expandPlaygroundControls";
|
|
3
9
|
import type { PlaygroundMeta } from "../types/playground";
|
|
4
10
|
import type { PlaygroundPreviewComponent, PlaygroundPreviewProps } from "../types/preview";
|
|
5
11
|
|
|
@@ -7,12 +13,14 @@ export type DefinedPlayground = {
|
|
|
7
13
|
playgroundMeta: PlaygroundMeta;
|
|
8
14
|
playgroundControls: PlaygroundControl[];
|
|
9
15
|
PlaygroundPreview: PlaygroundPreviewComponent;
|
|
16
|
+
playgroundKitHints?: PlaygroundKitHints;
|
|
10
17
|
};
|
|
11
18
|
|
|
12
19
|
type PlaygroundDefinitionBase = {
|
|
13
20
|
/**
|
|
14
21
|
* Stable sidebar / URL id. Defaults to the component’s `displayName` or function `name`
|
|
15
|
-
* (dev builds).
|
|
22
|
+
* (dev builds). For `render`-only definitions, omit when the module export is
|
|
23
|
+
* `{name}Playground` (resolved at collect time).
|
|
16
24
|
*/
|
|
17
25
|
id?: string;
|
|
18
26
|
title?: string;
|
|
@@ -20,7 +28,11 @@ type PlaygroundDefinitionBase = {
|
|
|
20
28
|
group?: string;
|
|
21
29
|
/** @deprecated Use `group` — kept for older call sites. */
|
|
22
30
|
section?: string;
|
|
23
|
-
controls?:
|
|
31
|
+
controls?: PlaygroundControlsInput;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type PlaygroundMetaOptions = Omit<PlaygroundDefinitionBase, "controls"> & {
|
|
35
|
+
controls?: PlaygroundControlsInput;
|
|
24
36
|
};
|
|
25
37
|
|
|
26
38
|
type DefineWithComponent<P> = PlaygroundDefinitionBase & {
|
|
@@ -28,8 +40,15 @@ type DefineWithComponent<P> = PlaygroundDefinitionBase & {
|
|
|
28
40
|
props: (values: PlaygroundArgs) => P;
|
|
29
41
|
};
|
|
30
42
|
|
|
43
|
+
type DefineWithKit<T extends PlaygroundArgs = PlaygroundArgs> = PlaygroundDefinitionBase & {
|
|
44
|
+
/** Preview renderer receiving mapped control values. */
|
|
45
|
+
kit: (args: T) => ReactNode;
|
|
46
|
+
/** Optional overrides merged after inferred / declared control defaults. */
|
|
47
|
+
defaults?: Partial<T>;
|
|
48
|
+
};
|
|
49
|
+
|
|
31
50
|
type DefineWithRender = PlaygroundDefinitionBase & {
|
|
32
|
-
/**
|
|
51
|
+
/** Escape hatch when `kit` control mapping is not enough (raw panel values, legacy call sites). */
|
|
33
52
|
render: (values: PlaygroundArgs) => ReactNode;
|
|
34
53
|
};
|
|
35
54
|
|
|
@@ -50,8 +69,8 @@ function resolveMeta(
|
|
|
50
69
|
base: PlaygroundDefinitionBase,
|
|
51
70
|
nameFallback: string | undefined,
|
|
52
71
|
): PlaygroundMeta {
|
|
53
|
-
const id = base.id ?? inferId(nameFallback);
|
|
54
|
-
const title = base.title ?? id;
|
|
72
|
+
const id = base.id ?? (nameFallback ? inferId(nameFallback) : "");
|
|
73
|
+
const title = base.title ?? (id || "");
|
|
55
74
|
const group = base.group ?? base.section;
|
|
56
75
|
return {
|
|
57
76
|
id,
|
|
@@ -60,27 +79,69 @@ function resolveMeta(
|
|
|
60
79
|
};
|
|
61
80
|
}
|
|
62
81
|
|
|
82
|
+
function resolveKitControls(
|
|
83
|
+
kit: (args: PlaygroundArgs) => ReactNode,
|
|
84
|
+
opts: PlaygroundDefinitionBase & { defaults?: Partial<PlaygroundArgs> },
|
|
85
|
+
catalogId: string,
|
|
86
|
+
): { controls: PlaygroundControl[]; hints: PlaygroundKitHints } {
|
|
87
|
+
return buildKitControls(kit, {
|
|
88
|
+
controls: opts.controls,
|
|
89
|
+
defaults: opts.defaults as Record<string, string | number | boolean> | undefined,
|
|
90
|
+
catalogId: catalogId || undefined,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function buildKitPlayground<T extends PlaygroundArgs>(
|
|
95
|
+
kit: (args: T) => ReactNode,
|
|
96
|
+
opts: PlaygroundDefinitionBase & { defaults?: Partial<T> },
|
|
97
|
+
): DefinedPlayground {
|
|
98
|
+
const meta = resolveMeta(opts, undefined);
|
|
99
|
+
const { controls, hints } = resolveKitControls(kit, opts, meta.id);
|
|
100
|
+
function PlaygroundPreview({ values }: PlaygroundPreviewProps) {
|
|
101
|
+
return <>{kit(propsFromControls(controls, values, opts.defaults))}</>;
|
|
102
|
+
}
|
|
103
|
+
PlaygroundPreview.displayName = `${meta.id}PlaygroundPreview`;
|
|
104
|
+
return {
|
|
105
|
+
playgroundMeta: meta,
|
|
106
|
+
playgroundControls: controls,
|
|
107
|
+
PlaygroundPreview,
|
|
108
|
+
playgroundKitHints: hints,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isComponentPropsOptions(value: unknown): value is DefineWithComponent<Record<string, unknown>> {
|
|
113
|
+
return typeof value === "object" && value !== null && "props" in value;
|
|
114
|
+
}
|
|
115
|
+
|
|
63
116
|
/**
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* dashboard package imports.
|
|
117
|
+
* Declare dashboard playground exports manually. Pass a kit callback directly — control
|
|
118
|
+
* keys are inferred from destructured parameters (inline defaults become panel defaults).
|
|
67
119
|
*/
|
|
68
120
|
export function definePlayground<P extends Record<string, unknown>>(
|
|
69
121
|
component: ComponentType<P>,
|
|
70
122
|
options: DefineWithComponent<P>,
|
|
71
123
|
): DefinedPlayground;
|
|
72
124
|
|
|
125
|
+
export function definePlayground<T extends PlaygroundArgs>(
|
|
126
|
+
kit: (args: T) => ReactNode,
|
|
127
|
+
meta?: PlaygroundMetaOptions & { defaults?: Partial<T> },
|
|
128
|
+
): DefinedPlayground;
|
|
129
|
+
|
|
130
|
+
export function definePlayground<T extends PlaygroundArgs>(
|
|
131
|
+
options: DefineWithKit<T>,
|
|
132
|
+
): DefinedPlayground;
|
|
133
|
+
|
|
73
134
|
export function definePlayground(options: DefineWithRender): DefinedPlayground;
|
|
74
135
|
|
|
75
|
-
export function definePlayground<P extends Record<string, unknown
|
|
76
|
-
|
|
77
|
-
options?: DefineWithComponent<P
|
|
136
|
+
export function definePlayground<P extends Record<string, unknown>, T extends PlaygroundArgs>(
|
|
137
|
+
componentOrKitOrOptions: ComponentType<P> | ((args: T) => ReactNode) | DefineWithKit<T> | DefineWithRender,
|
|
138
|
+
options?: DefineWithComponent<P> | (PlaygroundMetaOptions & { defaults?: Partial<T> }),
|
|
78
139
|
): DefinedPlayground {
|
|
79
|
-
if (
|
|
80
|
-
const Component =
|
|
140
|
+
if (typeof componentOrKitOrOptions === "function" && isComponentPropsOptions(options)) {
|
|
141
|
+
const Component = componentOrKitOrOptions as ComponentType<P>;
|
|
81
142
|
const opts = options;
|
|
82
143
|
const meta = resolveMeta(opts, getComponentLabel(Component));
|
|
83
|
-
const controls = opts.controls
|
|
144
|
+
const controls = expandPlaygroundControls(opts.controls);
|
|
84
145
|
function PlaygroundPreview({ values }: PlaygroundPreviewProps) {
|
|
85
146
|
return <Component {...opts.props(values)} />;
|
|
86
147
|
}
|
|
@@ -88,12 +149,23 @@ export function definePlayground<P extends Record<string, unknown>>(
|
|
|
88
149
|
return { playgroundMeta: meta, playgroundControls: controls, PlaygroundPreview };
|
|
89
150
|
}
|
|
90
151
|
|
|
91
|
-
|
|
152
|
+
if (typeof componentOrKitOrOptions === "function") {
|
|
153
|
+
const kit = componentOrKitOrOptions as (args: T) => ReactNode;
|
|
154
|
+
return buildKitPlayground(kit, options ?? {});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const opts = componentOrKitOrOptions as DefineWithKit<T> | DefineWithRender;
|
|
158
|
+
if ("kit" in opts) {
|
|
159
|
+
return buildKitPlayground(opts.kit, opts);
|
|
160
|
+
}
|
|
161
|
+
|
|
92
162
|
const meta = resolveMeta(opts, undefined);
|
|
93
|
-
const controls = opts.controls
|
|
163
|
+
const controls = expandPlaygroundControls(opts.controls);
|
|
94
164
|
function PlaygroundPreview({ values }: PlaygroundPreviewProps) {
|
|
95
165
|
return <>{opts.render(values)}</>;
|
|
96
166
|
}
|
|
97
167
|
PlaygroundPreview.displayName = `${meta.id}PlaygroundPreview`;
|
|
98
168
|
return { playgroundMeta: meta, playgroundControls: controls, PlaygroundPreview };
|
|
99
169
|
}
|
|
170
|
+
|
|
171
|
+
export type { DefineWithKit as DefinePlaygroundKitOptions, PlaygroundKitHints };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PlaygroundArgs } from "../types/controls";
|
|
2
|
+
import {
|
|
3
|
+
definePlayground,
|
|
4
|
+
type DefinedPlayground,
|
|
5
|
+
type DefinePlaygroundKitOptions,
|
|
6
|
+
} from "./definePlayground";
|
|
7
|
+
|
|
8
|
+
/** @deprecated Use `definePlayground({ kit, controls })` instead. */
|
|
9
|
+
export type DefinePlaygroundFromKitOptions<T extends PlaygroundArgs> =
|
|
10
|
+
DefinePlaygroundKitOptions<T>;
|
|
11
|
+
|
|
12
|
+
/** @deprecated Use `definePlayground({ kit, controls })` instead. */
|
|
13
|
+
export function definePlaygroundFromKit<T extends PlaygroundArgs>(
|
|
14
|
+
options: DefinePlaygroundFromKitOptions<T>,
|
|
15
|
+
): DefinedPlayground {
|
|
16
|
+
return definePlayground(options);
|
|
17
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Virtual module map key for a scanner `rel_path` (embed convention). */
|
|
2
|
+
export function embedGlobKeyFromRelPath(relPath: string): string {
|
|
3
|
+
const trimmed = relPath.replace(/^\/+/, "");
|
|
4
|
+
return `@dslinter-scan/${trimmed}`;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/** Alias for {@link embedGlobKeyFromRelPath} used by embed playground join helpers. */
|
|
8
|
+
export const defaultEmbedGlobKeyFromRelPath = embedGlobKeyFromRelPath;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { enrichControlsFromKitJsx } from "./enrichKitControls";
|
|
3
|
+
import { expandPlaygroundControls } from "./expandPlaygroundControls";
|
|
4
|
+
|
|
5
|
+
describe("enrichControlsFromKitJsx", () => {
|
|
6
|
+
it("labels slot params from compound subcomponents", () => {
|
|
7
|
+
const controls = expandPlaygroundControls({ title: "title", description: "description" });
|
|
8
|
+
const enriched = enrichControlsFromKitJsx(controls, [
|
|
9
|
+
{ component: "AlertTitle", param: "title" },
|
|
10
|
+
{ component: "AlertDescription", param: "description" },
|
|
11
|
+
]);
|
|
12
|
+
expect(enriched[0]).toMatchObject({
|
|
13
|
+
key: "title",
|
|
14
|
+
label: "Title",
|
|
15
|
+
default: "Heads up",
|
|
16
|
+
hint: "AlertTitle children",
|
|
17
|
+
});
|
|
18
|
+
expect(enriched[1]).toMatchObject({
|
|
19
|
+
key: "description",
|
|
20
|
+
label: "Description",
|
|
21
|
+
default: "This is a short description.",
|
|
22
|
+
hint: "AlertDescription children",
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { PlaygroundControl } from "../types/controls";
|
|
2
|
+
import type { ComponentDefinition, PlaygroundSpec, WorkspaceReport } from "../types/report";
|
|
3
|
+
import { controlsForSpec } from "./controls";
|
|
4
|
+
import {
|
|
5
|
+
inferKitRootPropBindings,
|
|
6
|
+
inferKitJsxSlots,
|
|
7
|
+
slotDefaultFromComponent,
|
|
8
|
+
slotLabelFromComponent,
|
|
9
|
+
type KitJsxSlot,
|
|
10
|
+
type KitRootPropBinding,
|
|
11
|
+
} from "./inferKitJsx";
|
|
12
|
+
import type { InferredKitParam } from "./inferKitParams";
|
|
13
|
+
import { inferKitParams } from "./inferKitParams";
|
|
14
|
+
import { expandPlaygroundControls } from "./expandPlaygroundControls";
|
|
15
|
+
|
|
16
|
+
export type PlaygroundKitHints = {
|
|
17
|
+
rootComponent?: string;
|
|
18
|
+
slots: KitJsxSlot[];
|
|
19
|
+
rootPropBindings: KitRootPropBinding[];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function slotForParam(slots: KitJsxSlot[], key: string): KitJsxSlot | undefined {
|
|
23
|
+
return slots.find((slot) => slot.param === key);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function enrichOneControl(control: PlaygroundControl, slot: KitJsxSlot | undefined): PlaygroundControl {
|
|
27
|
+
if (!slot) return control;
|
|
28
|
+
const label = slotLabelFromComponent(slot.component);
|
|
29
|
+
const exampleDefault = slotDefaultFromComponent(slot.component);
|
|
30
|
+
const hint = `${slot.component} children`;
|
|
31
|
+
|
|
32
|
+
if (control.type === "string") {
|
|
33
|
+
const useExample =
|
|
34
|
+
exampleDefault !== undefined &&
|
|
35
|
+
(control.default === control.key || control.default === control.key.toLowerCase());
|
|
36
|
+
return {
|
|
37
|
+
...control,
|
|
38
|
+
label,
|
|
39
|
+
hint,
|
|
40
|
+
...(useExample
|
|
41
|
+
? { default: exampleDefault, defaultSource: "example" as const, placeholder: exampleDefault }
|
|
42
|
+
: {}),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return { ...control, label, hint };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Apply compound slot metadata (labels, example defaults) to inferred controls. */
|
|
49
|
+
export function enrichControlsFromKitJsx(
|
|
50
|
+
controls: PlaygroundControl[],
|
|
51
|
+
slots: KitJsxSlot[],
|
|
52
|
+
): PlaygroundControl[] {
|
|
53
|
+
return controls.map((control) => enrichOneControl(control, slotForParam(slots, control.key)));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function mergeInferredParams(
|
|
57
|
+
params: InferredKitParam[],
|
|
58
|
+
rootPropBindings: KitRootPropBinding[],
|
|
59
|
+
): InferredKitParam[] {
|
|
60
|
+
const byKey = new Map(params.map((param) => [param.key, param]));
|
|
61
|
+
|
|
62
|
+
for (const binding of rootPropBindings) {
|
|
63
|
+
if (!byKey.has(binding.param)) {
|
|
64
|
+
byKey.set(binding.param, { key: binding.param });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return [...byKey.values()];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function specForCatalog(
|
|
72
|
+
report: { playgrounds?: PlaygroundSpec[] } | null | undefined,
|
|
73
|
+
catalogId: string,
|
|
74
|
+
): PlaygroundSpec | undefined {
|
|
75
|
+
return report?.playgrounds?.find(
|
|
76
|
+
(spec) => spec.export_name === catalogId || spec.id === catalogId,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function definitionForExport(
|
|
81
|
+
report: WorkspaceReport | null | undefined,
|
|
82
|
+
exportName: string,
|
|
83
|
+
): ComponentDefinition | undefined {
|
|
84
|
+
for (const file of report?.files ?? []) {
|
|
85
|
+
const def = file.definitions?.find((d) => d.name === exportName);
|
|
86
|
+
if (def) return def;
|
|
87
|
+
}
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function propMetadataForCatalog(
|
|
92
|
+
report: WorkspaceReport | null | undefined,
|
|
93
|
+
catalogId: string,
|
|
94
|
+
): {
|
|
95
|
+
spec: PlaygroundSpec;
|
|
96
|
+
propOptions: Record<string, string[]>;
|
|
97
|
+
propDefaults: Record<string, string>;
|
|
98
|
+
propKinds: PlaygroundSpec["declared_prop_kinds"];
|
|
99
|
+
} | undefined {
|
|
100
|
+
const spec = specForCatalog(report, catalogId);
|
|
101
|
+
if (!spec || !report) return undefined;
|
|
102
|
+
|
|
103
|
+
const def = definitionForExport(report, spec.export_name);
|
|
104
|
+
const propOptions = {
|
|
105
|
+
...(def?.declared_prop_options ?? {}),
|
|
106
|
+
...(spec.declared_prop_options ?? {}),
|
|
107
|
+
};
|
|
108
|
+
const propDefaults = {
|
|
109
|
+
...(def?.declared_prop_defaults ?? {}),
|
|
110
|
+
...(spec.declared_prop_defaults ?? {}),
|
|
111
|
+
};
|
|
112
|
+
const propKinds = { ...spec.declared_prop_kinds };
|
|
113
|
+
|
|
114
|
+
return { spec, propOptions, propDefaults, propKinds };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Merge typed root props (e.g. CVA `variant`) from the report when the kit binds them. */
|
|
118
|
+
export function mergeReportControlsForKit(
|
|
119
|
+
controls: PlaygroundControl[],
|
|
120
|
+
rootPropBindings: KitRootPropBinding[],
|
|
121
|
+
report: WorkspaceReport | null | undefined,
|
|
122
|
+
catalogId: string,
|
|
123
|
+
): PlaygroundControl[] {
|
|
124
|
+
const metadata = propMetadataForCatalog(report, catalogId);
|
|
125
|
+
if (!metadata) return controls;
|
|
126
|
+
|
|
127
|
+
const { spec, propOptions, propDefaults, propKinds } = metadata;
|
|
128
|
+
const rootBindings = rootPropBindings.filter(
|
|
129
|
+
(binding) => binding.component === spec.export_name || binding.component === catalogId,
|
|
130
|
+
);
|
|
131
|
+
if (rootBindings.length === 0) return controls;
|
|
132
|
+
|
|
133
|
+
const boundProps = new Set(rootBindings.map((binding) => binding.prop));
|
|
134
|
+
const reportControls = controlsForSpec(
|
|
135
|
+
catalogId,
|
|
136
|
+
(spec.declared_props ?? []).filter((prop) => boundProps.has(prop)),
|
|
137
|
+
propKinds,
|
|
138
|
+
Object.keys(propOptions).length ? propOptions : undefined,
|
|
139
|
+
Object.keys(propDefaults).length ? propDefaults : undefined,
|
|
140
|
+
{},
|
|
141
|
+
spec.export_name,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const byKey = new Map(controls.map((control) => [control.key, control]));
|
|
145
|
+
for (const control of reportControls) {
|
|
146
|
+
byKey.set(control.key, control);
|
|
147
|
+
}
|
|
148
|
+
return [...byKey.values()];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function buildKitControls(
|
|
152
|
+
kit: (args: Record<string, unknown>) => unknown,
|
|
153
|
+
options: {
|
|
154
|
+
controls?: Parameters<typeof expandPlaygroundControls>[0];
|
|
155
|
+
defaults?: Record<string, string | number | boolean>;
|
|
156
|
+
report?: { playgrounds?: PlaygroundSpec[] } | null;
|
|
157
|
+
catalogId?: string;
|
|
158
|
+
},
|
|
159
|
+
): { controls: PlaygroundControl[]; hints: PlaygroundKitHints } {
|
|
160
|
+
const slots = inferKitJsxSlots(kit);
|
|
161
|
+
const rootPropBindings = inferKitRootPropBindings(kit);
|
|
162
|
+
const params = mergeInferredParams(inferKitParams(kit), rootPropBindings);
|
|
163
|
+
|
|
164
|
+
let controls: PlaygroundControl[];
|
|
165
|
+
if (options.controls !== undefined) {
|
|
166
|
+
controls = expandPlaygroundControls(options.controls);
|
|
167
|
+
} else if (options.defaults !== undefined && Object.keys(options.defaults).length > 0) {
|
|
168
|
+
controls = expandPlaygroundControls(options.defaults);
|
|
169
|
+
} else if (params.length > 0) {
|
|
170
|
+
controls = expandPlaygroundControls(
|
|
171
|
+
Object.fromEntries(params.map((param) => [param.key, param.defaultValue ?? param.key])),
|
|
172
|
+
);
|
|
173
|
+
} else {
|
|
174
|
+
controls = [];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
controls = enrichControlsFromKitJsx(controls, slots);
|
|
178
|
+
|
|
179
|
+
if (options.catalogId && options.report) {
|
|
180
|
+
controls = mergeReportControlsForKit(
|
|
181
|
+
controls,
|
|
182
|
+
rootPropBindings,
|
|
183
|
+
options.report,
|
|
184
|
+
options.catalogId,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const rootBinding = rootPropBindings[0];
|
|
189
|
+
return {
|
|
190
|
+
controls,
|
|
191
|
+
hints: {
|
|
192
|
+
rootComponent: rootBinding?.component ?? options.catalogId,
|
|
193
|
+
slots,
|
|
194
|
+
rootPropBindings,
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|