@yahoo/uds-v5-wip 1.61.0 → 1.62.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/dist/config/dist/AssetGroup.d.ts +75 -0
- package/dist/config/dist/AssetGroup.js +114 -0
- package/dist/config/dist/Component.d.ts +309 -0
- package/dist/config/dist/Component.js +897 -0
- package/dist/config/dist/ComponentGroup.d.ts +21 -0
- package/dist/config/dist/ComponentGroup.js +46 -0
- package/dist/config/dist/CompositeStyle.d.ts +28 -0
- package/dist/config/dist/CompositeStyle.js +52 -0
- package/dist/config/dist/Config.d.ts +422 -0
- package/dist/config/dist/Config.js +1426 -0
- package/dist/config/dist/Mode.d.ts +42 -0
- package/dist/config/dist/Mode.js +81 -0
- package/dist/config/dist/Modifier.d.ts +52 -0
- package/dist/config/dist/Modifier.js +97 -0
- package/dist/config/dist/MotionDef.d.ts +50 -0
- package/dist/config/dist/MotionDef.js +97 -0
- package/dist/config/dist/Props.d.ts +235 -126
- package/dist/config/dist/Provider.d.ts +21 -0
- package/dist/config/dist/Provider.js +14 -0
- package/dist/config/dist/StyleProp.d.ts +113 -0
- package/dist/config/dist/StyleProp.js +197 -0
- package/dist/config/dist/Token.d.ts +56 -0
- package/dist/config/dist/Token.js +112 -0
- package/dist/config/dist/TokenGroup.d.ts +33 -0
- package/dist/config/dist/TokenGroup.js +68 -0
- package/dist/config/dist/asset-kind.d.ts +56 -0
- package/dist/config/dist/asset-kind.js +29 -0
- package/dist/config/dist/brands.d.ts +31 -0
- package/dist/config/dist/brands.js +20 -0
- package/dist/config/dist/captureCallerPath.d.ts +49 -0
- package/dist/config/dist/captureCallerPath.js +95 -0
- package/dist/config/dist/colorExpressions.d.ts +132 -0
- package/dist/config/dist/colorExpressions.js +148 -0
- package/dist/config/dist/defineAssetGroup.d.ts +158 -0
- package/dist/config/dist/defineAssetGroup.js +263 -0
- package/dist/config/dist/defineProvider.d.ts +30 -0
- package/dist/config/dist/defineProvider.js +60 -0
- package/dist/config/dist/element-marker.d.ts +55 -0
- package/dist/config/dist/element-marker.js +113 -0
- package/dist/config/dist/entity-utils.d.ts +27 -0
- package/dist/config/dist/entity-utils.js +105 -0
- package/dist/config/dist/factories.d.ts +707 -0
- package/dist/config/dist/factories.js +393 -0
- package/dist/config/dist/foreign-component-name.js +42 -0
- package/dist/config/dist/index.d.ts +30 -21
- package/dist/config/dist/index.js +26 -14
- package/dist/config/dist/interpolate.d.ts +21 -0
- package/dist/config/dist/interpolate.js +10 -0
- package/dist/config/dist/markers.d.ts +218 -0
- package/dist/config/dist/markers.js +67 -0
- package/dist/config/dist/refs.d.ts +143 -69
- package/dist/config/dist/refs.js +95 -113
- package/dist/config/dist/token-override-rows.d.ts +67 -0
- package/dist/config/dist/token-override-rows.js +223 -0
- package/dist/config/dist/tokenValueType.js +138 -0
- package/dist/config/dist/types/css-properties.d.ts +132 -122
- package/dist/config/dist/types/css-property-keywords.d.ts +157 -0
- package/dist/config/dist/types/css-property-keywords.js +616 -0
- package/dist/config/dist/types/css-values.d.ts +18 -16
- package/dist/config/dist/types.d.ts +688 -66
- package/dist/config/dist/units.d.ts +15 -0
- package/dist/config/dist/units.js +16 -0
- package/dist/config.d.ts +31 -22
- package/dist/config.js +28 -16
- package/dist/loader/dist/loader/artifacts.js +26 -9
- package/dist/loader/dist/loader.d.ts +11 -23
- package/dist/loader/dist/loader.js +54 -99
- package/dist/loader/dist/next.js +3 -3
- package/dist/loader/dist/transform-runner.js +1 -8
- package/dist/loader/dist/unplugin.d.ts +2 -2
- package/dist/loader/dist/unplugin.js +2 -2
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/dist/index.js +1 -0
- package/dist/utils/dist/motion-utils/resolveMotionState.d.ts +35 -0
- package/dist/utils/dist/motion-utils/resolveMotionState.js +33 -0
- package/dist/utils/dist/string-utils/componentClassName.d.ts +41 -8
- package/dist/utils/dist/string-utils/componentClassName.js +48 -11
- package/dist/utils.d.ts +3 -2
- package/dist/utils.js +3 -2
- package/package.json +3 -3
- package/dist/codegen/dist/loader/getLoaderArtifacts.js +0 -2
- package/dist/config/dist/bindRender.d.ts +0 -16
- package/dist/config/dist/bindRender.js +0 -310
- package/dist/config/dist/className.d.ts +0 -54
- package/dist/config/dist/className.js +0 -77
- package/dist/config/dist/component-config.d.ts +0 -862
- package/dist/config/dist/component-config.js +0 -163
- package/dist/config/dist/component-refs.d.ts +0 -121
- package/dist/config/dist/createConfig.d.ts +0 -456
- package/dist/config/dist/createConfig.js +0 -503
- package/dist/config/dist/createStyler.d.ts +0 -35
- package/dist/config/dist/createStyler.js +0 -93
- package/dist/config/dist/defineComponent.d.ts +0 -317
- package/dist/config/dist/defineComponent.js +0 -264
- package/dist/config/dist/defineCompositeStyle.d.ts +0 -93
- package/dist/config/dist/defineCompositeStyle.js +0 -18
- package/dist/config/dist/defineMotionConfig.d.ts +0 -152
- package/dist/config/dist/definePreview.d.ts +0 -45
- package/dist/config/dist/definePreview.js +0 -35
- package/dist/config/dist/defineStyleProp.d.ts +0 -289
- package/dist/config/dist/defineStyleProp.js +0 -61
- package/dist/config/dist/merge-utils.js +0 -51
- package/dist/config/dist/propertyAcceptedTypes.d.ts +0 -19
- package/dist/config/dist/propertyAcceptedTypes.js +0 -220
- package/dist/config/dist/resolveCompoundProps.d.ts +0 -65
- package/dist/config/dist/resolveCompoundProps.js +0 -120
- package/dist/config/dist/resolveStyleProp.d.ts +0 -157
- package/dist/config/dist/resolveStyleProp.js +0 -260
- package/dist/config/dist/runtime-registry.js +0 -37
- package/dist/config/dist/serialize.d.ts +0 -47
- package/dist/config/dist/serialize.js +0 -111
- package/dist/config/dist/sniffTokenType.d.ts +0 -7
- package/dist/config/dist/sniffTokenType.js +0 -67
- package/dist/config/dist/style-prop-runtime.d.ts +0 -75
- package/dist/config/dist/style-prop-runtime.js +0 -390
- package/dist/loader/dist/loader/internal-meta.js +0 -59
- package/dist/loader/dist/loader/lower-new-api-primitive.js +0 -252
|
@@ -0,0 +1,897 @@
|
|
|
1
|
+
import { compact } from "./entity-utils.js";
|
|
2
|
+
import { isPlaceable } from "./asset-kind.js";
|
|
3
|
+
import { readSourcePath } from "./captureCallerPath.js";
|
|
4
|
+
import { isAssetGroupRef, isCompositeRef, isStylePropRef, isTokenRef, tag } from "./refs.js";
|
|
5
|
+
import { isColorExpression } from "./colorExpressions.js";
|
|
6
|
+
import { readAssetGroupSlug } from "./defineAssetGroup.js";
|
|
7
|
+
import { serializeElementsDeep } from "./element-marker.js";
|
|
8
|
+
import { isBoolMarker, isNumberMarker, isSlotMarker, isStringMarker, isVariantArrayMarker, isVariantMarker } from "./markers.js";
|
|
9
|
+
import { componentClassNames } from "../../utils/dist/string-utils/componentClassName.js";
|
|
10
|
+
import "../../utils/dist/index.js";
|
|
11
|
+
//#region ../config/dist/Component.js
|
|
12
|
+
/**
|
|
13
|
+
* `Component` — a registered component with layers, props, and base
|
|
14
|
+
* styles.
|
|
15
|
+
*
|
|
16
|
+
* Layer instances are purely structural (no derived). Cross-layer
|
|
17
|
+
* derivations live on `Component.derived` because they walk
|
|
18
|
+
* `compoundProps` + `base` together. `derived.propInfo(name)` returns a
|
|
19
|
+
* discriminated union narrowed by `.kind` — that surface replaces
|
|
20
|
+
* Studio's bespoke `componentInfo.ts` walker.
|
|
21
|
+
*
|
|
22
|
+
* `subcomponentOf` / `subcomponents` aren't authored on the component
|
|
23
|
+
* itself — they fall out of a parent's
|
|
24
|
+
* `defineComponent(...).subcomponents({...})` chain when `Config.
|
|
25
|
+
* registerComponents` walks it. They live under `.derived` to keep the
|
|
26
|
+
* authored-vs-derived split clean per the config naming convention.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Humanize a PascalCase/camelCase component name into a space-separated
|
|
30
|
+
* display label — `"GridItem"` → `"Grid Item"`, `"HStack"` → `"H Stack"`,
|
|
31
|
+
* `"TableHeadCell"` → `"Table Head Cell"`. Used as the default
|
|
32
|
+
* `metadata.label` when an author didn't supply one, so every consumer
|
|
33
|
+
* reads a label off `metadata` uniformly instead of re-deriving a
|
|
34
|
+
* fallback from `name` at each call site.
|
|
35
|
+
*/
|
|
36
|
+
function humanizeComponentName(name) {
|
|
37
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").trim();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Normalize each layer-input authoring form down to the canonical
|
|
41
|
+
* marker shape stored on `Layer.tag`:
|
|
42
|
+
*
|
|
43
|
+
* - bare string → `TagRef`
|
|
44
|
+
* - `LayerWithOverrides` (`{ tag, defaultProps?, base?, ... }`) →
|
|
45
|
+
* the unwrapped `tag` field (recursing once for the bare-string
|
|
46
|
+
* case nested inside).
|
|
47
|
+
* - Existing `TagRef` / `ComponentRef` / `ComponentDefinition` value
|
|
48
|
+
* is passed through unchanged.
|
|
49
|
+
*
|
|
50
|
+
* Centralized here so every Layer constructed downstream sees the
|
|
51
|
+
* marker shape, regardless of how the consumer authored the layer.
|
|
52
|
+
*/
|
|
53
|
+
function normalizeLayerInput(input) {
|
|
54
|
+
if (input === void 0) return void 0;
|
|
55
|
+
if (typeof input === "string") return tag(input);
|
|
56
|
+
if (typeof input === "object" && input !== null && "tag" in input && typeof input.tag !== "undefined" && !("__kind" in input)) {
|
|
57
|
+
const inner = input.tag;
|
|
58
|
+
return typeof inner === "string" ? tag(inner) : inner;
|
|
59
|
+
}
|
|
60
|
+
return input;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Preserve component identity across the JSON wire when a layer was
|
|
64
|
+
* authored with a registered `ComponentDefinition` value (e.g.
|
|
65
|
+
* `.layers({ root: Box, text: Text })`). Without this, `JSON.stringify`
|
|
66
|
+
* inlines the whole definition and drops the non-enumerable
|
|
67
|
+
* `__componentName` stamp — consumers reading `Layer.tag` from a
|
|
68
|
+
* `Config.fromJSON` round-trip then have no way to tell which
|
|
69
|
+
* registered component the layer references. We emit a `ComponentRef`
|
|
70
|
+
* marker instead so the identity round-trips.
|
|
71
|
+
*/
|
|
72
|
+
function serializeLayerTag(input) {
|
|
73
|
+
if (typeof input === "object" && input !== null && !("__kind" in input) && "__componentName" in input) {
|
|
74
|
+
const name = input.__componentName;
|
|
75
|
+
if (typeof name === "string") return {
|
|
76
|
+
__kind: "component",
|
|
77
|
+
ref: name
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return input;
|
|
81
|
+
}
|
|
82
|
+
var Layer = class {
|
|
83
|
+
name;
|
|
84
|
+
tag;
|
|
85
|
+
base;
|
|
86
|
+
constructor(args) {
|
|
87
|
+
this.name = args.name;
|
|
88
|
+
this.tag = normalizeLayerInput(args.tag);
|
|
89
|
+
this.base = args.base;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* What this layer renders, normalized across the tag's representations:
|
|
93
|
+
* - `{ kind: 'component', ref }` — renders a registered component (`Box`,
|
|
94
|
+
* `Text`), so the layer takes that component's prop surface. The identity
|
|
95
|
+
* survives both in memory (a non-enumerable `__componentName` stamp on the
|
|
96
|
+
* builder) and on the wire (`{ __kind: 'component', ref }` from `fromJSON`).
|
|
97
|
+
* - `{ kind: 'tag', ref }` — renders a bare HTML element (`'div'`).
|
|
98
|
+
* - `undefined` — an inline anonymous builder with no registered identity.
|
|
99
|
+
*/
|
|
100
|
+
get renders() {
|
|
101
|
+
const t = this.tag;
|
|
102
|
+
if (t === void 0) return void 0;
|
|
103
|
+
if (typeof t === "string") return {
|
|
104
|
+
kind: "tag",
|
|
105
|
+
ref: t
|
|
106
|
+
};
|
|
107
|
+
const obj = t;
|
|
108
|
+
if (obj.__kind === "component" && typeof obj.ref === "string") return {
|
|
109
|
+
kind: "component",
|
|
110
|
+
ref: obj.ref
|
|
111
|
+
};
|
|
112
|
+
if (obj.__kind === "tag" && typeof obj.ref === "string") return {
|
|
113
|
+
kind: "tag",
|
|
114
|
+
ref: obj.ref
|
|
115
|
+
};
|
|
116
|
+
if (typeof obj.__componentName === "string") return {
|
|
117
|
+
kind: "component",
|
|
118
|
+
ref: obj.__componentName
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
toJSON() {
|
|
122
|
+
return {
|
|
123
|
+
name: this.name,
|
|
124
|
+
...compact({
|
|
125
|
+
tag: this.tag,
|
|
126
|
+
base: this.base
|
|
127
|
+
})
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
var Component = class {
|
|
132
|
+
name;
|
|
133
|
+
layers;
|
|
134
|
+
props;
|
|
135
|
+
base;
|
|
136
|
+
defaultProps;
|
|
137
|
+
compoundProps;
|
|
138
|
+
motion;
|
|
139
|
+
preview;
|
|
140
|
+
metadata;
|
|
141
|
+
/**
|
|
142
|
+
* Absolute path of the file that called `defineComponent('Name', …)`.
|
|
143
|
+
* Captured via the stack walk in `captureCallerPath` and stashed
|
|
144
|
+
* non-enumerably on the authored definition; lifted onto the class
|
|
145
|
+
* here so codegen consumers don't have to thread a separate
|
|
146
|
+
* `componentSourceMap`. `undefined` when the component was hydrated
|
|
147
|
+
* from JSON (no original define call) or built outside the `define*`
|
|
148
|
+
* helpers.
|
|
149
|
+
*/
|
|
150
|
+
sourceFilePath;
|
|
151
|
+
/**
|
|
152
|
+
* `true` when the authored definition chained `.render(fn)` — the
|
|
153
|
+
* non-enumerable `__hasRenderFn` flag set by the builder. Codegen
|
|
154
|
+
* needs this to classify single-layer components that ship a custom
|
|
155
|
+
* render fn (e.g. Accordion) into the transform path rather than
|
|
156
|
+
* the inline primitive emitter, so the two paths don't both emit a
|
|
157
|
+
* wrapper FC with the same name.
|
|
158
|
+
*/
|
|
159
|
+
hasRenderFn;
|
|
160
|
+
/**
|
|
161
|
+
* The actual `.render(fn)` callback when `hasRenderFn` is `true`.
|
|
162
|
+
* Lifted from the seed's non-enumerable `__renderFn` so the spec
|
|
163
|
+
* extractor can invoke it under the mock-react plugin to capture
|
|
164
|
+
* the component's internal structure as a Spec. Not used at
|
|
165
|
+
* production render time — the codegen-emitted per-file FC handles
|
|
166
|
+
* that path.
|
|
167
|
+
*/
|
|
168
|
+
renderFn;
|
|
169
|
+
/**
|
|
170
|
+
* Name of the component this one value-extends from
|
|
171
|
+
* (`defineComponent(Box)` → `'Box'`). Resolved from the non-
|
|
172
|
+
* enumerable `__extendsFrom.__componentName` chain stamped by
|
|
173
|
+
* `defineComponent`'s value-extend branch and `Config.registerComponents`'
|
|
174
|
+
* registration loop. `undefined` for primitives, brace-ref shorthand
|
|
175
|
+
* forms, and JSON-hydrated configs (the wire format doesn't carry
|
|
176
|
+
* this relationship).
|
|
177
|
+
*/
|
|
178
|
+
extendsFrom;
|
|
179
|
+
#componentGroup;
|
|
180
|
+
#subcomponentOf;
|
|
181
|
+
#subcomponents;
|
|
182
|
+
#namespaceGetter;
|
|
183
|
+
#prefixGetter;
|
|
184
|
+
#componentResolver;
|
|
185
|
+
#resolveStylePropValueToken;
|
|
186
|
+
#isStyleProp;
|
|
187
|
+
/**
|
|
188
|
+
* Globally-unique lookup key — the spec `type` string a renderer
|
|
189
|
+
* resolves against the registry. When the owning `Config` has a
|
|
190
|
+
* namespace set (`Config.configure({ namespace: 'uds' })`), this is
|
|
191
|
+
* `<namespace>:<name>` (`uds:Badge`); otherwise it's the bare `name`
|
|
192
|
+
* (un-namespaced configs). Read live via a getter so it reflects the
|
|
193
|
+
* namespace regardless of whether `configure` ran before or after the
|
|
194
|
+
* component was registered — mirrors the lazy `prefixGetter` pattern
|
|
195
|
+
* used for token CSS vars.
|
|
196
|
+
*
|
|
197
|
+
* `name` stays the bare authored/display/export identifier; codegen
|
|
198
|
+
* emits a bare-name alias alongside the canonical key so existing
|
|
199
|
+
* specs referencing the bare `name` keep resolving.
|
|
200
|
+
*/
|
|
201
|
+
get registryKey() {
|
|
202
|
+
const namespace = this.#namespaceGetter?.();
|
|
203
|
+
return namespace ? `${namespace}:${this.name}` : this.name;
|
|
204
|
+
}
|
|
205
|
+
#derived;
|
|
206
|
+
constructor(args) {
|
|
207
|
+
this.name = args.name;
|
|
208
|
+
this.#namespaceGetter = args.namespaceGetter;
|
|
209
|
+
this.#prefixGetter = args.prefixGetter;
|
|
210
|
+
this.#componentResolver = args.componentResolver;
|
|
211
|
+
this.#resolveStylePropValueToken = args.resolveStylePropValueToken;
|
|
212
|
+
this.#isStyleProp = args.isStyleProp;
|
|
213
|
+
const layers = /* @__PURE__ */ new Map();
|
|
214
|
+
const liftedLayerBase = {};
|
|
215
|
+
for (const [layerName, tag] of Object.entries(args.definition.layers)) {
|
|
216
|
+
layers.set(layerName, new Layer({
|
|
217
|
+
name: layerName,
|
|
218
|
+
tag
|
|
219
|
+
}));
|
|
220
|
+
if (typeof tag === "object" && tag !== null && !("__kind" in tag) && "tag" in tag) {
|
|
221
|
+
const overrides = tag;
|
|
222
|
+
const merged = {
|
|
223
|
+
...overrides.defaultProps,
|
|
224
|
+
...overrides.base
|
|
225
|
+
};
|
|
226
|
+
if (Object.keys(merged).length > 0) liftedLayerBase[layerName] = merged;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
this.layers = layers;
|
|
230
|
+
this.props = args.definition.props ?? {};
|
|
231
|
+
const authoredBase = args.definition.base;
|
|
232
|
+
if (Object.keys(liftedLayerBase).length > 0) {
|
|
233
|
+
const merged = { ...liftedLayerBase };
|
|
234
|
+
if (authoredBase) for (const [layerName, layerStyles] of Object.entries(authoredBase)) merged[layerName] = {
|
|
235
|
+
...merged[layerName] ?? {},
|
|
236
|
+
...layerStyles
|
|
237
|
+
};
|
|
238
|
+
this.base = merged;
|
|
239
|
+
} else this.base = authoredBase;
|
|
240
|
+
this.defaultProps = args.definition.defaultProps;
|
|
241
|
+
this.compoundProps = args.definition.compoundProps;
|
|
242
|
+
this.motion = args.definition.motion;
|
|
243
|
+
const def = args.definition;
|
|
244
|
+
const previewField = typeof def.preview === "function" ? void 0 : def.preview;
|
|
245
|
+
this.preview = def.__preview ?? previewField;
|
|
246
|
+
const metaField = def.metadata;
|
|
247
|
+
this.metadata = {
|
|
248
|
+
...metaField,
|
|
249
|
+
label: metaField?.label ?? humanizeComponentName(this.name)
|
|
250
|
+
};
|
|
251
|
+
this.sourceFilePath = readSourcePath(args.definition);
|
|
252
|
+
this.hasRenderFn = args.definition.__hasRenderFn === true;
|
|
253
|
+
this.renderFn = args.definition.__renderFn;
|
|
254
|
+
const extendsFromSeed = args.definition.__extendsFrom;
|
|
255
|
+
const extendsFromName = extendsFromSeed && typeof extendsFromSeed === "object" && typeof extendsFromSeed.__componentName === "string" ? extendsFromSeed.__componentName : void 0;
|
|
256
|
+
this.extendsFrom = extendsFromName ?? args.definition.extendsFrom;
|
|
257
|
+
this.#componentGroup = args.componentGroup;
|
|
258
|
+
this.#subcomponentOf = args.subcomponentOf;
|
|
259
|
+
this.#subcomponents = args.subcomponents;
|
|
260
|
+
}
|
|
261
|
+
get derived() {
|
|
262
|
+
if (!this.#derived) {
|
|
263
|
+
const propsArray = [];
|
|
264
|
+
const propsIndex = /* @__PURE__ */ new Map();
|
|
265
|
+
for (const [propName, binding] of Object.entries(this.props)) {
|
|
266
|
+
const info = inferPropInfo(propName, binding, this.layers, this.#componentResolver);
|
|
267
|
+
if (info === void 0) continue;
|
|
268
|
+
propsArray.push(info);
|
|
269
|
+
propsIndex.set(propName, info);
|
|
270
|
+
}
|
|
271
|
+
const styleUsage = collectStyleUsage(this.base, this.props, this.compoundProps, this.#resolveStylePropValueToken, this.#isStyleProp);
|
|
272
|
+
this.#derived = {
|
|
273
|
+
layers: Array.from(this.layers.values()),
|
|
274
|
+
classNames: componentClassNames(this.#prefixGetter?.() ?? "", this.name),
|
|
275
|
+
compoundOverrides: buildCompoundOverrides(this.compoundProps),
|
|
276
|
+
...compact({
|
|
277
|
+
componentGroup: this.#componentGroup,
|
|
278
|
+
subcomponentOf: this.#subcomponentOf,
|
|
279
|
+
subcomponents: this.#subcomponents
|
|
280
|
+
}),
|
|
281
|
+
props: propsArray,
|
|
282
|
+
stylePropNames: propsArray.filter((p) => p.kind === "styleProp").map((p) => p.name),
|
|
283
|
+
tokenRefs: styleUsage.tokenRefs,
|
|
284
|
+
stylePropTokenUsages: styleUsage.stylePropTokenUsages,
|
|
285
|
+
stylePropKeywords: styleUsage.stylePropKeywords,
|
|
286
|
+
kind: this.#deriveKind(),
|
|
287
|
+
acceptsChildren: derivesAcceptsChildren(this.layers, propsArray),
|
|
288
|
+
acceptsChildNodes: derivesAcceptsChildNodes(this.layers, propsArray),
|
|
289
|
+
prop(propName) {
|
|
290
|
+
return propsIndex.get(propName);
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
return this.#derived;
|
|
295
|
+
}
|
|
296
|
+
/** Classify authoring shape: `extends` wins (value-extension is the defining
|
|
297
|
+
* trait), then anything with multiple layers or a custom render is
|
|
298
|
+
* `multi-layer`, else a bare single-tag `primitive`. */
|
|
299
|
+
#deriveKind() {
|
|
300
|
+
if (this.extendsFrom) return "extends";
|
|
301
|
+
if (this.layers.size > 1 || this.hasRenderFn) return "multi-layer";
|
|
302
|
+
return "primitive";
|
|
303
|
+
}
|
|
304
|
+
toJSON(opts) {
|
|
305
|
+
const layers = {};
|
|
306
|
+
for (const [layerName, layer] of this.layers) {
|
|
307
|
+
if (layer.tag === void 0) continue;
|
|
308
|
+
layers[layerName] = serializeLayerTag(layer.tag);
|
|
309
|
+
}
|
|
310
|
+
let sourceFilePath = this.sourceFilePath;
|
|
311
|
+
if (sourceFilePath && opts?.projectRoot) sourceFilePath = sourceFilePath.replace(`${opts.projectRoot}/`, "");
|
|
312
|
+
return {
|
|
313
|
+
name: this.name,
|
|
314
|
+
layers,
|
|
315
|
+
...compact({
|
|
316
|
+
props: Object.keys(this.props).length > 0 ? serializeAcceptsInProps(this.props) : void 0,
|
|
317
|
+
base: this.base,
|
|
318
|
+
defaultProps: this.defaultProps,
|
|
319
|
+
compoundProps: this.compoundProps,
|
|
320
|
+
motion: this.motion,
|
|
321
|
+
preview: this.preview ? serializeElementsDeep(this.preview) : void 0,
|
|
322
|
+
metadata: this.metadata,
|
|
323
|
+
sourceFilePath,
|
|
324
|
+
subcomponentOf: this.#subcomponentOf,
|
|
325
|
+
subcomponents: this.#subcomponents,
|
|
326
|
+
extendsFrom: this.extendsFrom
|
|
327
|
+
})
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
/**
|
|
332
|
+
* The token + keyword usage a component carries across its styling — one walk
|
|
333
|
+
* of `base`, each prop binding's variant styles, and `compoundProps` overrides,
|
|
334
|
+
* recursing modifier sub-objects (`_hover`) and color expressions. Produces
|
|
335
|
+
* three views the reverse lookups read for "what breaks if I remove this?":
|
|
336
|
+
* - `tokenRefs` — every qualified token name referenced, from structured
|
|
337
|
+
* `token('color/brand')` refs (matched at any depth) and shorthand
|
|
338
|
+
* style-prop values (`bg: 'tertiary'`, resolved via `resolveStylePropValueToken`,
|
|
339
|
+
* which returns `undefined` for keywords / arbitrary literals).
|
|
340
|
+
* - `stylePropTokenUsages` — the `(styleProp, token)` pair for each, so a
|
|
341
|
+
* consumer can tell *which* prop used a token. A token's style prop is the
|
|
342
|
+
* key it sits under: directly (`bg: token(...)` / `bg: 'tertiary'`) or as a
|
|
343
|
+
* color-expression value (`bg: darken(token(...))`), whose nested refs are
|
|
344
|
+
* all attributed to that key.
|
|
345
|
+
* - `stylePropKeywords` — `(styleProp, value)` pairs for the *non-token*
|
|
346
|
+
* keyword values a style prop carries (`display=flex`). Disjoint from
|
|
347
|
+
* `tokenRefs`; `isStyleProp` gates it so layer keys / marker internals
|
|
348
|
+
* don't read as keywords, and bracketed arbitrary literals are excluded.
|
|
349
|
+
* Internal `__`-prefixed keys (`__kind`, a slot's `__accepts` child defs) are
|
|
350
|
+
* skipped, so a child's usage never bleeds into the parent's sets.
|
|
351
|
+
*/
|
|
352
|
+
function collectStyleUsage(base, props, compoundProps, resolveStylePropValueToken, isStyleProp) {
|
|
353
|
+
const refs = /* @__PURE__ */ new Set();
|
|
354
|
+
const pairKeys = /* @__PURE__ */ new Set();
|
|
355
|
+
const pairs = [];
|
|
356
|
+
const keywords = /* @__PURE__ */ new Map();
|
|
357
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
358
|
+
const addPair = (styleProp, token) => {
|
|
359
|
+
const key = `${styleProp}${token}`;
|
|
360
|
+
if (pairKeys.has(key)) return;
|
|
361
|
+
pairKeys.add(key);
|
|
362
|
+
pairs.push({
|
|
363
|
+
styleProp,
|
|
364
|
+
token
|
|
365
|
+
});
|
|
366
|
+
};
|
|
367
|
+
const collectUnder = (node, styleProp) => {
|
|
368
|
+
if (node === null || typeof node !== "object") return;
|
|
369
|
+
if (isTokenRef(node)) {
|
|
370
|
+
refs.add(node.ref);
|
|
371
|
+
addPair(styleProp, node.ref);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
for (const value of Object.values(node)) collectUnder(value, styleProp);
|
|
375
|
+
};
|
|
376
|
+
const walk = (node) => {
|
|
377
|
+
if (node === null || typeof node !== "object") return;
|
|
378
|
+
if (isTokenRef(node)) {
|
|
379
|
+
refs.add(node.ref);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
if (seen.has(node)) return;
|
|
383
|
+
seen.add(node);
|
|
384
|
+
if (Array.isArray(node)) {
|
|
385
|
+
for (const item of node) walk(item);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
for (const [key, value] of Object.entries(node)) {
|
|
389
|
+
if (key.startsWith("__")) continue;
|
|
390
|
+
if (typeof value === "string") {
|
|
391
|
+
const ref = resolveStylePropValueToken?.(key, value);
|
|
392
|
+
if (ref) {
|
|
393
|
+
refs.add(ref);
|
|
394
|
+
addPair(key, ref);
|
|
395
|
+
} else if (isStyleProp?.(key) && !value.startsWith("[")) {
|
|
396
|
+
const values = keywords.get(key) ?? /* @__PURE__ */ new Set();
|
|
397
|
+
values.add(value);
|
|
398
|
+
keywords.set(key, values);
|
|
399
|
+
}
|
|
400
|
+
} else if (isTokenRef(value)) {
|
|
401
|
+
refs.add(value.ref);
|
|
402
|
+
addPair(key, value.ref);
|
|
403
|
+
} else if (isColorExpression(value)) collectUnder(value, key);
|
|
404
|
+
else walk(value);
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
if (base) walk(base);
|
|
408
|
+
for (const binding of Object.values(props)) walk(binding);
|
|
409
|
+
for (const entry of compoundProps ?? []) walk(entry.layers);
|
|
410
|
+
const stylePropKeywords = [...keywords].flatMap(([styleProp, values]) => [...values].map((value) => ({
|
|
411
|
+
styleProp,
|
|
412
|
+
value
|
|
413
|
+
})));
|
|
414
|
+
return {
|
|
415
|
+
tokenRefs: [...refs],
|
|
416
|
+
stylePropTokenUsages: pairs,
|
|
417
|
+
stylePropKeywords
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function inferPropInfo(name, binding, layers, resolve) {
|
|
421
|
+
if (binding === void 0 || binding === null) return void 0;
|
|
422
|
+
if (typeof binding === "function") return {
|
|
423
|
+
kind: "function",
|
|
424
|
+
name,
|
|
425
|
+
required: false,
|
|
426
|
+
isEventHandler: name.startsWith("on") && name.length > 2
|
|
427
|
+
};
|
|
428
|
+
if (typeof binding !== "object") return void 0;
|
|
429
|
+
if (isSlotMarker(binding)) return resolveSlotPropInfo(name, binding, layers, resolve);
|
|
430
|
+
if (isVariantMarker(binding)) {
|
|
431
|
+
const values = isVariantArrayMarker(binding) ? binding.values : Object.keys(binding.values);
|
|
432
|
+
return {
|
|
433
|
+
kind: "variant",
|
|
434
|
+
name,
|
|
435
|
+
required: binding.__required === true,
|
|
436
|
+
values
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
if (isBoolMarker(binding)) return {
|
|
440
|
+
kind: "boolean",
|
|
441
|
+
name,
|
|
442
|
+
required: binding.__required === true
|
|
443
|
+
};
|
|
444
|
+
if (isStringMarker(binding)) return {
|
|
445
|
+
kind: "string",
|
|
446
|
+
name,
|
|
447
|
+
required: binding.__required === true
|
|
448
|
+
};
|
|
449
|
+
if (isNumberMarker(binding)) return {
|
|
450
|
+
kind: "number",
|
|
451
|
+
name,
|
|
452
|
+
required: binding.__required === true
|
|
453
|
+
};
|
|
454
|
+
if (isStylePropRef(binding)) return {
|
|
455
|
+
kind: "styleProp",
|
|
456
|
+
name,
|
|
457
|
+
required: false,
|
|
458
|
+
stylePropName: binding.ref
|
|
459
|
+
};
|
|
460
|
+
const obj = binding;
|
|
461
|
+
if (obj.__kind === "composite" && typeof obj.ref === "string") return {
|
|
462
|
+
kind: "composite",
|
|
463
|
+
name,
|
|
464
|
+
required: false,
|
|
465
|
+
compositeName: obj.ref,
|
|
466
|
+
...typeof obj.layer === "string" ? { layer: obj.layer } : {}
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Build a `kind: 'slot'` `PropInfo` from a `SlotMarker | SlotRef`.
|
|
471
|
+
* Walks the slot's forward chain (when it targets a composed-component
|
|
472
|
+
* layer's slot prop) to resolve:
|
|
473
|
+
*
|
|
474
|
+
* - `accepts` — outer marker's `__accepts` wins; otherwise inherits
|
|
475
|
+
* from the innermost descendant slot that declared `.accepts(...)`.
|
|
476
|
+
* - `valueType` — what the JSX value resolves to at the terminal
|
|
477
|
+
* binding (the non-slot binding the chain bottoms out at, or
|
|
478
|
+
* `'ReactNode'` for open/children slots).
|
|
479
|
+
*
|
|
480
|
+
* Cycle protection: `seen` keys on `"layer/layerProp"`.
|
|
481
|
+
*/
|
|
482
|
+
function resolveSlotPropInfo(name, binding, layers, resolve) {
|
|
483
|
+
const layer = "layer" in binding ? binding.layer : void 0;
|
|
484
|
+
const layerProp = "layerProp" in binding && binding.layerProp || "children";
|
|
485
|
+
const required = binding.__required === true;
|
|
486
|
+
const isEventHandler = layerProp.startsWith("on") && layerProp.length > 2;
|
|
487
|
+
const chain = walkSlotForwardChain(binding, layers, resolve);
|
|
488
|
+
const baseValueType = isEventHandler ? "function" : chain.valueType;
|
|
489
|
+
const authoredType = binding.__valueType ?? (baseValueType === "string" ? "string" : void 0);
|
|
490
|
+
return {
|
|
491
|
+
kind: "slot",
|
|
492
|
+
name,
|
|
493
|
+
required,
|
|
494
|
+
layerProp,
|
|
495
|
+
valueType: baseValueType,
|
|
496
|
+
...layer !== void 0 ? { layer } : {},
|
|
497
|
+
...chain.accepts !== void 0 ? { accepts: chain.accepts } : {},
|
|
498
|
+
...chain.acceptsAssetGroup !== void 0 ? { acceptsAssetGroup: chain.acceptsAssetGroup } : {},
|
|
499
|
+
...authoredType !== void 0 ? { authoredType } : {},
|
|
500
|
+
...baseValueType === "variant" && chain.options !== void 0 ? { valueOptions: chain.options } : {},
|
|
501
|
+
...baseValueType === "composite" && chain.compositeName !== void 0 ? { compositeName: chain.compositeName } : {}
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Walk a slot's forward chain across composed-component layers.
|
|
506
|
+
*
|
|
507
|
+
* The chain ends when we hit a non-slot binding (boolean/variant/string/
|
|
508
|
+
* styleProp/function/etc.), an HTML-element layer (no `__config`), or an
|
|
509
|
+
* open slot with no further layer to follow. A `children`-targeted ref is
|
|
510
|
+
* followed into its layer (a Text leaf resolves to `'string'`, a node
|
|
511
|
+
* container to `'ReactNode'`) rather than short-circuited — only the
|
|
512
|
+
* component's own open `slot()` is treated as a node container outright.
|
|
513
|
+
* `accepts` is collected as the first `__accepts` seen anywhere in the
|
|
514
|
+
* chain (outermost wins, then any inner ancestor that declared one).
|
|
515
|
+
*/
|
|
516
|
+
function walkSlotForwardChain(start, startLayers, resolve) {
|
|
517
|
+
const startAccepts = resolveAccepts(start.__accepts);
|
|
518
|
+
let accepts = startAccepts.names;
|
|
519
|
+
let acceptsAssetGroup = startAccepts.assetGroup;
|
|
520
|
+
let current = start;
|
|
521
|
+
let currentLayers = startLayers;
|
|
522
|
+
let followed = false;
|
|
523
|
+
const seen = /* @__PURE__ */ new Set();
|
|
524
|
+
while (true) {
|
|
525
|
+
const layer = "layer" in current ? current.layer : void 0;
|
|
526
|
+
const layerProp = "layerProp" in current && current.layerProp || "children";
|
|
527
|
+
if (!layer) return {
|
|
528
|
+
valueType: (followed && current.__valueType === "string" ? "string" : void 0) ?? "ReactNode",
|
|
529
|
+
accepts,
|
|
530
|
+
acceptsAssetGroup
|
|
531
|
+
};
|
|
532
|
+
const key = `${layer}/${layerProp}`;
|
|
533
|
+
if (seen.has(key)) return {
|
|
534
|
+
valueType: "ReactNode",
|
|
535
|
+
accepts,
|
|
536
|
+
acceptsAssetGroup
|
|
537
|
+
};
|
|
538
|
+
seen.add(key);
|
|
539
|
+
const innerLayers = currentLayers;
|
|
540
|
+
if (!innerLayers) return {
|
|
541
|
+
valueType: "ReactNode",
|
|
542
|
+
accepts,
|
|
543
|
+
acceptsAssetGroup
|
|
544
|
+
};
|
|
545
|
+
const layerTag = innerLayers.get(layer)?.tag;
|
|
546
|
+
const surface = resolveLayerSurface(layerTag, resolve);
|
|
547
|
+
if (!surface) return {
|
|
548
|
+
valueType: layerProp === "children" ? "ReactNode" : "string",
|
|
549
|
+
accepts,
|
|
550
|
+
acceptsAssetGroup
|
|
551
|
+
};
|
|
552
|
+
const innerBinding = surface.props?.[layerProp];
|
|
553
|
+
if (innerBinding === void 0) return {
|
|
554
|
+
valueType: layerProp === "children" ? "ReactNode" : "string",
|
|
555
|
+
accepts,
|
|
556
|
+
acceptsAssetGroup
|
|
557
|
+
};
|
|
558
|
+
if (isSlotMarker(innerBinding)) {
|
|
559
|
+
if (accepts === void 0 || acceptsAssetGroup === void 0) {
|
|
560
|
+
const inner = resolveAccepts(innerBinding.__accepts);
|
|
561
|
+
if (accepts === void 0) accepts = inner.names;
|
|
562
|
+
if (acceptsAssetGroup === void 0) acceptsAssetGroup = inner.assetGroup;
|
|
563
|
+
}
|
|
564
|
+
current = innerBinding;
|
|
565
|
+
currentLayers = surface.layers;
|
|
566
|
+
followed = true;
|
|
567
|
+
continue;
|
|
568
|
+
}
|
|
569
|
+
return {
|
|
570
|
+
...terminalResult(innerBinding),
|
|
571
|
+
accepts,
|
|
572
|
+
acceptsAssetGroup
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* A layer's prop surface + sub-layers, resolved from either an inline
|
|
578
|
+
* composed definition or a registered-component ref. `undefined` for HTML-
|
|
579
|
+
* element layers (`'div'`) and unresolvable refs — the chain stops there.
|
|
580
|
+
*/
|
|
581
|
+
function resolveLayerSurface(layerTag, resolve) {
|
|
582
|
+
const inlineDef = getComposedLayerDefinition(layerTag);
|
|
583
|
+
if (inlineDef) return {
|
|
584
|
+
props: inlineDef.props,
|
|
585
|
+
layers: toLayerMap(inlineDef.layers)
|
|
586
|
+
};
|
|
587
|
+
const ref = componentRefName(layerTag);
|
|
588
|
+
const comp = ref ? resolve?.(ref) : void 0;
|
|
589
|
+
if (comp) return {
|
|
590
|
+
props: comp.props,
|
|
591
|
+
layers: comp.layers
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
/** Read a registered-component-ref layer tag's target name, from the wire
|
|
595
|
+
* form (`{ __kind: 'component', ref }`) or the in-memory `__componentName`
|
|
596
|
+
* stamp. Element/inline layers have neither and return `undefined`. */
|
|
597
|
+
function componentRefName(tag) {
|
|
598
|
+
if (tag === null || typeof tag !== "object") return void 0;
|
|
599
|
+
const obj = tag;
|
|
600
|
+
if (obj.__kind === "component" && typeof obj.ref === "string") return obj.ref;
|
|
601
|
+
if (typeof obj.__componentName === "string") return obj.__componentName;
|
|
602
|
+
}
|
|
603
|
+
/** Resolve a terminal (non-slot) binding's value kind plus, for a variant, its
|
|
604
|
+
* selectable values, or for a composite ref its registry name, so tools can
|
|
605
|
+
* render the matching control (a dropdown / composite picker). */
|
|
606
|
+
function terminalResult(binding) {
|
|
607
|
+
if (typeof binding === "function") return { valueType: "function" };
|
|
608
|
+
if (binding === null || typeof binding !== "object") return { valueType: "string" };
|
|
609
|
+
if (isVariantMarker(binding)) return {
|
|
610
|
+
valueType: "variant",
|
|
611
|
+
options: isVariantArrayMarker(binding) ? binding.values : Object.keys(binding.values)
|
|
612
|
+
};
|
|
613
|
+
if (isCompositeRef(binding)) return {
|
|
614
|
+
valueType: "composite",
|
|
615
|
+
compositeName: binding.ref
|
|
616
|
+
};
|
|
617
|
+
if (isBoolMarker(binding)) return { valueType: "boolean" };
|
|
618
|
+
if (isNumberMarker(binding)) return { valueType: "number" };
|
|
619
|
+
if (isStringMarker(binding)) return { valueType: "string" };
|
|
620
|
+
if (isStylePropRef(binding)) return { valueType: "string" };
|
|
621
|
+
return { valueType: "string" };
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Resolve a slot's `__accepts` into the two distinct things a slot can
|
|
625
|
+
* accept: registered component *names* and a placeable asset-group *slug*.
|
|
626
|
+
*
|
|
627
|
+
* Component entries are `ComponentDefinition`s in memory (carrying an
|
|
628
|
+
* authored `name` or the `__componentName` stamp) and bare name strings on
|
|
629
|
+
* the wire; either way `readDefinitionName` reads the name. Asset-group
|
|
630
|
+
* entries are the double-duty group definition in memory and a
|
|
631
|
+
* `{ __kind: 'assetGroup', ref }` marker on the wire; `isAssetGroupRef`
|
|
632
|
+
* catches both and `readAcceptedAssetGroupSlug` reads the slug. The asset
|
|
633
|
+
* branch throws for a non-placeable class (a font group), keyed to the
|
|
634
|
+
* `isPlaceable` capability rather than to any one class — without it a font
|
|
635
|
+
* group would silently contribute nothing and the designer panel would fall
|
|
636
|
+
* back to the all-components picker.
|
|
637
|
+
*
|
|
638
|
+
* `names` is `undefined` (not `[]`) when no component was accepted, so callers
|
|
639
|
+
* can distinguish "unconstrained" from "empty list"; `assetGroup` is the first
|
|
640
|
+
* accepted group's slug (slots target a single group in practice).
|
|
641
|
+
*/
|
|
642
|
+
function resolveAccepts(accepts) {
|
|
643
|
+
if (!accepts) return {};
|
|
644
|
+
const names = [];
|
|
645
|
+
let assetGroup;
|
|
646
|
+
for (const entry of accepts) {
|
|
647
|
+
if (isAssetGroupRef(entry)) {
|
|
648
|
+
assertAcceptedAssetGroupPlaceable(entry);
|
|
649
|
+
const slug = readAcceptedAssetGroupSlug(entry);
|
|
650
|
+
if (slug === void 0) throw new Error(UNREGISTERED_ACCEPTED_GROUP_ERROR);
|
|
651
|
+
if (assetGroup === void 0) assetGroup = slug;
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
const name = readDefinitionName(entry);
|
|
655
|
+
if (name) names.push(name);
|
|
656
|
+
}
|
|
657
|
+
return {
|
|
658
|
+
names: names.length > 0 ? names : void 0,
|
|
659
|
+
assetGroup
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Read an accepted asset group's slug from either form an `__accepts` entry
|
|
664
|
+
* takes: the live double-duty definition (slug on the internal `__slug` slot)
|
|
665
|
+
* or the serialized `{ __kind: 'assetGroup', ref }` wire marker.
|
|
666
|
+
*
|
|
667
|
+
* Crucially this never reads the live definition's `ref` *getter* — that getter
|
|
668
|
+
* throws "read before registration" until `registerAssetGroups` stamps `__slug`,
|
|
669
|
+
* which would turn a recoverable "group not registered" condition into a cryptic
|
|
670
|
+
* crash on the first `derived`/`toJSON` access. The `.ref` fallback is reached
|
|
671
|
+
* only for wire markers (no `assetKind` field); an unregistered *live* group
|
|
672
|
+
* resolves to `undefined` here, and the caller raises a clear error.
|
|
673
|
+
*/
|
|
674
|
+
function readAcceptedAssetGroupSlug(entry) {
|
|
675
|
+
const live = readAssetGroupSlug(entry);
|
|
676
|
+
if (live) return live;
|
|
677
|
+
if (entry !== null && typeof entry === "object" && !("assetKind" in entry)) {
|
|
678
|
+
const ref = entry.ref;
|
|
679
|
+
if (typeof ref === "string" && ref.length > 0) return ref;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
/** Shared message for an accepted asset group whose slug can't be resolved —
|
|
683
|
+
* it was pointed at by `.accepts(...)` but never registered. */
|
|
684
|
+
const UNREGISTERED_ACCEPTED_GROUP_ERROR = "slot .accepts(<asset group>) references a group that has not been registered — call Config.registerAssetGroups(...) for it before the slot is resolved or serialized.";
|
|
685
|
+
/**
|
|
686
|
+
* Throw for `.accepts(<non-placeable group>)`. Reads the stamped
|
|
687
|
+
* `assetKind` off the accepted group (the double-duty definition
|
|
688
|
+
* object); a bare `{ __kind, ref }` marker carries no class to
|
|
689
|
+
* validate, so it passes through.
|
|
690
|
+
*/
|
|
691
|
+
function assertAcceptedAssetGroupPlaceable(entry, context) {
|
|
692
|
+
const group = entry;
|
|
693
|
+
const assetKind = group.assetKind;
|
|
694
|
+
if (assetKind === void 0) return;
|
|
695
|
+
if (isPlaceable({ assetKind })) return;
|
|
696
|
+
const slug = readAssetGroupSlug(entry) ?? group.label ?? "unknown";
|
|
697
|
+
const where = context ? `component "${context.componentName}" prop "${context.propName}": ` : "";
|
|
698
|
+
throw new Error(`${where}slot accepts asset group "${slug}" (class "${assetKind}"), which is not placeable — only placeable asset classes (e.g. icons) can fill a slot.`);
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Catches a slot that declares `.accepts(<non-placeable group>)` — e.g.
|
|
702
|
+
* a slot wired to accept a font group, which can never fill a slot.
|
|
703
|
+
*
|
|
704
|
+
* This is the *eager* guard: `Config.registerComponents` and
|
|
705
|
+
* `registerComponentGroups` run it once per definition at registration
|
|
706
|
+
* time, so the error surfaces right when the component is registered
|
|
707
|
+
* rather than later, on the first lazy `derived` read buried deep in a
|
|
708
|
+
* render path (where it'd be much harder to trace back to the cause).
|
|
709
|
+
*
|
|
710
|
+
* There's also a *lazy* check inside `resolveAccepts`. We keep both
|
|
711
|
+
* on purpose: this eager walk only sees the top-level definition, so the
|
|
712
|
+
* lazy check is the backstop for bindings it can't reach. (Inner slots
|
|
713
|
+
* of a composed layer aren't a gap — they get validated through their
|
|
714
|
+
* own component's registration.)
|
|
715
|
+
*
|
|
716
|
+
* The same pass enforces the `slot({ type: 'component' })` contract: a
|
|
717
|
+
* component-typed slot resolves to the icon component itself, which is only
|
|
718
|
+
* meaningful when it accepts an asset group, so one declared without an
|
|
719
|
+
* asset-group `.accepts(...)` is a config-load error.
|
|
720
|
+
*
|
|
721
|
+
* Hydration note: the serialized `{ __kind: 'assetGroup', ref }` marker
|
|
722
|
+
* (`serializeAcceptsList`) is enumerable plain JSON, so `isAssetGroupRef`
|
|
723
|
+
* recognizes it browser-side and the placeable group's slug reaches
|
|
724
|
+
* `SlotInfo.acceptsAssetGroup` after a round-trip. The placeability throw,
|
|
725
|
+
* however, does NOT re-fire on the wire form — the marker carries no
|
|
726
|
+
* `assetKind`, so `assertAcceptedAssetGroupPlaceable` returns early. That's
|
|
727
|
+
* fine in practice: `serializeAcceptsList` runs the same throw on the way
|
|
728
|
+
* *out*, so a non-placeable group can never be written to a wire blob this
|
|
729
|
+
* code produces. The browser-side guarantee is the safe-degradation one —
|
|
730
|
+
* an unresolvable or non-icon group yields no `acceptsAssetGroup` and an
|
|
731
|
+
* empty picker, never the all-components fallback — not a re-validation.
|
|
732
|
+
*/
|
|
733
|
+
function assertDefinitionAcceptsPlaceable(componentName, definition) {
|
|
734
|
+
for (const [propName, binding] of Object.entries(definition.props ?? {})) {
|
|
735
|
+
if (!isSlotMarker(binding)) continue;
|
|
736
|
+
const groupRefs = (binding.__accepts ?? []).filter(isAssetGroupRef);
|
|
737
|
+
for (const entry of groupRefs) assertAcceptedAssetGroupPlaceable(entry, {
|
|
738
|
+
componentName,
|
|
739
|
+
propName
|
|
740
|
+
});
|
|
741
|
+
if (groupRefs.length > 1) throw new Error(`component "${componentName}" prop "${propName}": a slot can accept at most one asset group (got ${groupRefs.length}).`);
|
|
742
|
+
const isForwardingRef = "layer" in binding && binding.layer !== void 0;
|
|
743
|
+
if (binding.__valueType === "component" && groupRefs.length === 0 && !isForwardingRef) throw new Error(`component "${componentName}" prop "${propName}": slot({ type: 'component' }) requires an asset-group .accepts() — it resolves to the asset's component (e.g. the aliased Icon pattern), so it must target a placeable asset group.`);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
function readDefinitionName(entry) {
|
|
747
|
+
if (typeof entry === "string") return entry.length > 0 ? entry : void 0;
|
|
748
|
+
if (entry === null) return void 0;
|
|
749
|
+
if (typeof entry !== "object" && typeof entry !== "function") return void 0;
|
|
750
|
+
const direct = entry.name;
|
|
751
|
+
if (typeof direct === "string" && direct.length > 0) return direct;
|
|
752
|
+
const stamped = entry.__componentName;
|
|
753
|
+
if (typeof stamped === "string" && stamped.length > 0) return stamped;
|
|
754
|
+
const inner = entry.__config;
|
|
755
|
+
if (inner && typeof inner === "object") return readDefinitionName(inner);
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Rewrite each slot binding's `__accepts` to bare component-name strings
|
|
759
|
+
* for the wire. In memory `__accepts` holds whole `ComponentDefinition`
|
|
760
|
+
* references; serializing them verbatim both bloats the JSON (the entire
|
|
761
|
+
* accepted component is deep-cloned into every consumer) and loses the
|
|
762
|
+
* non-enumerable `__componentName` stamp `JSON.stringify` drops — so after
|
|
763
|
+
* a `fromJSON` round-trip the names can't be recovered and the slot reads
|
|
764
|
+
* as unconstrained. Resolving to names here (while the stamp is present)
|
|
765
|
+
* keeps `accepts` intact across the DB round-trip. Non-slot bindings pass
|
|
766
|
+
* through untouched; the marker's methods are non-enumerable and so are
|
|
767
|
+
* already dropped by the spread.
|
|
768
|
+
*
|
|
769
|
+
* Asset-group entries can't be name strings — the group's live FCs are
|
|
770
|
+
* non-enumerable (so they never leak across the wire) and its `__kind`/`ref`
|
|
771
|
+
* tag is likewise non-enumerable, so a verbatim `JSON.stringify` would drop
|
|
772
|
+
* the ref entirely and the slot would rehydrate as unconstrained. They are
|
|
773
|
+
* rewritten to an explicit `{ __kind: 'assetGroup', ref: slug }` marker —
|
|
774
|
+
* plain JSON that `isAssetGroupRef` recognizes on the way back in.
|
|
775
|
+
*/
|
|
776
|
+
function serializeAcceptsInProps(props) {
|
|
777
|
+
const out = {};
|
|
778
|
+
for (const [name, binding] of Object.entries(props)) {
|
|
779
|
+
if (!isSlotMarker(binding)) {
|
|
780
|
+
out[name] = binding;
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
const { __accepts: _drop, ...rest } = binding;
|
|
784
|
+
const list = serializeAcceptsList(binding.__accepts);
|
|
785
|
+
out[name] = list ? {
|
|
786
|
+
...rest,
|
|
787
|
+
__accepts: list
|
|
788
|
+
} : rest;
|
|
789
|
+
}
|
|
790
|
+
return out;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Serialize a slot's `__accepts` to its wire form: component entries become
|
|
794
|
+
* bare name strings, asset-group entries become `{ __kind: 'assetGroup', ref }`
|
|
795
|
+
* markers. The non-placeability throw fires here too, so a bad group can't slip
|
|
796
|
+
* onto the wire even if a caller serializes before the eager registration
|
|
797
|
+
* guard runs. Returns `undefined` for an empty/absent list so the serializer
|
|
798
|
+
* omits `__accepts` rather than writing `[]`.
|
|
799
|
+
*/
|
|
800
|
+
function serializeAcceptsList(accepts) {
|
|
801
|
+
if (!accepts) return void 0;
|
|
802
|
+
const out = [];
|
|
803
|
+
for (const entry of accepts) {
|
|
804
|
+
if (isAssetGroupRef(entry)) {
|
|
805
|
+
assertAcceptedAssetGroupPlaceable(entry);
|
|
806
|
+
const slug = readAcceptedAssetGroupSlug(entry);
|
|
807
|
+
if (slug === void 0) throw new Error(UNREGISTERED_ACCEPTED_GROUP_ERROR);
|
|
808
|
+
out.push({
|
|
809
|
+
__kind: "assetGroup",
|
|
810
|
+
ref: slug
|
|
811
|
+
});
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
const componentName = readDefinitionName(entry);
|
|
815
|
+
if (componentName) out.push(componentName);
|
|
816
|
+
}
|
|
817
|
+
return out.length > 0 ? out : void 0;
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Pull the inner `ComponentDefinition` off a layer tag value when the
|
|
821
|
+
* layer was authored as a composed-component reference. HTML-element
|
|
822
|
+
* layers (`tag('div')` / bare `'div'`) and `ComponentRef` markers
|
|
823
|
+
* return `undefined` — the chain walk stops there.
|
|
824
|
+
*/
|
|
825
|
+
function getComposedLayerDefinition(tagValue) {
|
|
826
|
+
if (tagValue === void 0 || tagValue === null) return void 0;
|
|
827
|
+
if (typeof tagValue === "string") return void 0;
|
|
828
|
+
if (typeof tagValue !== "object" && typeof tagValue !== "function") return;
|
|
829
|
+
const obj = tagValue;
|
|
830
|
+
if (obj.__kind === "tag" || obj.__kind === "component") return void 0;
|
|
831
|
+
if (obj.__config && typeof obj.__config === "object") return obj.__config;
|
|
832
|
+
if (obj.layers && typeof obj.layers === "object") return tagValue;
|
|
833
|
+
}
|
|
834
|
+
/** Build a `Map<layerName, Layer>` from a definition's authored layers. */
|
|
835
|
+
function toLayerMap(layersDef) {
|
|
836
|
+
if (!layersDef) return void 0;
|
|
837
|
+
const out = /* @__PURE__ */ new Map();
|
|
838
|
+
for (const [layerName, layerTag] of Object.entries(layersDef)) out.set(layerName, new Layer({
|
|
839
|
+
name: layerName,
|
|
840
|
+
tag: layerTag
|
|
841
|
+
}));
|
|
842
|
+
return out;
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* A single primitive root layer (`{ root: 'div' }` or `{ root: tag('div') }`)
|
|
846
|
+
* — render forwards children straight to the element, so the component holds
|
|
847
|
+
* arbitrary node children.
|
|
848
|
+
*/
|
|
849
|
+
function hasSinglePrimitiveRoot(layers) {
|
|
850
|
+
if (layers.size !== 1) return false;
|
|
851
|
+
const t = layers.get("root")?.tag;
|
|
852
|
+
if (typeof t === "string") return true;
|
|
853
|
+
if (typeof t === "object" && t !== null) return t.__kind === "tag";
|
|
854
|
+
return false;
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* `acceptsChildren` is true when either:
|
|
858
|
+
* - the component has a single primitive root layer, OR
|
|
859
|
+
* - a `slot()` prop binds its value to a layer's `children` surface
|
|
860
|
+
* (covered by `kind: 'slot'` with `layerProp === 'children'`).
|
|
861
|
+
*
|
|
862
|
+
* Composed-component roots and multi-layer configs without a children
|
|
863
|
+
* slot do NOT accept children — the auto-render has nowhere to put
|
|
864
|
+
* them. This stays value-kind-agnostic: a string-label `children` (Button)
|
|
865
|
+
* still counts as "accepts children" for codegen/prop purposes. Tools that
|
|
866
|
+
* need "accepts *droppable layers*" should use {@link derivesAcceptsChildNodes}.
|
|
867
|
+
*/
|
|
868
|
+
function derivesAcceptsChildren(layers, props) {
|
|
869
|
+
if (hasSinglePrimitiveRoot(layers)) return true;
|
|
870
|
+
for (const info of props) if (info.kind === "slot" && info.layerProp === "children") return true;
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* `acceptsChildNodes` narrows {@link derivesAcceptsChildren} to children that
|
|
875
|
+
* are *renderable nodes* — the ones a layer-reparent tool can drop into. A
|
|
876
|
+
* single primitive root qualifies (it forwards `ReactNode` children), as does
|
|
877
|
+
* a `children` slot — unless the author declared it `slot({ type: 'string' })`
|
|
878
|
+
* (a text label like Button's). The decision keys on the *authored intent*,
|
|
879
|
+
* not the resolved `valueType` (a string label still resolves to `'ReactNode'`,
|
|
880
|
+
* so `valueType` alone can't distinguish the two).
|
|
881
|
+
*/
|
|
882
|
+
function derivesAcceptsChildNodes(layers, props) {
|
|
883
|
+
if (hasSinglePrimitiveRoot(layers)) return true;
|
|
884
|
+
for (const info of props) if (info.kind === "slot" && info.layerProp === "children" && info.authoredType !== "string") return true;
|
|
885
|
+
return false;
|
|
886
|
+
}
|
|
887
|
+
function buildCompoundOverrides(entries) {
|
|
888
|
+
const out = {};
|
|
889
|
+
if (!entries) return out;
|
|
890
|
+
for (const entry of entries) for (const [layerName, override] of Object.entries(entry.layers)) {
|
|
891
|
+
if (!out[layerName]) out[layerName] = {};
|
|
892
|
+
Object.assign(out[layerName], override);
|
|
893
|
+
}
|
|
894
|
+
return out;
|
|
895
|
+
}
|
|
896
|
+
//#endregion
|
|
897
|
+
export { Component, Layer, assertDefinitionAcceptsPlaceable };
|