@yahoo/uds-create-config 1.1.1
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/AssetGroup.d.ts +77 -0
- package/dist/AssetGroup.js +125 -0
- package/dist/Component.d.ts +308 -0
- package/dist/Component.js +896 -0
- package/dist/ComponentGroup.d.ts +20 -0
- package/dist/ComponentGroup.js +46 -0
- package/dist/CompositeStyle.d.ts +27 -0
- package/dist/CompositeStyle.js +52 -0
- package/dist/Config.d.ts +423 -0
- package/dist/Config.js +1429 -0
- package/dist/Mode.d.ts +41 -0
- package/dist/Mode.js +81 -0
- package/dist/Modifier.d.ts +51 -0
- package/dist/Modifier.js +97 -0
- package/dist/MotionDef.d.ts +49 -0
- package/dist/MotionDef.js +97 -0
- package/dist/Props.d.ts +317 -0
- package/dist/Props.js +35 -0
- package/dist/Provider.d.ts +20 -0
- package/dist/Provider.js +14 -0
- package/dist/StyleProp.d.ts +112 -0
- package/dist/StyleProp.js +197 -0
- package/dist/Token.d.ts +55 -0
- package/dist/Token.js +112 -0
- package/dist/TokenGroup.d.ts +32 -0
- package/dist/TokenGroup.js +68 -0
- package/dist/asset-kind.d.ts +55 -0
- package/dist/asset-kind.js +29 -0
- package/dist/brands.d.ts +30 -0
- package/dist/brands.js +20 -0
- package/dist/captureCallerPath.d.ts +48 -0
- package/dist/captureCallerPath.js +95 -0
- package/dist/colorExpressions.d.ts +131 -0
- package/dist/colorExpressions.js +148 -0
- package/dist/defineAssetGroup.d.ts +166 -0
- package/dist/defineAssetGroup.js +264 -0
- package/dist/defineProvider.d.ts +29 -0
- package/dist/defineProvider.js +60 -0
- package/dist/element-marker.d.ts +54 -0
- package/dist/element-marker.js +113 -0
- package/dist/entity-utils.d.ts +56 -0
- package/dist/entity-utils.js +105 -0
- package/dist/factories.d.ts +707 -0
- package/dist/factories.js +393 -0
- package/dist/foreign-component-name.d.ts +21 -0
- package/dist/foreign-component-name.js +42 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +28 -0
- package/dist/interpolate.d.ts +20 -0
- package/dist/interpolate.js +10 -0
- package/dist/jsx/__fixtures__/cross-component-preview.d.ts +3 -0
- package/dist/jsx/__fixtures__/cross-component-preview.js +15 -0
- package/dist/jsx/jsx-dev-runtime.d.ts +15 -0
- package/dist/jsx/jsx-dev-runtime.js +11 -0
- package/dist/jsx/jsx-runtime.d.ts +48 -0
- package/dist/jsx/jsx-runtime.js +305 -0
- package/dist/markers.d.ts +217 -0
- package/dist/markers.js +67 -0
- package/dist/refs.d.ts +158 -0
- package/dist/refs.js +104 -0
- package/dist/renderer/RendererErrorBoundary.d.ts +26 -0
- package/dist/renderer/RendererErrorBoundary.js +30 -0
- package/dist/renderer/UdsRenderer.d.ts +80 -0
- package/dist/renderer/UdsRenderer.js +33 -0
- package/dist/renderer/assetRenderable.d.ts +13 -0
- package/dist/renderer/assetRenderable.js +13 -0
- package/dist/renderer/index.d.ts +14 -0
- package/dist/renderer/index.js +14 -0
- package/dist/renderer/makeRegistry.d.ts +34 -0
- package/dist/renderer/makeRegistry.js +52 -0
- package/dist/renderer/makeUdsRenderer.d.ts +29 -0
- package/dist/renderer/makeUdsRenderer.js +35 -0
- package/dist/renderer/primitives/FragmentRenderer.d.ts +12 -0
- package/dist/renderer/primitives/FragmentRenderer.js +11 -0
- package/dist/renderer/primitives/SlotRenderer.d.ts +21 -0
- package/dist/renderer/primitives/SlotRenderer.js +20 -0
- package/dist/renderer/wrapRegistry.d.ts +64 -0
- package/dist/renderer/wrapRegistry.js +39 -0
- package/dist/renderer/wrappers/component-slots.d.ts +41 -0
- package/dist/renderer/wrappers/component-slots.js +66 -0
- package/dist/renderer/wrappers/event-bridge.d.ts +20 -0
- package/dist/renderer/wrappers/event-bridge.js +78 -0
- package/dist/renderer/wrappers/hex-normalize.d.ts +14 -0
- package/dist/renderer/wrappers/hex-normalize.js +35 -0
- package/dist/renderer/wrappers/html-aliases.d.ts +24 -0
- package/dist/renderer/wrappers/html-aliases.js +62 -0
- package/dist/renderer/wrappers/inline-styles.d.ts +16 -0
- package/dist/renderer/wrappers/inline-styles.js +104 -0
- package/dist/renderer/wrappers/slot-resolution.d.ts +25 -0
- package/dist/renderer/wrappers/slot-resolution.js +68 -0
- package/dist/renderer/wrappers/void-elements.d.ts +23 -0
- package/dist/renderer/wrappers/void-elements.js +28 -0
- package/dist/spec/apply-forced-modifiers.d.ts +19 -0
- package/dist/spec/apply-forced-modifiers.js +49 -0
- package/dist/spec/asset-jsx.d.ts +48 -0
- package/dist/spec/asset-jsx.js +48 -0
- package/dist/spec/collapse-text-labels.d.ts +44 -0
- package/dist/spec/collapse-text-labels.js +107 -0
- package/dist/spec/empty-node-slots.d.ts +51 -0
- package/dist/spec/empty-node-slots.js +141 -0
- package/dist/spec/index.d.ts +10 -0
- package/dist/spec/index.js +10 -0
- package/dist/spec/jsxToSpec.d.ts +48 -0
- package/dist/spec/jsxToSpec.js +506 -0
- package/dist/spec/layer-props.d.ts +52 -0
- package/dist/spec/layer-props.js +149 -0
- package/dist/spec/preview-controls.d.ts +44 -0
- package/dist/spec/preview-controls.js +139 -0
- package/dist/spec/slot-refs.d.ts +39 -0
- package/dist/spec/slot-refs.js +56 -0
- package/dist/spec/specToJsx.d.ts +35 -0
- package/dist/spec/specToJsx.js +127 -0
- package/dist/token-override-rows.d.ts +66 -0
- package/dist/token-override-rows.js +223 -0
- package/dist/tokenValueType.d.ts +34 -0
- package/dist/tokenValueType.js +138 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/css-properties.d.ts +232 -0
- package/dist/types/css-properties.js +14 -0
- package/dist/types/css-property-keywords.d.ts +156 -0
- package/dist/types/css-property-keywords.js +616 -0
- package/dist/types/css-values.d.ts +63 -0
- package/dist/types/css-values.js +16 -0
- package/dist/types.d.ts +708 -0
- package/dist/types.js +12 -0
- package/dist/units.d.ts +14 -0
- package/dist/units.js +16 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +4 -0
- package/package.json +81 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { AssetNodeResolver } from "./asset-jsx.js";
|
|
2
|
+
import { Spec } from "@json-render/core";
|
|
3
|
+
|
|
4
|
+
//#region src/spec/jsxToSpec.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Sentinel value used when a prop expression couldn't be statically resolved.
|
|
7
|
+
* Callers can check `hasUnresolvedProps(spec)` to detect this.
|
|
8
|
+
*/
|
|
9
|
+
declare const UNRESOLVED: unique symbol;
|
|
10
|
+
/** Check if any element in the spec has prop values that couldn't be statically resolved. */
|
|
11
|
+
declare function hasUnresolvedProps(spec: Spec): boolean;
|
|
12
|
+
/** Return a copy of the spec with all UNRESOLVED and STATE_SETTER prop values removed. */
|
|
13
|
+
declare function stripUnresolvedProps(spec: Spec): Spec;
|
|
14
|
+
interface JsxToSpecOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Full module source for scope resolution.
|
|
17
|
+
* When provided, top-level const declarations are resolved
|
|
18
|
+
* and local component functions are inlined.
|
|
19
|
+
*/
|
|
20
|
+
moduleSource?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Set of known component names in the registry. When provided, any
|
|
23
|
+
* PascalCase JSX element whose tag is NOT in this set (e.g. providers,
|
|
24
|
+
* wrappers) is treated as a transparent fragment — its children are
|
|
25
|
+
* flattened into the parent.
|
|
26
|
+
*/
|
|
27
|
+
knownComponents?: Set<string>;
|
|
28
|
+
/**
|
|
29
|
+
* Reconstructs placed asset nodes from authoring-component tags —
|
|
30
|
+
* `<Icon name="Trophy" …/>` → type `yahoo-os:asset:icons/Trophy`. The
|
|
31
|
+
* exact inverse of `specToJsx`'s `assetComponents` mapping; without it an
|
|
32
|
+
* `<Icon name=…/>` round-trips as a literal `Icon` component node.
|
|
33
|
+
*/
|
|
34
|
+
resolveAssetNode?: AssetNodeResolver;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Convert JSX source code into a json-render Spec.
|
|
38
|
+
*
|
|
39
|
+
* Accepts either a full JSX module or a JSX expression snippet.
|
|
40
|
+
* When `moduleSource` is provided, identifier references are resolved
|
|
41
|
+
* against the module's top-level const declarations, and local component
|
|
42
|
+
* functions are inlined.
|
|
43
|
+
*
|
|
44
|
+
* Returns `null` if no JSX element is found in the source.
|
|
45
|
+
*/
|
|
46
|
+
declare function jsxToSpec(source: string, options?: JsxToSpecOptions): Spec | null;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { JsxToSpecOptions, UNRESOLVED, hasUnresolvedProps, jsxToSpec, stripUnresolvedProps };
|
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
import { parse } from "@babel/parser";
|
|
2
|
+
//#region src/spec/jsxToSpec.ts
|
|
3
|
+
/**
|
|
4
|
+
* Sentinel value for useState setter functions.
|
|
5
|
+
* When a prop value resolves to this, the prop is a state setter (e.g. onChange={setValue})
|
|
6
|
+
* and should be stripped — $bindState handles write-back automatically.
|
|
7
|
+
*/
|
|
8
|
+
const STATE_SETTER = Symbol.for("jsxToSpec.stateSetter");
|
|
9
|
+
function buildModuleScope(ast) {
|
|
10
|
+
const scope = {
|
|
11
|
+
values: /* @__PURE__ */ new Map(),
|
|
12
|
+
valueNodes: /* @__PURE__ */ new Map(),
|
|
13
|
+
components: /* @__PURE__ */ new Map(),
|
|
14
|
+
stateBindings: /* @__PURE__ */ new Map(),
|
|
15
|
+
stateSetters: /* @__PURE__ */ new Map()
|
|
16
|
+
};
|
|
17
|
+
const body = getModuleBody(ast);
|
|
18
|
+
if (!body) return scope;
|
|
19
|
+
collectUseStateBindings(body, scope);
|
|
20
|
+
for (const stmt of body) {
|
|
21
|
+
if (stmt.type === "VariableDeclaration") {
|
|
22
|
+
const decl = stmt;
|
|
23
|
+
if (decl.kind !== "const") continue;
|
|
24
|
+
for (const declarator of decl.declarations) {
|
|
25
|
+
const d = declarator;
|
|
26
|
+
if (d.id.type !== "Identifier" || !d.init) continue;
|
|
27
|
+
const name = d.id.name;
|
|
28
|
+
const value = evaluateExpression(d.init, scope);
|
|
29
|
+
if (value !== UNRESOLVED) scope.values.set(name, value);
|
|
30
|
+
scope.valueNodes.set(name, d.init);
|
|
31
|
+
if (name[0] === name[0].toUpperCase()) {
|
|
32
|
+
const jsx = findJsxInFunctionBody(d.init);
|
|
33
|
+
if (jsx) scope.components.set(name, jsx);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (stmt.type === "FunctionDeclaration") {
|
|
38
|
+
const fn = stmt;
|
|
39
|
+
if (!fn.id || fn.id.type !== "Identifier") continue;
|
|
40
|
+
const name = fn.id.name;
|
|
41
|
+
if (name[0] === name[0].toUpperCase()) {
|
|
42
|
+
const jsx = findJsxInFunctionBody(fn);
|
|
43
|
+
if (jsx) scope.components.set(name, jsx);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return scope;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Detect `const [x, setX] = useState(initialValue)` patterns in function
|
|
51
|
+
* bodies and register them in the scope's stateBindings / stateSetters maps.
|
|
52
|
+
*
|
|
53
|
+
* Scans all function declarations and arrow function expressions at any
|
|
54
|
+
* nesting level (top-level functions, component functions, etc.).
|
|
55
|
+
*/
|
|
56
|
+
function collectUseStateBindings(stmts, scope) {
|
|
57
|
+
for (const stmt of stmts) {
|
|
58
|
+
if (stmt.type === "FunctionDeclaration") {
|
|
59
|
+
const body = stmt.body;
|
|
60
|
+
if (body.type === "BlockStatement") collectUseStateFromBlock(body.body, scope);
|
|
61
|
+
}
|
|
62
|
+
if (stmt.type === "VariableDeclaration") {
|
|
63
|
+
const decl = stmt;
|
|
64
|
+
for (const declarator of decl.declarations) {
|
|
65
|
+
const d = declarator;
|
|
66
|
+
if (!d.init) continue;
|
|
67
|
+
if (d.init.type === "ArrowFunctionExpression" || d.init.type === "FunctionExpression") {
|
|
68
|
+
const body = d.init.body;
|
|
69
|
+
if (body.type === "BlockStatement") collectUseStateFromBlock(body.body, scope);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Scan statements in a block for `const [x, setX] = useState(init)` patterns.
|
|
77
|
+
*/
|
|
78
|
+
function collectUseStateFromBlock(stmts, scope) {
|
|
79
|
+
for (const stmt of stmts) {
|
|
80
|
+
if (stmt.type !== "VariableDeclaration") continue;
|
|
81
|
+
const decl = stmt;
|
|
82
|
+
if (decl.kind !== "const") continue;
|
|
83
|
+
for (const declarator of decl.declarations) {
|
|
84
|
+
const d = declarator;
|
|
85
|
+
if (d.id.type !== "ArrayPattern" || !d.init) continue;
|
|
86
|
+
if (d.init.type !== "CallExpression") continue;
|
|
87
|
+
const call = d.init;
|
|
88
|
+
const callee = call.callee;
|
|
89
|
+
if (callee.type !== "Identifier") continue;
|
|
90
|
+
if (callee.name !== "useState") continue;
|
|
91
|
+
const elements = d.id.elements;
|
|
92
|
+
if (elements.length < 2) continue;
|
|
93
|
+
if (elements[0].type !== "Identifier" || elements[1].type !== "Identifier") continue;
|
|
94
|
+
const stateVarName = elements[0].name;
|
|
95
|
+
const setterName = elements[1].name;
|
|
96
|
+
const initArg = call.arguments[0];
|
|
97
|
+
const initialValue = initArg ? evaluateExpression(initArg, scope) : void 0;
|
|
98
|
+
scope.stateBindings.set(stateVarName, initialValue !== UNRESOLVED ? initialValue : null);
|
|
99
|
+
scope.stateSetters.set(setterName, stateVarName);
|
|
100
|
+
scope.values.set(stateVarName, { $bindState: `/${stateVarName}` });
|
|
101
|
+
scope.values.set(setterName, STATE_SETTER);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function getModuleBody(ast) {
|
|
106
|
+
if (ast.type === "File") return ast.program.body;
|
|
107
|
+
if (ast.type === "Program") return ast.body;
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Extract JSX from a function body (arrow function or function declaration).
|
|
112
|
+
*/
|
|
113
|
+
function findJsxInFunctionBody(node) {
|
|
114
|
+
if (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") {
|
|
115
|
+
const body = node.body;
|
|
116
|
+
return extractJsxFromBody(body);
|
|
117
|
+
}
|
|
118
|
+
if (node.type === "FunctionDeclaration") {
|
|
119
|
+
const body = node.body;
|
|
120
|
+
return extractJsxFromBody(body);
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
function extractJsxFromBody(body) {
|
|
125
|
+
if (body.type === "JSXElement") return body;
|
|
126
|
+
if (body.type === "JSXFragment") return body;
|
|
127
|
+
if (body.type === "ParenthesizedExpression") return extractJsxFromBody(body.expression);
|
|
128
|
+
if (body.type === "BlockStatement") {
|
|
129
|
+
const stmts = body.body;
|
|
130
|
+
for (const stmt of stmts) if (stmt.type === "ReturnStatement") {
|
|
131
|
+
const arg = stmt.argument;
|
|
132
|
+
if (arg) return extractJsxFromBody(arg);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Sentinel value used when a prop expression couldn't be statically resolved.
|
|
139
|
+
* Callers can check `hasUnresolvedProps(spec)` to detect this.
|
|
140
|
+
*/
|
|
141
|
+
const UNRESOLVED = Symbol.for("jsxToSpec.unresolved");
|
|
142
|
+
/** Check if any element in the spec has prop values that couldn't be statically resolved. */
|
|
143
|
+
function hasUnresolvedProps(spec) {
|
|
144
|
+
for (const el of Object.values(spec.elements)) if (el.props) {
|
|
145
|
+
for (const value of Object.values(el.props)) if (value === UNRESOLVED || value === STATE_SETTER) return true;
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
/** Return a copy of the spec with all UNRESOLVED and STATE_SETTER prop values removed. */
|
|
150
|
+
function stripUnresolvedProps(spec) {
|
|
151
|
+
const elements = {};
|
|
152
|
+
for (const [key, el] of Object.entries(spec.elements)) {
|
|
153
|
+
if (!el.props) {
|
|
154
|
+
elements[key] = { ...el };
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
const cleanProps = {};
|
|
158
|
+
for (const [k, v] of Object.entries(el.props)) if (v !== UNRESOLVED && v !== STATE_SETTER) cleanProps[k] = v;
|
|
159
|
+
elements[key] = {
|
|
160
|
+
...el,
|
|
161
|
+
props: cleanProps
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
...spec,
|
|
166
|
+
elements
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function evaluateExpression(node, scope) {
|
|
170
|
+
switch (node.type) {
|
|
171
|
+
case "StringLiteral": return node.value;
|
|
172
|
+
case "NumericLiteral": return node.value;
|
|
173
|
+
case "BooleanLiteral": return node.value;
|
|
174
|
+
case "NullLiteral": return null;
|
|
175
|
+
case "Identifier": {
|
|
176
|
+
const name = node.name;
|
|
177
|
+
if (name === "undefined") return UNRESOLVED;
|
|
178
|
+
if (scope.values.has(name)) return scope.values.get(name);
|
|
179
|
+
const initNode = scope.valueNodes.get(name);
|
|
180
|
+
if (initNode) {
|
|
181
|
+
const value = evaluateExpression(initNode, scope);
|
|
182
|
+
if (value !== UNRESOLVED) {
|
|
183
|
+
scope.values.set(name, value);
|
|
184
|
+
return value;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return UNRESOLVED;
|
|
188
|
+
}
|
|
189
|
+
case "TemplateLiteral": {
|
|
190
|
+
const quasis = node.quasis;
|
|
191
|
+
const expressions = node.expressions;
|
|
192
|
+
if (expressions.length === 0) return quasis[0].value.cooked;
|
|
193
|
+
let result = "";
|
|
194
|
+
for (let i = 0; i < quasis.length; i++) {
|
|
195
|
+
result += quasis[i].value.cooked;
|
|
196
|
+
if (i < expressions.length) {
|
|
197
|
+
const val = evaluateExpression(expressions[i], scope);
|
|
198
|
+
if (val === UNRESOLVED) return UNRESOLVED;
|
|
199
|
+
result += String(val);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
case "ObjectExpression": return evaluateObjectExpression(node, scope);
|
|
205
|
+
case "ArrayExpression": return evaluateArrayExpression(node, scope);
|
|
206
|
+
case "UnaryExpression": {
|
|
207
|
+
const unary = node;
|
|
208
|
+
if (unary.operator === "-") {
|
|
209
|
+
const arg = evaluateExpression(unary.argument, scope);
|
|
210
|
+
if (typeof arg === "number") return -arg;
|
|
211
|
+
}
|
|
212
|
+
if (unary.operator === "!") {
|
|
213
|
+
const arg = evaluateExpression(unary.argument, scope);
|
|
214
|
+
if (arg !== UNRESOLVED) return !arg;
|
|
215
|
+
}
|
|
216
|
+
return UNRESOLVED;
|
|
217
|
+
}
|
|
218
|
+
default: return UNRESOLVED;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function evaluateObjectExpression(node, scope) {
|
|
222
|
+
const result = {};
|
|
223
|
+
for (const prop of node.properties) {
|
|
224
|
+
if (prop.type === "SpreadElement") {
|
|
225
|
+
const arg = prop.argument;
|
|
226
|
+
const val = evaluateExpression(arg, scope);
|
|
227
|
+
if (val === UNRESOLVED || typeof val !== "object" || val === null) return UNRESOLVED;
|
|
228
|
+
Object.assign(result, val);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (prop.type !== "ObjectProperty") return UNRESOLVED;
|
|
232
|
+
const objProp = prop;
|
|
233
|
+
if (objProp.computed) return UNRESOLVED;
|
|
234
|
+
let key;
|
|
235
|
+
if (objProp.key.type === "Identifier") key = objProp.key.name;
|
|
236
|
+
else if (objProp.key.type === "StringLiteral") key = objProp.key.value;
|
|
237
|
+
else return UNRESOLVED;
|
|
238
|
+
const value = evaluateExpression(objProp.value, scope);
|
|
239
|
+
if (value !== UNRESOLVED) result[key] = value;
|
|
240
|
+
}
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
function evaluateArrayExpression(node, scope) {
|
|
244
|
+
const result = [];
|
|
245
|
+
for (const element of node.elements) {
|
|
246
|
+
if (!element) return UNRESOLVED;
|
|
247
|
+
if (element.type === "SpreadElement") {
|
|
248
|
+
const arg = element.argument;
|
|
249
|
+
const val = evaluateExpression(arg, scope);
|
|
250
|
+
if (val === UNRESOLVED || !Array.isArray(val)) return UNRESOLVED;
|
|
251
|
+
result.push(...val);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
const value = evaluateExpression(element, scope);
|
|
255
|
+
if (value === UNRESOLVED) return UNRESOLVED;
|
|
256
|
+
result.push(value);
|
|
257
|
+
}
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
function getTagName(element) {
|
|
261
|
+
const { name } = element.openingElement;
|
|
262
|
+
if (name.type === "JSXIdentifier") return name.name;
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
function getAttributeValue(valueNode, scope, ctx) {
|
|
266
|
+
if (valueNode === null) return true;
|
|
267
|
+
switch (valueNode.type) {
|
|
268
|
+
case "StringLiteral": return valueNode.value;
|
|
269
|
+
case "JSXExpressionContainer": {
|
|
270
|
+
const expr = valueNode.expression;
|
|
271
|
+
if (ctx && (expr.type === "JSXElement" || expr.type === "JSXFragment")) {
|
|
272
|
+
const slotKey = convertJsxNode(expr, ctx);
|
|
273
|
+
if (slotKey) return { $slot: slotKey };
|
|
274
|
+
return UNRESOLVED;
|
|
275
|
+
}
|
|
276
|
+
return evaluateExpression(expr, scope);
|
|
277
|
+
}
|
|
278
|
+
default: return UNRESOLVED;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function collectTextChildren(children) {
|
|
282
|
+
const textParts = [];
|
|
283
|
+
for (const child of children) if (child.type === "JSXText") {
|
|
284
|
+
const trimmed = child.value.replace(/\n\s*/g, " ").trim();
|
|
285
|
+
if (trimmed) textParts.push(trimmed);
|
|
286
|
+
} else if (child.type === "JSXExpressionContainer") {
|
|
287
|
+
const expr = child.expression;
|
|
288
|
+
if (expr.type === "StringLiteral") textParts.push(expr.value);
|
|
289
|
+
else return null;
|
|
290
|
+
} else if (child.type === "JSXElement" || child.type === "JSXFragment") return null;
|
|
291
|
+
return textParts.length > 0 ? textParts.join(" ") : null;
|
|
292
|
+
}
|
|
293
|
+
function hasElementChildren(children) {
|
|
294
|
+
return children.some((c) => c.type === "JSXElement" || c.type === "JSXFragment");
|
|
295
|
+
}
|
|
296
|
+
function nextKey(ctx) {
|
|
297
|
+
return `el${ctx.counter++}`;
|
|
298
|
+
}
|
|
299
|
+
/** Map React event prop names to json-render event names */
|
|
300
|
+
const EVENT_PROP_MAP = {
|
|
301
|
+
onClick: "press",
|
|
302
|
+
onPress: "press",
|
|
303
|
+
onSubmit: "submit"
|
|
304
|
+
};
|
|
305
|
+
/**
|
|
306
|
+
* Try to extract a json-render `on` binding from an event handler prop.
|
|
307
|
+
*
|
|
308
|
+
* Detects patterns like:
|
|
309
|
+
* onClick={() => setSetter(value)} → { action: "setState", params: { statePath, value } }
|
|
310
|
+
* onClick={() => setter(value)} → same (setter is a known state setter)
|
|
311
|
+
*
|
|
312
|
+
* Returns null if the prop is not a recognized event handler pattern.
|
|
313
|
+
*/
|
|
314
|
+
function tryExtractOnBinding(propName, valueNode, scope) {
|
|
315
|
+
const eventName = EVENT_PROP_MAP[propName];
|
|
316
|
+
if (!eventName || !valueNode) return null;
|
|
317
|
+
if (valueNode.type !== "JSXExpressionContainer") return null;
|
|
318
|
+
const expr = valueNode.expression;
|
|
319
|
+
if (expr.type !== "ArrowFunctionExpression") return null;
|
|
320
|
+
const arrow = expr;
|
|
321
|
+
let callExpr = null;
|
|
322
|
+
if (arrow.body.type === "CallExpression") callExpr = arrow.body;
|
|
323
|
+
else if (arrow.body.type === "BlockStatement") {
|
|
324
|
+
const stmts = arrow.body.body;
|
|
325
|
+
if (stmts.length === 1 && stmts[0].type === "ExpressionStatement") {
|
|
326
|
+
const innerExpr = stmts[0].expression;
|
|
327
|
+
if (innerExpr.type === "CallExpression") callExpr = innerExpr;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (!callExpr) return null;
|
|
331
|
+
const call = callExpr;
|
|
332
|
+
if (call.callee.type !== "Identifier") return null;
|
|
333
|
+
const calleeName = call.callee.name;
|
|
334
|
+
const stateVarName = scope.stateSetters.get(calleeName);
|
|
335
|
+
if (!stateVarName) return null;
|
|
336
|
+
const arg = call.arguments[0];
|
|
337
|
+
const value = arg ? evaluateExpression(arg, scope) : null;
|
|
338
|
+
if (value === UNRESOLVED) return null;
|
|
339
|
+
return {
|
|
340
|
+
event: eventName,
|
|
341
|
+
action: {
|
|
342
|
+
action: "setState",
|
|
343
|
+
params: {
|
|
344
|
+
statePath: `/${stateVarName}`,
|
|
345
|
+
value
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
function convertElement(node, ctx) {
|
|
351
|
+
const tagName = getTagName(node);
|
|
352
|
+
if (!tagName) return null;
|
|
353
|
+
if (ctx.scope.components.has(tagName) && !ctx.inlining.has(tagName)) {
|
|
354
|
+
const componentJsx = ctx.scope.components.get(tagName);
|
|
355
|
+
ctx.inlining.add(tagName);
|
|
356
|
+
const result = convertJsxNode(componentJsx, ctx);
|
|
357
|
+
ctx.inlining.delete(tagName);
|
|
358
|
+
return result;
|
|
359
|
+
}
|
|
360
|
+
if (ctx.knownComponents && tagName[0] === tagName[0].toUpperCase() && !ctx.knownComponents.has(tagName)) return flattenChildrenAsFragment(node.children, ctx);
|
|
361
|
+
const key = nextKey(ctx);
|
|
362
|
+
const props = {};
|
|
363
|
+
const onBindings = {};
|
|
364
|
+
for (const attr of node.openingElement.attributes) if (attr.type === "JSXAttribute") {
|
|
365
|
+
const jsxAttr = attr;
|
|
366
|
+
const propName = jsxAttr.name.name;
|
|
367
|
+
const binding = tryExtractOnBinding(propName, jsxAttr.value, ctx.scope);
|
|
368
|
+
if (binding) {
|
|
369
|
+
onBindings[binding.event] = binding.action;
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
props[propName] = getAttributeValue(jsxAttr.value, ctx.scope, ctx);
|
|
373
|
+
} else if (attr.type === "JSXSpreadAttribute") props.__spread__ = UNRESOLVED;
|
|
374
|
+
const childKeys = [];
|
|
375
|
+
const textContent = collectTextChildren(node.children);
|
|
376
|
+
if (textContent !== null && !hasElementChildren(node.children)) props.children = textContent;
|
|
377
|
+
else for (const child of node.children) {
|
|
378
|
+
const childKey = convertJsxNode(child, ctx);
|
|
379
|
+
if (childKey) childKeys.push(childKey);
|
|
380
|
+
}
|
|
381
|
+
const assetType = ctx.resolveAssetNode?.(tagName, props) ?? null;
|
|
382
|
+
if (assetType) delete props.name;
|
|
383
|
+
const element = {
|
|
384
|
+
type: assetType ?? tagName,
|
|
385
|
+
props
|
|
386
|
+
};
|
|
387
|
+
if (childKeys.length > 0) element.children = childKeys;
|
|
388
|
+
if (Object.keys(onBindings).length > 0) element.on = onBindings;
|
|
389
|
+
ctx.elements[key] = element;
|
|
390
|
+
return key;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Flatten a list of JSX children as if they were inside a fragment.
|
|
394
|
+
* Returns a single key if there's one child, a Fragment element for
|
|
395
|
+
* multiple, or null for none.
|
|
396
|
+
*/
|
|
397
|
+
function flattenChildrenAsFragment(children, ctx) {
|
|
398
|
+
const childKeys = [];
|
|
399
|
+
for (const child of children) {
|
|
400
|
+
const childKey = convertJsxNode(child, ctx);
|
|
401
|
+
if (childKey) childKeys.push(childKey);
|
|
402
|
+
}
|
|
403
|
+
if (childKeys.length === 1) return childKeys[0];
|
|
404
|
+
if (childKeys.length > 1) {
|
|
405
|
+
const fragKey = nextKey(ctx);
|
|
406
|
+
ctx.elements[fragKey] = {
|
|
407
|
+
type: "Fragment",
|
|
408
|
+
props: {},
|
|
409
|
+
children: childKeys
|
|
410
|
+
};
|
|
411
|
+
return fragKey;
|
|
412
|
+
}
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
function convertJsxNode(node, ctx) {
|
|
416
|
+
if (node.type === "JSXElement") return convertElement(node, ctx);
|
|
417
|
+
if (node.type === "JSXFragment") return flattenChildrenAsFragment(node.children, ctx);
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
function findJsxRoot(node) {
|
|
421
|
+
if (node.type === "JSXElement") return node;
|
|
422
|
+
if (node.type === "JSXFragment") return node;
|
|
423
|
+
if (node.type === "ParenthesizedExpression") return findJsxRoot(node.expression);
|
|
424
|
+
if (node.type === "ReturnStatement") {
|
|
425
|
+
const arg = node.argument;
|
|
426
|
+
return arg ? findJsxRoot(arg) : null;
|
|
427
|
+
}
|
|
428
|
+
if (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression") {
|
|
429
|
+
const body = node.body;
|
|
430
|
+
return extractJsxFromBody(body);
|
|
431
|
+
}
|
|
432
|
+
if (node.type === "ExpressionStatement") return findJsxRoot(node.expression);
|
|
433
|
+
if (node.type === "VariableDeclaration") {
|
|
434
|
+
const declarations = node.declarations;
|
|
435
|
+
for (const decl of declarations) if (decl.type === "VariableDeclarator") {
|
|
436
|
+
const init = decl.init;
|
|
437
|
+
if (init) {
|
|
438
|
+
const found = findJsxRoot(init);
|
|
439
|
+
if (found) return found;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
444
|
+
if (node.type === "FunctionDeclaration") {
|
|
445
|
+
const body = node.body;
|
|
446
|
+
if (body.type === "BlockStatement") {
|
|
447
|
+
const stmts = body.body;
|
|
448
|
+
for (const stmt of stmts) if (stmt.type === "ReturnStatement") return findJsxRoot(stmt);
|
|
449
|
+
}
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
if (node.type === "Program" || node.type === "File") {
|
|
453
|
+
const body = node.type === "File" ? node.program.body : node.body;
|
|
454
|
+
for (const stmt of body) {
|
|
455
|
+
const found = findJsxRoot(stmt);
|
|
456
|
+
if (found) return found;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Convert JSX source code into a json-render Spec.
|
|
463
|
+
*
|
|
464
|
+
* Accepts either a full JSX module or a JSX expression snippet.
|
|
465
|
+
* When `moduleSource` is provided, identifier references are resolved
|
|
466
|
+
* against the module's top-level const declarations, and local component
|
|
467
|
+
* functions are inlined.
|
|
468
|
+
*
|
|
469
|
+
* Returns `null` if no JSX element is found in the source.
|
|
470
|
+
*/
|
|
471
|
+
function jsxToSpec(source, options) {
|
|
472
|
+
const ast = parse(source, {
|
|
473
|
+
sourceType: "module",
|
|
474
|
+
plugins: ["jsx", "typescript"],
|
|
475
|
+
errorRecovery: true
|
|
476
|
+
});
|
|
477
|
+
const scope = buildModuleScope(options?.moduleSource ? parse(options.moduleSource, {
|
|
478
|
+
sourceType: "module",
|
|
479
|
+
plugins: ["jsx", "typescript"],
|
|
480
|
+
errorRecovery: true
|
|
481
|
+
}) : ast);
|
|
482
|
+
const jsxRoot = findJsxRoot(ast);
|
|
483
|
+
if (!jsxRoot) return null;
|
|
484
|
+
const ctx = {
|
|
485
|
+
elements: {},
|
|
486
|
+
counter: 0,
|
|
487
|
+
scope,
|
|
488
|
+
inlining: /* @__PURE__ */ new Set(),
|
|
489
|
+
knownComponents: options?.knownComponents ?? null,
|
|
490
|
+
resolveAssetNode: options?.resolveAssetNode ?? null
|
|
491
|
+
};
|
|
492
|
+
const rootKey = convertJsxNode(jsxRoot, ctx);
|
|
493
|
+
if (!rootKey) return null;
|
|
494
|
+
const spec = {
|
|
495
|
+
root: rootKey,
|
|
496
|
+
elements: ctx.elements
|
|
497
|
+
};
|
|
498
|
+
if (scope.stateBindings.size > 0) {
|
|
499
|
+
const state = {};
|
|
500
|
+
for (const [name, initialValue] of scope.stateBindings) state[name] = initialValue;
|
|
501
|
+
spec.state = state;
|
|
502
|
+
}
|
|
503
|
+
return spec;
|
|
504
|
+
}
|
|
505
|
+
//#endregion
|
|
506
|
+
export { UNRESOLVED, hasUnresolvedProps, jsxToSpec, stripUnresolvedProps };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { BaseStyles, CompoundPropsEntry } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/spec/layer-props.d.ts
|
|
4
|
+
type RenderedLayerProps = Record<string, Record<string, unknown>>;
|
|
5
|
+
/**
|
|
6
|
+
* Structural view of the component fields style resolution reads. Satisfied by
|
|
7
|
+
* a real `@yahoo/uds-create-config` `Component` (Studio, playground) AND by codegen's
|
|
8
|
+
* capture-time `ProbeComponent` (`extractPreviewSpecs.buildRenderSpec`), so the
|
|
9
|
+
* same resolver bakes styles into the render spec at build time and resolves
|
|
10
|
+
* derived (non-render) values like the layers-panel `display` at render time.
|
|
11
|
+
*/
|
|
12
|
+
interface ResolvableComponent {
|
|
13
|
+
layers: ReadonlyMap<string, unknown>;
|
|
14
|
+
base?: BaseStyles;
|
|
15
|
+
props?: Record<string, unknown>;
|
|
16
|
+
compoundProps?: readonly CompoundPropsEntry[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the styles a layer effectively has for a set of prop-axis values and
|
|
20
|
+
* (optionally) one forced interaction state, walking the authoring surfaces
|
|
21
|
+
* codegen reads:
|
|
22
|
+
*
|
|
23
|
+
* 1. `component.base[layer]` — the always-on baseline.
|
|
24
|
+
* 2. `component.props[axis].values[value][layer]` — per axis key in `propValues`.
|
|
25
|
+
* 3. `component.compoundProps[].layers[layer]` — for entries whose `when`
|
|
26
|
+
* is a subset of `propValues` (multi-axis overrides).
|
|
27
|
+
* 4. State overlay — when `state` names a modifier sub-object (`_hover`) on
|
|
28
|
+
* the merged surface, its props overlay the base so a static preview
|
|
29
|
+
* shows that state.
|
|
30
|
+
*
|
|
31
|
+
* `propValues` are plain axis names → values (no reserved keys). `state` is a
|
|
32
|
+
* single forced interaction modifier (`'_hover'`); pass it directly rather than
|
|
33
|
+
* smuggling it into `propValues`. **Modes are intentionally not a parameter** —
|
|
34
|
+
* mode visuals ride a scoped wrapper class (`.dark &`) and flip live, so they
|
|
35
|
+
* are never folded into the static base here.
|
|
36
|
+
*/
|
|
37
|
+
declare function resolveAuthoredLayerProps(component: ResolvableComponent | undefined | null, layer: string, propValues: Record<string, unknown>, state?: string | null): Record<string, unknown>;
|
|
38
|
+
/**
|
|
39
|
+
* Resolve per-layer style bags for a set of prop values, mirroring the config's
|
|
40
|
+
* variant resolution. `buildRenderSpec` calls this once per variant combo to
|
|
41
|
+
* bake the bags onto the render spec.
|
|
42
|
+
*
|
|
43
|
+
* `state` is left `null` by the bake path — modifier blocks ride through as
|
|
44
|
+
* nested style props and forced state is applied per cell (the render-spec
|
|
45
|
+
* consumers fold the active modifier into props), so folding it into the base
|
|
46
|
+
* here would be redundant. (Derived callers like the layers panel also pass no
|
|
47
|
+
* state.) `cssVarPrefix` resolves color expressions to a `var(...)`-referencing
|
|
48
|
+
* arbitrary value.
|
|
49
|
+
*/
|
|
50
|
+
declare function resolveRenderedLayerProps(component: ResolvableComponent | undefined, propValues: Record<string, unknown>, state?: string | null, cssVarPrefix?: string): RenderedLayerProps;
|
|
51
|
+
//#endregion
|
|
52
|
+
export { ResolvableComponent, resolveAuthoredLayerProps, resolveRenderedLayerProps };
|