@yahoo/uds 3.171.0 → 3.172.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/automated-config/dist/utils/getConfigVariantProperties.d.cts +2 -2
- package/dist/automated-config/dist/utils/getConfigVariantProperties.d.ts +2 -2
- package/dist/css/dist/css/purgeWorker.js +50 -0
- package/dist/css/dist/css/runner.cjs +3 -1
- package/dist/css/dist/css/runner.js +3 -1
- package/dist/css/dist/css/workerPool.cjs +18 -2
- package/dist/css/dist/css/workerPool.js +16 -1
- package/dist/css/dist/purger/optimized/ast/expressions.cjs +278 -278
- package/dist/css/dist/purger/optimized/ast/expressions.js +278 -278
- package/dist/css/dist/purger/optimized/ast/jsx.cjs +67 -36
- package/dist/css/dist/purger/optimized/ast/jsx.js +67 -35
- package/dist/css/dist/purger/optimized/ast/oxc.cjs +265 -0
- package/dist/css/dist/purger/optimized/ast/oxc.js +253 -0
- package/dist/css/dist/purger/optimized/ast/props.cjs +21 -15
- package/dist/css/dist/purger/optimized/ast/props.js +21 -14
- package/dist/css/dist/purger/optimized/ast/spread.cjs +19 -19
- package/dist/css/dist/purger/optimized/ast/spread.js +19 -18
- package/dist/css/dist/purger/optimized/purgeFromCode.cjs +86 -101
- package/dist/css/dist/purger/optimized/purgeFromCode.js +86 -100
- package/dist/tailwind-internal/dist/utils/getShadowStyles.d.cts +2 -2
- package/dist/tailwind-internal/dist/utils/getShadowStyles.d.ts +2 -2
- package/dist/tailwind-v3/dist/purger/legacy/purgeCSS.cjs +1 -1
- package/dist/tailwind-v3/dist/purger/legacy/purgeCSS.js +1 -1
- package/dist/uds/generated/componentData.cjs +273 -273
- package/dist/uds/generated/componentData.js +273 -273
- package/dist/uds/package.cjs +2 -0
- package/dist/uds/package.js +2 -0
- package/generated/componentData.json +419 -419
- package/package.json +3 -1
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
|
|
2
|
+
import { parseSync, rawTransferSupported, visitorKeys } from "oxc-parser";
|
|
3
|
+
//#region ../css/dist/purger/optimized/ast/oxc.mjs
|
|
4
|
+
/*! © 2026 Yahoo, Inc. UDS CSS v0.0.0-development */
|
|
5
|
+
const RAW_TRANSFER_AVAILABLE = rawTransferSupported();
|
|
6
|
+
const getLangForFile = (filePath) => {
|
|
7
|
+
const lowered = filePath.toLowerCase();
|
|
8
|
+
if (lowered.endsWith(".tsx")) return "tsx";
|
|
9
|
+
if (lowered.endsWith(".ts") || lowered.endsWith(".mts") || lowered.endsWith(".cts")) return "ts";
|
|
10
|
+
return "jsx";
|
|
11
|
+
};
|
|
12
|
+
const isNodeLike = (value) => typeof value === "object" && value !== null && typeof value.type === "string";
|
|
13
|
+
/**
|
|
14
|
+
* Visit `node` and all descendants in document order (self included).
|
|
15
|
+
*/
|
|
16
|
+
const visitNodeAndDescendants = (node, parent, visit) => {
|
|
17
|
+
visit(node, parent);
|
|
18
|
+
const keys = visitorKeys[node.type] ?? [];
|
|
19
|
+
for (const key of keys) {
|
|
20
|
+
const value = node[key];
|
|
21
|
+
if (Array.isArray(value)) {
|
|
22
|
+
for (const child of value) if (isNodeLike(child)) visitNodeAndDescendants(child, node, visit);
|
|
23
|
+
} else if (isNodeLike(value)) visitNodeAndDescendants(value, node, visit);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Visit all descendants of `node` (self excluded), mirroring
|
|
28
|
+
* ts-morph `getDescendants()` / `getDescendantsOfKind()` scoped scans.
|
|
29
|
+
*/
|
|
30
|
+
const forEachDescendant = (node, visit) => {
|
|
31
|
+
visitNodeAndDescendants(node, null, (descendant) => {
|
|
32
|
+
if (descendant !== node) visit(descendant);
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
const getPatternIdentifier = (pattern) => {
|
|
36
|
+
if (pattern.type === "Identifier") return pattern;
|
|
37
|
+
if (pattern.type === "AssignmentPattern" && pattern.left.type === "Identifier") return pattern.left;
|
|
38
|
+
if (pattern.type === "RestElement" && pattern.argument.type === "Identifier") return pattern.argument;
|
|
39
|
+
return null;
|
|
40
|
+
};
|
|
41
|
+
const getPatternDefault = (pattern) => pattern.type === "AssignmentPattern" ? pattern.right : null;
|
|
42
|
+
const getPatternTypeNode = (pattern) => {
|
|
43
|
+
return (pattern.type === "AssignmentPattern" ? pattern.left : pattern).typeAnnotation?.typeAnnotation ?? null;
|
|
44
|
+
};
|
|
45
|
+
const addDefinition = (source, definition) => {
|
|
46
|
+
const existing = source.definitions.get(definition.name);
|
|
47
|
+
if (existing) existing.push(definition);
|
|
48
|
+
else source.definitions.set(definition.name, [definition]);
|
|
49
|
+
};
|
|
50
|
+
const collectParameterDefinitions = (source, fn) => {
|
|
51
|
+
fn.params.forEach((param, index) => {
|
|
52
|
+
const identifier = getPatternIdentifier(param);
|
|
53
|
+
if (!identifier) return;
|
|
54
|
+
addDefinition(source, {
|
|
55
|
+
kind: "parameter",
|
|
56
|
+
name: identifier.name,
|
|
57
|
+
start: param.start,
|
|
58
|
+
node: param,
|
|
59
|
+
init: getPatternDefault(param),
|
|
60
|
+
typeNode: getPatternTypeNode(param),
|
|
61
|
+
paramFunction: fn,
|
|
62
|
+
paramIndex: index
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
const collectBindingDefinition = (source, element, pattern) => {
|
|
67
|
+
if (element.type === "Property") {
|
|
68
|
+
const identifier = getPatternIdentifier(element.value);
|
|
69
|
+
if (!identifier) return;
|
|
70
|
+
const key = element.key;
|
|
71
|
+
const bindingPropertyName = key.type === "Identifier" ? key.name : source.code.slice(key.start, key.end);
|
|
72
|
+
addDefinition(source, {
|
|
73
|
+
kind: "binding",
|
|
74
|
+
name: identifier.name,
|
|
75
|
+
start: element.start,
|
|
76
|
+
node: element,
|
|
77
|
+
init: getPatternDefault(element.value),
|
|
78
|
+
typeNode: null,
|
|
79
|
+
bindingPropertyName,
|
|
80
|
+
bindingPattern: pattern
|
|
81
|
+
});
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const identifier = getPatternIdentifier(element);
|
|
85
|
+
if (!identifier) return;
|
|
86
|
+
addDefinition(source, {
|
|
87
|
+
kind: "binding",
|
|
88
|
+
name: identifier.name,
|
|
89
|
+
start: element.start,
|
|
90
|
+
node: element,
|
|
91
|
+
init: getPatternDefault(element),
|
|
92
|
+
typeNode: null,
|
|
93
|
+
bindingPropertyName: identifier.name,
|
|
94
|
+
bindingPattern: pattern
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
const isTopLevel = (parent, source) => {
|
|
98
|
+
if (!parent) return false;
|
|
99
|
+
if (parent.type === "Program") return true;
|
|
100
|
+
if (parent.type === "ExportNamedDeclaration" || parent.type === "ExportDefaultDeclaration") return source.parents.get(parent)?.type === "Program";
|
|
101
|
+
return false;
|
|
102
|
+
};
|
|
103
|
+
const indexNode = (source, node, parent) => {
|
|
104
|
+
if (parent) source.parents.set(node, parent);
|
|
105
|
+
switch (node.type) {
|
|
106
|
+
case "JSXOpeningElement":
|
|
107
|
+
source.jsxElements.push(node);
|
|
108
|
+
break;
|
|
109
|
+
case "CallExpression":
|
|
110
|
+
source.callExpressions.push(node);
|
|
111
|
+
break;
|
|
112
|
+
case "JSXAttribute":
|
|
113
|
+
source.jsxAttributes.push(node);
|
|
114
|
+
break;
|
|
115
|
+
case "Property":
|
|
116
|
+
if (parent?.type === "ObjectExpression") source.objectProperties.push(node);
|
|
117
|
+
else if (parent?.type === "ObjectPattern") collectBindingDefinition(source, node, parent);
|
|
118
|
+
break;
|
|
119
|
+
case "RestElement":
|
|
120
|
+
if (parent?.type === "ObjectPattern" || parent?.type === "ArrayPattern") collectBindingDefinition(source, node, parent);
|
|
121
|
+
break;
|
|
122
|
+
case "Identifier":
|
|
123
|
+
case "AssignmentPattern":
|
|
124
|
+
if (parent?.type === "ArrayPattern") collectBindingDefinition(source, node, parent);
|
|
125
|
+
break;
|
|
126
|
+
case "VariableDeclarator":
|
|
127
|
+
source.variableDeclarators.push(node);
|
|
128
|
+
if (node.id.type === "Identifier") {
|
|
129
|
+
const name = node.id.name;
|
|
130
|
+
addDefinition(source, {
|
|
131
|
+
kind: "variable",
|
|
132
|
+
name,
|
|
133
|
+
start: node.start,
|
|
134
|
+
node,
|
|
135
|
+
init: node.init ?? null,
|
|
136
|
+
typeNode: getPatternTypeNode(node.id)
|
|
137
|
+
});
|
|
138
|
+
const declaration = source.parents.get(node);
|
|
139
|
+
if (declaration && isTopLevel(source.parents.get(declaration) ?? null, source)) {
|
|
140
|
+
if (!source.topLevelVariables.has(name)) source.topLevelVariables.set(name, node);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
case "FunctionDeclaration": {
|
|
145
|
+
const fn = node;
|
|
146
|
+
collectParameterDefinitions(source, fn);
|
|
147
|
+
if (fn.id && isTopLevel(parent, source)) {
|
|
148
|
+
const name = fn.id.name;
|
|
149
|
+
addDefinition(source, {
|
|
150
|
+
kind: "function",
|
|
151
|
+
name,
|
|
152
|
+
start: parent && parent.type !== "Program" && "start" in parent ? parent.start : fn.start,
|
|
153
|
+
node: fn,
|
|
154
|
+
init: null,
|
|
155
|
+
typeNode: null
|
|
156
|
+
});
|
|
157
|
+
if (!source.topLevelFunctions.has(name)) source.topLevelFunctions.set(name, fn);
|
|
158
|
+
}
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
case "FunctionExpression":
|
|
162
|
+
case "ArrowFunctionExpression":
|
|
163
|
+
collectParameterDefinitions(source, node);
|
|
164
|
+
break;
|
|
165
|
+
case "TSEnumDeclaration":
|
|
166
|
+
if (isTopLevel(parent, source) && !source.topLevelEnums.has(node.id.name)) source.topLevelEnums.set(node.id.name, node);
|
|
167
|
+
break;
|
|
168
|
+
default: break;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
/**
|
|
172
|
+
* Parse a source file and build the purger's node indexes in a single walk.
|
|
173
|
+
*/
|
|
174
|
+
const parseSource = (filePath, code) => {
|
|
175
|
+
const parserOptions = { lang: getLangForFile(filePath) };
|
|
176
|
+
if (RAW_TRANSFER_AVAILABLE) parserOptions.experimentalRawTransfer = true;
|
|
177
|
+
const result = parseSync(filePath, code, parserOptions);
|
|
178
|
+
const source = {
|
|
179
|
+
filePath,
|
|
180
|
+
code,
|
|
181
|
+
program: result.program,
|
|
182
|
+
staticImports: result.module.staticImports,
|
|
183
|
+
parents: /* @__PURE__ */ new WeakMap(),
|
|
184
|
+
jsxElements: [],
|
|
185
|
+
callExpressions: [],
|
|
186
|
+
jsxAttributes: [],
|
|
187
|
+
objectProperties: [],
|
|
188
|
+
variableDeclarators: [],
|
|
189
|
+
topLevelVariables: /* @__PURE__ */ new Map(),
|
|
190
|
+
topLevelFunctions: /* @__PURE__ */ new Map(),
|
|
191
|
+
topLevelEnums: /* @__PURE__ */ new Map(),
|
|
192
|
+
definitions: /* @__PURE__ */ new Map(),
|
|
193
|
+
lineStarts: null
|
|
194
|
+
};
|
|
195
|
+
visitNodeAndDescendants(source.program, null, (node, parent) => indexNode(source, node, parent));
|
|
196
|
+
return source;
|
|
197
|
+
};
|
|
198
|
+
/** ts-morph `node.getText()` analog */
|
|
199
|
+
const getText = (source, node) => source.code.slice(node.start, node.end);
|
|
200
|
+
const getParent = (source, node) => source.parents.get(node);
|
|
201
|
+
/** ts-morph `node.getStartLineNumber()` analog (1-based) */
|
|
202
|
+
const getLineNumber = (source, offset) => {
|
|
203
|
+
if (!source.lineStarts) {
|
|
204
|
+
const lineStarts = [0];
|
|
205
|
+
for (let i = 0; i < source.code.length; i++) if (source.code[i] === "\n") lineStarts.push(i + 1);
|
|
206
|
+
source.lineStarts = lineStarts;
|
|
207
|
+
}
|
|
208
|
+
const lineStarts = source.lineStarts;
|
|
209
|
+
let low = 0;
|
|
210
|
+
let high = lineStarts.length - 1;
|
|
211
|
+
while (low < high) {
|
|
212
|
+
const mid = Math.ceil((low + high) / 2);
|
|
213
|
+
if (lineStarts[mid] <= offset) low = mid;
|
|
214
|
+
else high = mid - 1;
|
|
215
|
+
}
|
|
216
|
+
return low + 1;
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* Position-aware local definition lookup, mirroring ts-morph's
|
|
220
|
+
* `getLocalDefinitionNodes`: latest definition at or before the identifier.
|
|
221
|
+
*/
|
|
222
|
+
const findLocalDefinitions = (source, identifier) => {
|
|
223
|
+
if (identifier.type !== "Identifier") return [];
|
|
224
|
+
const candidates = source.definitions.get(identifier.name);
|
|
225
|
+
if (!candidates) return [];
|
|
226
|
+
const matches = candidates.filter((definition) => definition.start <= identifier.start);
|
|
227
|
+
if (matches.length === 0) return [];
|
|
228
|
+
return [...matches].sort((left, right) => right.start - left.start).slice(0, 1);
|
|
229
|
+
};
|
|
230
|
+
/** True for a string `Literal` node */
|
|
231
|
+
const isStringLiteral = (node) => node.type === "Literal" && typeof node.value === "string";
|
|
232
|
+
const getStringLiteralValue = (node) => node.value;
|
|
233
|
+
/** True for a `TemplateLiteral` with no substitutions (ts NoSubstitutionTemplateLiteral) */
|
|
234
|
+
const isNoSubstitutionTemplate = (node) => node.type === "TemplateLiteral" && node.expressions.length === 0;
|
|
235
|
+
/** ts-morph `element.getTagNameNode().getText()` analog */
|
|
236
|
+
const getJsxTagName = (source, element) => {
|
|
237
|
+
const name = element.name;
|
|
238
|
+
return name.type === "JSXIdentifier" ? name.name : source.code.slice(name.start, name.end);
|
|
239
|
+
};
|
|
240
|
+
/** ts-morph `attr.getNameNode().getText()` analog */
|
|
241
|
+
const getJsxAttributeName = (source, attribute) => {
|
|
242
|
+
const name = attribute.name;
|
|
243
|
+
return name.type === "JSXIdentifier" ? name.name : source.code.slice(name.start, name.end);
|
|
244
|
+
};
|
|
245
|
+
/** ts-morph `prop.getName()` analog for object-literal properties */
|
|
246
|
+
const getPropertyName = (source, property) => {
|
|
247
|
+
const key = property.key;
|
|
248
|
+
if (key.type === "Identifier") return key.name;
|
|
249
|
+
if (isStringLiteral(key)) return getStringLiteralValue(key);
|
|
250
|
+
return source.code.slice(key.start, key.end);
|
|
251
|
+
};
|
|
252
|
+
//#endregion
|
|
253
|
+
export { findLocalDefinitions, forEachDescendant, getJsxAttributeName, getJsxTagName, getLineNumber, getParent, getPropertyName, getStringLiteralValue, getText, isNoSubstitutionTemplate, isStringLiteral, parseSource };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
|
|
2
|
-
require("
|
|
2
|
+
const require_oxc = require("./oxc.cjs");
|
|
3
3
|
const require_expressions = require("./expressions.cjs");
|
|
4
|
-
let ts_morph = require("ts-morph");
|
|
5
4
|
//#region ../css/dist/purger/optimized/ast/props.mjs
|
|
6
5
|
/*! © 2026 Yahoo, Inc. UDS CSS v0.0.0-development */
|
|
7
6
|
/**
|
|
@@ -11,27 +10,34 @@ let ts_morph = require("ts-morph");
|
|
|
11
10
|
* For spread props like `<Box {...props} />`, marks them as fromSpread for later resolution.
|
|
12
11
|
*
|
|
13
12
|
* @param element The JSX element to extract props from
|
|
13
|
+
* @param source The parsed source containing the element
|
|
14
14
|
* @returns Array of extracted props with their values
|
|
15
15
|
*/
|
|
16
|
-
const extractPropsFromElement = (element) => element.
|
|
17
|
-
if (
|
|
18
|
-
const propName =
|
|
19
|
-
const stringLiterals =
|
|
16
|
+
const extractPropsFromElement = (element, source) => element.attributes.flatMap((attr) => {
|
|
17
|
+
if (attr.type === "JSXAttribute") {
|
|
18
|
+
const propName = require_oxc.getJsxAttributeName(source, attr);
|
|
19
|
+
const stringLiterals = [];
|
|
20
|
+
if (attr.value) {
|
|
21
|
+
if (require_oxc.isStringLiteral(attr.value)) stringLiterals.push(attr.value);
|
|
22
|
+
require_oxc.forEachDescendant(attr.value, (descendant) => {
|
|
23
|
+
if (require_oxc.isStringLiteral(descendant)) stringLiterals.push(descendant);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
20
26
|
return [{
|
|
21
27
|
name: propName,
|
|
22
|
-
values: stringLiterals.length > 0 ? stringLiterals.map((lit) =>
|
|
23
|
-
const initializer = attr.
|
|
24
|
-
if (!initializer ||
|
|
25
|
-
const expression = initializer.
|
|
26
|
-
return expression ? require_expressions.extractStringLiterals(expression) : [];
|
|
28
|
+
values: stringLiterals.length > 0 ? stringLiterals.map((lit) => require_oxc.getStringLiteralValue(lit)) : (() => {
|
|
29
|
+
const initializer = attr.value;
|
|
30
|
+
if (!initializer || initializer.type !== "JSXExpressionContainer") return [];
|
|
31
|
+
const expression = initializer.expression;
|
|
32
|
+
return expression && expression.type !== "JSXEmptyExpression" ? require_expressions.extractStringLiterals(expression, source) : [];
|
|
27
33
|
})(),
|
|
28
34
|
fromSpread: false
|
|
29
35
|
}];
|
|
30
36
|
}
|
|
31
|
-
if (
|
|
32
|
-
const expression = attr.
|
|
37
|
+
if (attr.type === "JSXSpreadAttribute") {
|
|
38
|
+
const expression = attr.argument;
|
|
33
39
|
return [{
|
|
34
|
-
name: `__spread__:${
|
|
40
|
+
name: `__spread__:${expression.type === "Identifier" ? expression.name : expression.type === "MemberExpression" && !expression.computed && expression.property.type === "Identifier" ? expression.property.name : require_oxc.getText(source, expression).split(".").pop() ?? ""}`,
|
|
35
41
|
values: [],
|
|
36
42
|
fromSpread: true
|
|
37
43
|
}];
|
|
@@ -42,6 +48,6 @@ const extractPropsFromElement = (element) => element.getAttributes().flatMap((at
|
|
|
42
48
|
* Extract only explicit (non-spread) props from a JSX element.
|
|
43
49
|
* Used when we want to avoid including spread-derived values.
|
|
44
50
|
*/
|
|
45
|
-
const extractExplicitProps = (element) => extractPropsFromElement(element).filter((p) => !p.fromSpread);
|
|
51
|
+
const extractExplicitProps = (element, source) => extractPropsFromElement(element, source).filter((p) => !p.fromSpread);
|
|
46
52
|
//#endregion
|
|
47
53
|
exports.extractExplicitProps = extractExplicitProps;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
|
|
2
|
+
import { forEachDescendant, getJsxAttributeName, getStringLiteralValue, getText, isStringLiteral } from "./oxc.js";
|
|
2
3
|
import { extractStringLiterals } from "./expressions.js";
|
|
3
|
-
import { Node, SyntaxKind } from "ts-morph";
|
|
4
4
|
//#region ../css/dist/purger/optimized/ast/props.mjs
|
|
5
5
|
/*! © 2026 Yahoo, Inc. UDS CSS v0.0.0-development */
|
|
6
6
|
/**
|
|
@@ -10,27 +10,34 @@ import { Node, SyntaxKind } from "ts-morph";
|
|
|
10
10
|
* For spread props like `<Box {...props} />`, marks them as fromSpread for later resolution.
|
|
11
11
|
*
|
|
12
12
|
* @param element The JSX element to extract props from
|
|
13
|
+
* @param source The parsed source containing the element
|
|
13
14
|
* @returns Array of extracted props with their values
|
|
14
15
|
*/
|
|
15
|
-
const extractPropsFromElement = (element) => element.
|
|
16
|
-
if (
|
|
17
|
-
const propName = attr
|
|
18
|
-
const stringLiterals =
|
|
16
|
+
const extractPropsFromElement = (element, source) => element.attributes.flatMap((attr) => {
|
|
17
|
+
if (attr.type === "JSXAttribute") {
|
|
18
|
+
const propName = getJsxAttributeName(source, attr);
|
|
19
|
+
const stringLiterals = [];
|
|
20
|
+
if (attr.value) {
|
|
21
|
+
if (isStringLiteral(attr.value)) stringLiterals.push(attr.value);
|
|
22
|
+
forEachDescendant(attr.value, (descendant) => {
|
|
23
|
+
if (isStringLiteral(descendant)) stringLiterals.push(descendant);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
19
26
|
return [{
|
|
20
27
|
name: propName,
|
|
21
|
-
values: stringLiterals.length > 0 ? stringLiterals.map((lit) => lit
|
|
22
|
-
const initializer = attr.
|
|
23
|
-
if (!initializer ||
|
|
24
|
-
const expression = initializer.
|
|
25
|
-
return expression ? extractStringLiterals(expression) : [];
|
|
28
|
+
values: stringLiterals.length > 0 ? stringLiterals.map((lit) => getStringLiteralValue(lit)) : (() => {
|
|
29
|
+
const initializer = attr.value;
|
|
30
|
+
if (!initializer || initializer.type !== "JSXExpressionContainer") return [];
|
|
31
|
+
const expression = initializer.expression;
|
|
32
|
+
return expression && expression.type !== "JSXEmptyExpression" ? extractStringLiterals(expression, source) : [];
|
|
26
33
|
})(),
|
|
27
34
|
fromSpread: false
|
|
28
35
|
}];
|
|
29
36
|
}
|
|
30
|
-
if (
|
|
31
|
-
const expression = attr.
|
|
37
|
+
if (attr.type === "JSXSpreadAttribute") {
|
|
38
|
+
const expression = attr.argument;
|
|
32
39
|
return [{
|
|
33
|
-
name: `__spread__:${
|
|
40
|
+
name: `__spread__:${expression.type === "Identifier" ? expression.name : expression.type === "MemberExpression" && !expression.computed && expression.property.type === "Identifier" ? expression.property.name : getText(source, expression).split(".").pop() ?? ""}`,
|
|
34
41
|
values: [],
|
|
35
42
|
fromSpread: true
|
|
36
43
|
}];
|
|
@@ -41,6 +48,6 @@ const extractPropsFromElement = (element) => element.getAttributes().flatMap((at
|
|
|
41
48
|
* Extract only explicit (non-spread) props from a JSX element.
|
|
42
49
|
* Used when we want to avoid including spread-derived values.
|
|
43
50
|
*/
|
|
44
|
-
const extractExplicitProps = (element) => extractPropsFromElement(element).filter((p) => !p.fromSpread);
|
|
51
|
+
const extractExplicitProps = (element, source) => extractPropsFromElement(element, source).filter((p) => !p.fromSpread);
|
|
45
52
|
//#endregion
|
|
46
53
|
export { extractExplicitProps };
|
|
@@ -1,32 +1,31 @@
|
|
|
1
1
|
/*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
|
|
2
|
-
require("
|
|
2
|
+
const require_oxc = require("./oxc.cjs");
|
|
3
3
|
const require_jsx = require("./jsx.cjs");
|
|
4
4
|
const require_props = require("./props.cjs");
|
|
5
|
-
let ts_morph = require("ts-morph");
|
|
6
5
|
//#region ../css/dist/purger/optimized/ast/spread.mjs
|
|
7
6
|
/*! © 2026 Yahoo, Inc. UDS CSS v0.0.0-development */
|
|
8
7
|
/**
|
|
9
|
-
* Find all usages of a component across the
|
|
8
|
+
* Find all usages of a component across the analyzed sources.
|
|
10
9
|
* This is the key function for spread resolution - when we encounter
|
|
11
10
|
* {...props} on a UDS component inside a wrapper, we trace back to
|
|
12
11
|
* find all places where the wrapper is used and extract actual values.
|
|
13
12
|
*
|
|
14
13
|
* @param componentName Name of the component to find usages of
|
|
15
|
-
* @param
|
|
14
|
+
* @param sources The parsed sources to search
|
|
16
15
|
* @param cache Optional cache to avoid re-scanning
|
|
17
16
|
* @returns Array of component usages with their props
|
|
18
17
|
*/
|
|
19
|
-
const findComponentUsages = (componentName,
|
|
18
|
+
const findComponentUsages = (componentName, sources, cache) => {
|
|
20
19
|
if (cache?.has(componentName)) return cache.get(componentName);
|
|
21
20
|
const usages = [];
|
|
22
|
-
|
|
23
|
-
if (!fileReferencesComponent(
|
|
24
|
-
|
|
25
|
-
const props = require_props.extractExplicitProps(element);
|
|
21
|
+
sources.forEach((source) => {
|
|
22
|
+
if (!fileReferencesComponent(source, componentName)) return;
|
|
23
|
+
source.jsxElements.filter((el) => require_oxc.getJsxTagName(source, el) === componentName).forEach((element) => {
|
|
24
|
+
const props = require_props.extractExplicitProps(element, source);
|
|
26
25
|
usages.push({
|
|
27
26
|
componentName,
|
|
28
|
-
filePath:
|
|
29
|
-
line: element.
|
|
27
|
+
filePath: source.filePath,
|
|
28
|
+
line: require_oxc.getLineNumber(source, element.start),
|
|
30
29
|
props
|
|
31
30
|
});
|
|
32
31
|
});
|
|
@@ -37,10 +36,10 @@ const findComponentUsages = (componentName, project, cache) => {
|
|
|
37
36
|
/**
|
|
38
37
|
* Check if a source file references a component (by import or definition)
|
|
39
38
|
*/
|
|
40
|
-
const fileReferencesComponent = (
|
|
41
|
-
if (
|
|
42
|
-
if (
|
|
43
|
-
if (
|
|
39
|
+
const fileReferencesComponent = (source, componentName) => {
|
|
40
|
+
if (source.staticImports.some((staticImport) => staticImport.entries.some((entry) => entry.importName.kind === "Name" && entry.importName.name === componentName))) return true;
|
|
41
|
+
if (source.topLevelVariables.has(componentName)) return true;
|
|
42
|
+
if (source.topLevelFunctions.has(componentName)) return true;
|
|
44
43
|
return false;
|
|
45
44
|
};
|
|
46
45
|
/**
|
|
@@ -54,14 +53,15 @@ const fileReferencesComponent = (sourceFile, componentName) => {
|
|
|
54
53
|
*
|
|
55
54
|
* @param element The JSX element with the spread
|
|
56
55
|
* @param spreadIdentifier The identifier being spread (e.g., 'rest' from {...rest})
|
|
56
|
+
* @param elementSource The parsed source containing the element
|
|
57
57
|
* @param context The purge context for caching
|
|
58
58
|
* @returns Map of prop names to their resolved values, or undefined if couldn't trace
|
|
59
59
|
*/
|
|
60
|
-
const resolveSpreadFromUsages = (element, spreadIdentifier, context) => {
|
|
61
|
-
const parentInfo = require_jsx.getParentComponentInfo(element);
|
|
60
|
+
const resolveSpreadFromUsages = (element, spreadIdentifier, elementSource, context) => {
|
|
61
|
+
const parentInfo = require_jsx.getParentComponentInfo(element, elementSource);
|
|
62
62
|
if (!parentInfo) return;
|
|
63
63
|
if (!(parentInfo.spreadRestIdentifier === spreadIdentifier || parentInfo.paramIdentifier === spreadIdentifier)) return;
|
|
64
|
-
const usages = findComponentUsages(parentInfo.componentName, context.
|
|
64
|
+
const usages = findComponentUsages(parentInfo.componentName, context.sources, context.componentUsageCache);
|
|
65
65
|
if (usages.length === 0) return;
|
|
66
66
|
context.stats.spreadsTraced++;
|
|
67
67
|
const resolvedProps = /* @__PURE__ */ new Map();
|
|
@@ -75,7 +75,7 @@ const resolveSpreadFromUsages = (element, spreadIdentifier, context) => {
|
|
|
75
75
|
resolvedProps.set(prop.name, existing);
|
|
76
76
|
});
|
|
77
77
|
});
|
|
78
|
-
const cacheKey = `${
|
|
78
|
+
const cacheKey = `${elementSource.filePath}:${require_oxc.getLineNumber(elementSource, element.start)}:${spreadIdentifier}`;
|
|
79
79
|
context.spreadCache.set(cacheKey, Array.from(resolvedProps).map(([name, values]) => ({
|
|
80
80
|
name,
|
|
81
81
|
values,
|
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
/*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
|
|
2
|
+
import { getJsxTagName, getLineNumber } from "./oxc.js";
|
|
2
3
|
import { getParentComponentInfo } from "./jsx.js";
|
|
3
4
|
import { extractExplicitProps } from "./props.js";
|
|
4
|
-
import { SyntaxKind } from "ts-morph";
|
|
5
5
|
//#region ../css/dist/purger/optimized/ast/spread.mjs
|
|
6
6
|
/*! © 2026 Yahoo, Inc. UDS CSS v0.0.0-development */
|
|
7
7
|
/**
|
|
8
|
-
* Find all usages of a component across the
|
|
8
|
+
* Find all usages of a component across the analyzed sources.
|
|
9
9
|
* This is the key function for spread resolution - when we encounter
|
|
10
10
|
* {...props} on a UDS component inside a wrapper, we trace back to
|
|
11
11
|
* find all places where the wrapper is used and extract actual values.
|
|
12
12
|
*
|
|
13
13
|
* @param componentName Name of the component to find usages of
|
|
14
|
-
* @param
|
|
14
|
+
* @param sources The parsed sources to search
|
|
15
15
|
* @param cache Optional cache to avoid re-scanning
|
|
16
16
|
* @returns Array of component usages with their props
|
|
17
17
|
*/
|
|
18
|
-
const findComponentUsages = (componentName,
|
|
18
|
+
const findComponentUsages = (componentName, sources, cache) => {
|
|
19
19
|
if (cache?.has(componentName)) return cache.get(componentName);
|
|
20
20
|
const usages = [];
|
|
21
|
-
|
|
22
|
-
if (!fileReferencesComponent(
|
|
23
|
-
|
|
24
|
-
const props = extractExplicitProps(element);
|
|
21
|
+
sources.forEach((source) => {
|
|
22
|
+
if (!fileReferencesComponent(source, componentName)) return;
|
|
23
|
+
source.jsxElements.filter((el) => getJsxTagName(source, el) === componentName).forEach((element) => {
|
|
24
|
+
const props = extractExplicitProps(element, source);
|
|
25
25
|
usages.push({
|
|
26
26
|
componentName,
|
|
27
|
-
filePath:
|
|
28
|
-
line: element.
|
|
27
|
+
filePath: source.filePath,
|
|
28
|
+
line: getLineNumber(source, element.start),
|
|
29
29
|
props
|
|
30
30
|
});
|
|
31
31
|
});
|
|
@@ -36,10 +36,10 @@ const findComponentUsages = (componentName, project, cache) => {
|
|
|
36
36
|
/**
|
|
37
37
|
* Check if a source file references a component (by import or definition)
|
|
38
38
|
*/
|
|
39
|
-
const fileReferencesComponent = (
|
|
40
|
-
if (
|
|
41
|
-
if (
|
|
42
|
-
if (
|
|
39
|
+
const fileReferencesComponent = (source, componentName) => {
|
|
40
|
+
if (source.staticImports.some((staticImport) => staticImport.entries.some((entry) => entry.importName.kind === "Name" && entry.importName.name === componentName))) return true;
|
|
41
|
+
if (source.topLevelVariables.has(componentName)) return true;
|
|
42
|
+
if (source.topLevelFunctions.has(componentName)) return true;
|
|
43
43
|
return false;
|
|
44
44
|
};
|
|
45
45
|
/**
|
|
@@ -53,14 +53,15 @@ const fileReferencesComponent = (sourceFile, componentName) => {
|
|
|
53
53
|
*
|
|
54
54
|
* @param element The JSX element with the spread
|
|
55
55
|
* @param spreadIdentifier The identifier being spread (e.g., 'rest' from {...rest})
|
|
56
|
+
* @param elementSource The parsed source containing the element
|
|
56
57
|
* @param context The purge context for caching
|
|
57
58
|
* @returns Map of prop names to their resolved values, or undefined if couldn't trace
|
|
58
59
|
*/
|
|
59
|
-
const resolveSpreadFromUsages = (element, spreadIdentifier, context) => {
|
|
60
|
-
const parentInfo = getParentComponentInfo(element);
|
|
60
|
+
const resolveSpreadFromUsages = (element, spreadIdentifier, elementSource, context) => {
|
|
61
|
+
const parentInfo = getParentComponentInfo(element, elementSource);
|
|
61
62
|
if (!parentInfo) return;
|
|
62
63
|
if (!(parentInfo.spreadRestIdentifier === spreadIdentifier || parentInfo.paramIdentifier === spreadIdentifier)) return;
|
|
63
|
-
const usages = findComponentUsages(parentInfo.componentName, context.
|
|
64
|
+
const usages = findComponentUsages(parentInfo.componentName, context.sources, context.componentUsageCache);
|
|
64
65
|
if (usages.length === 0) return;
|
|
65
66
|
context.stats.spreadsTraced++;
|
|
66
67
|
const resolvedProps = /* @__PURE__ */ new Map();
|
|
@@ -74,7 +75,7 @@ const resolveSpreadFromUsages = (element, spreadIdentifier, context) => {
|
|
|
74
75
|
resolvedProps.set(prop.name, existing);
|
|
75
76
|
});
|
|
76
77
|
});
|
|
77
|
-
const cacheKey = `${
|
|
78
|
+
const cacheKey = `${elementSource.filePath}:${getLineNumber(elementSource, element.start)}:${spreadIdentifier}`;
|
|
78
79
|
context.spreadCache.set(cacheKey, Array.from(resolvedProps).map(([name, values]) => ({
|
|
79
80
|
name,
|
|
80
81
|
values,
|