@yahoo/uds 3.171.1 → 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.
Files changed (26) hide show
  1. package/dist/automated-config/dist/utils/getConfigVariantProperties.d.cts +2 -2
  2. package/dist/automated-config/dist/utils/getConfigVariantProperties.d.ts +2 -2
  3. package/dist/components/client/Popover/UDSPopoverConfigProvider.d.cts +1 -1
  4. package/dist/components/client/Popover/UDSPopoverConfigProvider.d.ts +1 -1
  5. package/dist/css/dist/purger/optimized/ast/expressions.cjs +278 -278
  6. package/dist/css/dist/purger/optimized/ast/expressions.js +278 -278
  7. package/dist/css/dist/purger/optimized/ast/jsx.cjs +67 -36
  8. package/dist/css/dist/purger/optimized/ast/jsx.js +67 -35
  9. package/dist/css/dist/purger/optimized/ast/oxc.cjs +265 -0
  10. package/dist/css/dist/purger/optimized/ast/oxc.js +253 -0
  11. package/dist/css/dist/purger/optimized/ast/props.cjs +21 -15
  12. package/dist/css/dist/purger/optimized/ast/props.js +21 -14
  13. package/dist/css/dist/purger/optimized/ast/spread.cjs +19 -19
  14. package/dist/css/dist/purger/optimized/ast/spread.js +19 -18
  15. package/dist/css/dist/purger/optimized/purgeFromCode.cjs +86 -101
  16. package/dist/css/dist/purger/optimized/purgeFromCode.js +86 -100
  17. package/dist/tailwind-internal/dist/utils/getShadowStyles.d.cts +2 -2
  18. package/dist/tailwind-internal/dist/utils/getShadowStyles.d.ts +2 -2
  19. package/dist/tailwind-v3/dist/purger/legacy/purgeCSS.cjs +1 -1
  20. package/dist/tailwind-v3/dist/purger/legacy/purgeCSS.js +1 -1
  21. package/dist/uds/generated/componentData.cjs +450 -450
  22. package/dist/uds/generated/componentData.js +450 -450
  23. package/dist/uds/package.cjs +2 -0
  24. package/dist/uds/package.js +2 -0
  25. package/generated/componentData.json +1049 -1049
  26. 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("../../../../../_virtual/_rolldown/runtime.cjs");
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.getAttributes().flatMap((attr) => {
17
- if (ts_morph.Node.isJsxAttribute(attr)) {
18
- const propName = attr.getNameNode().getText();
19
- const stringLiterals = attr.getDescendantsOfKind(ts_morph.SyntaxKind.StringLiteral);
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) => lit.getLiteralText()) : (() => {
23
- const initializer = attr.getInitializer();
24
- if (!initializer || !ts_morph.Node.isJsxExpression(initializer)) return [];
25
- const expression = initializer.getExpression();
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 (ts_morph.Node.isJsxSpreadAttribute(attr)) {
32
- const expression = attr.getExpression();
37
+ if (attr.type === "JSXSpreadAttribute") {
38
+ const expression = attr.argument;
33
39
  return [{
34
- name: `__spread__:${ts_morph.Node.isIdentifier(expression) ? expression.getText() : expression.getChildren().pop()?.getText() ?? ""}`,
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.getAttributes().flatMap((attr) => {
16
- if (Node.isJsxAttribute(attr)) {
17
- const propName = attr.getNameNode().getText();
18
- const stringLiterals = attr.getDescendantsOfKind(SyntaxKind.StringLiteral);
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.getLiteralText()) : (() => {
22
- const initializer = attr.getInitializer();
23
- if (!initializer || !Node.isJsxExpression(initializer)) return [];
24
- const expression = initializer.getExpression();
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 (Node.isJsxSpreadAttribute(attr)) {
31
- const expression = attr.getExpression();
37
+ if (attr.type === "JSXSpreadAttribute") {
38
+ const expression = attr.argument;
32
39
  return [{
33
- name: `__spread__:${Node.isIdentifier(expression) ? expression.getText() : expression.getChildren().pop()?.getText() ?? ""}`,
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("../../../../../_virtual/_rolldown/runtime.cjs");
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 entire project.
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 project The ts-morph project
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, project, cache) => {
18
+ const findComponentUsages = (componentName, sources, cache) => {
20
19
  if (cache?.has(componentName)) return cache.get(componentName);
21
20
  const usages = [];
22
- project.getSourceFiles().forEach((sourceFile) => {
23
- if (!fileReferencesComponent(sourceFile, componentName)) return;
24
- [...sourceFile.getDescendantsOfKind(ts_morph.SyntaxKind.JsxSelfClosingElement), ...sourceFile.getDescendantsOfKind(ts_morph.SyntaxKind.JsxOpeningElement)].filter((el) => el.getTagNameNode().getText() === componentName).forEach((element) => {
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: sourceFile.getFilePath(),
29
- line: element.getStartLineNumber(),
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 = (sourceFile, componentName) => {
41
- if (sourceFile.getImportDeclarations().some((imp) => imp.getNamedImports().some((named) => named.getName() === componentName))) return true;
42
- if (sourceFile.getVariableDeclaration(componentName)) return true;
43
- if (sourceFile.getFunction(componentName)) return true;
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.project, context.componentUsageCache);
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 = `${element.getSourceFile().getFilePath()}:${element.getStartLineNumber()}:${spreadIdentifier}`;
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 entire project.
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 project The ts-morph project
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, project, cache) => {
18
+ const findComponentUsages = (componentName, sources, cache) => {
19
19
  if (cache?.has(componentName)) return cache.get(componentName);
20
20
  const usages = [];
21
- project.getSourceFiles().forEach((sourceFile) => {
22
- if (!fileReferencesComponent(sourceFile, componentName)) return;
23
- [...sourceFile.getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement), ...sourceFile.getDescendantsOfKind(SyntaxKind.JsxOpeningElement)].filter((el) => el.getTagNameNode().getText() === componentName).forEach((element) => {
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: sourceFile.getFilePath(),
28
- line: element.getStartLineNumber(),
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 = (sourceFile, componentName) => {
40
- if (sourceFile.getImportDeclarations().some((imp) => imp.getNamedImports().some((named) => named.getName() === componentName))) return true;
41
- if (sourceFile.getVariableDeclaration(componentName)) return true;
42
- if (sourceFile.getFunction(componentName)) return true;
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.project, context.componentUsageCache);
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 = `${element.getSourceFile().getFilePath()}:${element.getStartLineNumber()}:${spreadIdentifier}`;
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,