@yahoo/uds 3.171.1 → 3.172.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/automated-config/dist/utils/getConfigVariantProperties.d.cts +2 -2
- package/dist/automated-config/dist/utils/getConfigVariantProperties.d.ts +2 -2
- package/dist/components/Link.cjs +3 -3
- package/dist/components/Link.js +3 -3
- package/dist/components/client/Popover/UDSPopoverConfigProvider.d.cts +1 -1
- package/dist/components/client/Popover/UDSPopoverConfigProvider.d.ts +1 -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 +340 -340
- package/dist/uds/generated/componentData.js +340 -340
- package/dist/uds/package.cjs +2 -0
- package/dist/uds/package.js +2 -0
- package/generated/componentData.json +783 -783
- package/package.json +3 -1
|
@@ -1,29 +1,14 @@
|
|
|
1
1
|
/*! © 2026 Yahoo, Inc. UDS v0.0.0-development */
|
|
2
|
+
import { findLocalDefinitions, forEachDescendant, getParent, getPropertyName, getStringLiteralValue, getText, isNoSubstitutionTemplate, isStringLiteral, parseSource } from "./oxc.js";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
4
|
-
import {
|
|
5
|
+
import { ResolverFactory } from "oxc-resolver";
|
|
5
6
|
//#region ../css/dist/purger/optimized/ast/expressions.mjs
|
|
6
7
|
/*! © 2026 Yahoo, Inc. UDS CSS v0.0.0-development */
|
|
7
|
-
const
|
|
8
|
-
const importedSourceFileCache = /* @__PURE__ */ new Map();
|
|
8
|
+
const importedSourceCache = /* @__PURE__ */ new Map();
|
|
9
9
|
const tsConfigPathCache = /* @__PURE__ */ new Map();
|
|
10
|
-
const
|
|
10
|
+
const resolverCache = /* @__PURE__ */ new Map();
|
|
11
11
|
const hasImportQuery = (moduleSpecifier) => moduleSpecifier.includes("?");
|
|
12
|
-
const getLocalDefinitionNodes = (node) => {
|
|
13
|
-
if (!Node.isIdentifier(node)) return [];
|
|
14
|
-
const name = node.getText();
|
|
15
|
-
const nodeStart = node.getStart();
|
|
16
|
-
const sourceFile = node.getSourceFile();
|
|
17
|
-
return [
|
|
18
|
-
...sourceFile.getDescendantsOfKind(SyntaxKind.VariableDeclaration).filter((definition) => definition.getName() === name && definition.getStart() <= nodeStart),
|
|
19
|
-
...sourceFile.getDescendantsOfKind(SyntaxKind.Parameter).filter((definition) => definition.getName() === name && definition.getStart() <= nodeStart),
|
|
20
|
-
...sourceFile.getDescendantsOfKind(SyntaxKind.BindingElement).filter((definition) => definition.getName() === name && definition.getStart() <= nodeStart),
|
|
21
|
-
...sourceFile.getFunctions().filter((definition) => definition.getName() === name && definition.getStart() <= nodeStart)
|
|
22
|
-
].sort((left, right) => right.getStart() - left.getStart()).slice(0, 1);
|
|
23
|
-
};
|
|
24
|
-
const getDefinitionNodesSafe = (node) => {
|
|
25
|
-
return getLocalDefinitionNodes(node);
|
|
26
|
-
};
|
|
27
12
|
const isExistingFile = (candidate) => fs.existsSync(candidate) && fs.statSync(candidate).isFile();
|
|
28
13
|
const resolveRelativeImportPath = (sourceFilePath, moduleSpecifier) => {
|
|
29
14
|
if (!path.isAbsolute(sourceFilePath) || !moduleSpecifier.startsWith(".") || hasImportQuery(moduleSpecifier)) return null;
|
|
@@ -68,55 +53,69 @@ const findNearestTypeScriptConfig = (sourceFilePath) => {
|
|
|
68
53
|
currentDir = parentDir;
|
|
69
54
|
}
|
|
70
55
|
};
|
|
71
|
-
const
|
|
56
|
+
const getResolverForConfig = (configPath) => {
|
|
57
|
+
const cached = resolverCache.get(configPath);
|
|
58
|
+
if (cached) return cached;
|
|
59
|
+
const resolver = new ResolverFactory({
|
|
60
|
+
tsconfig: {
|
|
61
|
+
configFile: configPath,
|
|
62
|
+
references: "auto"
|
|
63
|
+
},
|
|
64
|
+
extensions: [
|
|
65
|
+
".ts",
|
|
66
|
+
".tsx",
|
|
67
|
+
".mts",
|
|
68
|
+
".cts",
|
|
69
|
+
".js",
|
|
70
|
+
".jsx",
|
|
71
|
+
".mjs",
|
|
72
|
+
".cjs"
|
|
73
|
+
],
|
|
74
|
+
conditionNames: [
|
|
75
|
+
"import",
|
|
76
|
+
"require",
|
|
77
|
+
"node"
|
|
78
|
+
]
|
|
79
|
+
});
|
|
80
|
+
resolverCache.set(configPath, resolver);
|
|
81
|
+
return resolver;
|
|
82
|
+
};
|
|
83
|
+
const resolveConfigImportPath = (sourceFilePath, moduleSpecifier) => {
|
|
84
|
+
if (hasImportQuery(moduleSpecifier)) return null;
|
|
72
85
|
const configPath = findNearestTypeScriptConfig(sourceFilePath);
|
|
73
86
|
if (!configPath) return null;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
try {
|
|
88
|
+
const resolved = getResolverForConfig(configPath).sync(path.dirname(sourceFilePath), moduleSpecifier).path;
|
|
89
|
+
if (!resolved) return null;
|
|
90
|
+
if (resolved.includes(`${path.sep}node_modules${path.sep}`)) return null;
|
|
91
|
+
return isExistingFile(resolved) ? resolved : null;
|
|
92
|
+
} catch {
|
|
79
93
|
return null;
|
|
80
94
|
}
|
|
81
|
-
const parsed = ts.parseJsonConfigFileContent(readResult.config, ts.sys, path.dirname(configPath));
|
|
82
|
-
const compilerOptions = parsed.options.paths && !parsed.options.baseUrl ? {
|
|
83
|
-
...parsed.options,
|
|
84
|
-
baseUrl: path.dirname(configPath)
|
|
85
|
-
} : parsed.options;
|
|
86
|
-
compilerOptionsCache.set(configPath, compilerOptions);
|
|
87
|
-
return compilerOptions;
|
|
88
|
-
};
|
|
89
|
-
const resolveConfigImportPath = (sourceFilePath, moduleSpecifier) => {
|
|
90
|
-
if (hasImportQuery(moduleSpecifier)) return null;
|
|
91
|
-
const compilerOptions = getCompilerOptionsForSourceFile(sourceFilePath);
|
|
92
|
-
if (!compilerOptions) return null;
|
|
93
|
-
const resolvedModule = ts.resolveModuleName(moduleSpecifier, sourceFilePath, compilerOptions, ts.sys).resolvedModule;
|
|
94
|
-
if (!resolvedModule) return null;
|
|
95
|
-
return isExistingFile(resolvedModule.resolvedFileName) ? resolvedModule.resolvedFileName : null;
|
|
96
95
|
};
|
|
97
96
|
const resolveImportPath = (sourceFilePath, moduleSpecifier) => moduleSpecifier.startsWith(".") ? resolveRelativeImportPath(sourceFilePath, moduleSpecifier) : resolveConfigImportPath(sourceFilePath, moduleSpecifier);
|
|
98
|
-
const
|
|
99
|
-
const cached =
|
|
97
|
+
const getImportedSource = (filePath) => {
|
|
98
|
+
const cached = importedSourceCache.get(filePath);
|
|
100
99
|
if (cached) return cached;
|
|
101
100
|
if (!fs.existsSync(filePath)) return null;
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
return
|
|
101
|
+
const source = parseSource(filePath, fs.readFileSync(filePath, "utf-8"));
|
|
102
|
+
importedSourceCache.set(filePath, source);
|
|
103
|
+
return source;
|
|
105
104
|
};
|
|
106
|
-
const extractImportedIdentifierValues = (node, visited) => {
|
|
107
|
-
if (
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (!
|
|
114
|
-
const resolvedPath = resolveImportPath(
|
|
105
|
+
const extractImportedIdentifierValues = (node, source, visited) => {
|
|
106
|
+
if (node.type !== "Identifier") return [];
|
|
107
|
+
const localName = node.name;
|
|
108
|
+
const entry = source.staticImports.flatMap((staticImport) => staticImport.entries.map((importEntry) => ({
|
|
109
|
+
staticImport,
|
|
110
|
+
importEntry
|
|
111
|
+
}))).find(({ importEntry }) => importEntry.importName.kind === "Name" && importEntry.localName.value === localName);
|
|
112
|
+
if (!entry || !entry.importEntry.importName.name) return [];
|
|
113
|
+
const resolvedPath = resolveImportPath(source.filePath, entry.staticImport.moduleRequest.value);
|
|
115
114
|
if (!resolvedPath) return [];
|
|
116
|
-
const
|
|
117
|
-
if (!
|
|
118
|
-
const initializer =
|
|
119
|
-
return initializer ? extractStringLiterals(initializer, visited) : [];
|
|
115
|
+
const importedSource = getImportedSource(resolvedPath);
|
|
116
|
+
if (!importedSource) return [];
|
|
117
|
+
const initializer = importedSource.topLevelVariables.get(entry.importEntry.importName.name)?.init;
|
|
118
|
+
return initializer ? extractStringLiterals(initializer, importedSource, visited) : [];
|
|
120
119
|
};
|
|
121
120
|
/**
|
|
122
121
|
* Extracts string literal values from an expression.
|
|
@@ -131,93 +130,102 @@ const extractImportedIdentifierValues = (node, visited) => {
|
|
|
131
130
|
* - As expressions: value as Type
|
|
132
131
|
*
|
|
133
132
|
* @param node The expression node to extract values from
|
|
133
|
+
* @param source The parsed source containing the node
|
|
134
134
|
* @param visited Set of visited nodes to prevent infinite recursion
|
|
135
135
|
* @returns Array of extracted string literal values
|
|
136
136
|
*/
|
|
137
|
-
const extractStringLiterals = (node, visited = /* @__PURE__ */ new Set()) => {
|
|
137
|
+
const extractStringLiterals = (node, source, visited = /* @__PURE__ */ new Set()) => {
|
|
138
138
|
if (visited.has(node)) return [];
|
|
139
139
|
visited.add(node);
|
|
140
|
-
|
|
140
|
+
if (node.type === "ChainExpression") return extractStringLiterals(node.expression, source, visited);
|
|
141
|
+
const directValues = extractDirectLiteralValues(node, source);
|
|
141
142
|
if (directValues) return directValues;
|
|
142
|
-
const templateValues = extractTemplateExpressionValues(node, visited);
|
|
143
|
+
const templateValues = extractTemplateExpressionValues(node, source, visited);
|
|
143
144
|
if (templateValues) return templateValues;
|
|
144
|
-
if (
|
|
145
|
-
const binaryValues = extractBinaryExpressionValues(node, visited);
|
|
145
|
+
if (node.type === "ConditionalExpression") return [...extractStringLiterals(node.consequent, source, visited), ...extractStringLiterals(node.alternate, source, visited)];
|
|
146
|
+
const binaryValues = extractBinaryExpressionValues(node, source, visited);
|
|
146
147
|
if (binaryValues) return binaryValues;
|
|
147
|
-
const wrappedExpressionValues = extractWrappedExpressionValues(node, visited);
|
|
148
|
+
const wrappedExpressionValues = extractWrappedExpressionValues(node, source, visited);
|
|
148
149
|
if (wrappedExpressionValues) return wrappedExpressionValues;
|
|
149
|
-
const propertyAccessValues = extractPropertyAccessValues(node, visited);
|
|
150
|
+
const propertyAccessValues = extractPropertyAccessValues(node, source, visited);
|
|
150
151
|
if (propertyAccessValues) return propertyAccessValues;
|
|
151
|
-
const elementAccessValues = extractElementAccessValues(node, visited);
|
|
152
|
+
const elementAccessValues = extractElementAccessValues(node, source, visited);
|
|
152
153
|
if (elementAccessValues) return elementAccessValues;
|
|
153
|
-
const callExpressionValues = extractCallExpressionValues(node, visited);
|
|
154
|
+
const callExpressionValues = extractCallExpressionValues(node, source, visited);
|
|
154
155
|
if (callExpressionValues) return callExpressionValues;
|
|
155
|
-
const identifierValues = extractIdentifierValues(node, visited);
|
|
156
|
+
const identifierValues = extractIdentifierValues(node, source, visited);
|
|
156
157
|
if (identifierValues) return identifierValues;
|
|
157
158
|
return [];
|
|
158
159
|
};
|
|
159
|
-
const extractDirectLiteralValues = (node) => {
|
|
160
|
-
if (
|
|
160
|
+
const extractDirectLiteralValues = (node, _source) => {
|
|
161
|
+
if (isStringLiteral(node)) return [getStringLiteralValue(node)];
|
|
162
|
+
if (isNoSubstitutionTemplate(node) && node.type === "TemplateLiteral") return [node.quasis[0]?.value.cooked ?? ""];
|
|
161
163
|
return null;
|
|
162
164
|
};
|
|
163
|
-
const extractTemplateExpressionValues = (node, visited) => {
|
|
164
|
-
if (
|
|
165
|
-
const parts = [[node.
|
|
166
|
-
node.
|
|
167
|
-
const exprValues = extractStringLiterals(
|
|
168
|
-
const literalText =
|
|
165
|
+
const extractTemplateExpressionValues = (node, source, visited) => {
|
|
166
|
+
if (node.type !== "TemplateLiteral" || node.expressions.length === 0) return null;
|
|
167
|
+
const parts = [[node.quasis[0]?.value.cooked ?? ""]];
|
|
168
|
+
node.expressions.forEach((expression, index) => {
|
|
169
|
+
const exprValues = extractStringLiterals(expression, source, visited);
|
|
170
|
+
const literalText = node.quasis[index + 1]?.value.cooked ?? "";
|
|
169
171
|
parts.push(exprValues.length > 0 ? exprValues.map((value) => `${value}${literalText}`) : ["", literalText]);
|
|
170
172
|
});
|
|
171
173
|
return parts.reduce((acc, segment) => acc.flatMap((base) => segment.map((frag) => `${base}${frag}`)), [""]).filter((value) => value.length > 0);
|
|
172
174
|
};
|
|
173
|
-
const extractBinaryExpressionValues = (node, visited) => {
|
|
174
|
-
if (
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
175
|
+
const extractBinaryExpressionValues = (node, source, visited) => {
|
|
176
|
+
if (node.type === "BinaryExpression") {
|
|
177
|
+
if (node.operator === "+") {
|
|
178
|
+
const left = extractStringLiterals(node.left, source, visited);
|
|
179
|
+
const right = extractStringLiterals(node.right, source, visited);
|
|
180
|
+
const combined = left.flatMap((l) => right.map((r) => `${l}${r}`));
|
|
181
|
+
return combined.length > 0 ? combined : [];
|
|
182
|
+
}
|
|
183
|
+
return [];
|
|
181
184
|
}
|
|
182
|
-
if (
|
|
183
|
-
|
|
185
|
+
if (node.type === "LogicalExpression") {
|
|
186
|
+
if (node.operator === "||" || node.operator === "??") return [...extractStringLiterals(node.left, source, visited), ...extractStringLiterals(node.right, source, visited)];
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
184
190
|
};
|
|
185
|
-
const extractWrappedExpressionValues = (node, visited) => {
|
|
186
|
-
if (
|
|
191
|
+
const extractWrappedExpressionValues = (node, source, visited) => {
|
|
192
|
+
if (node.type === "ParenthesizedExpression" || node.type === "TSAsExpression") return extractStringLiterals(node.expression, source, visited);
|
|
187
193
|
return null;
|
|
188
194
|
};
|
|
189
|
-
const extractPropertyAccessValues = (node, visited) => {
|
|
190
|
-
if (
|
|
191
|
-
|
|
195
|
+
const extractPropertyAccessValues = (node, source, visited) => {
|
|
196
|
+
if (node.type !== "MemberExpression" || node.computed) return null;
|
|
197
|
+
if (node.property.type !== "Identifier") return null;
|
|
198
|
+
const typeValues = extractLiteralValuesFromType(node, source);
|
|
192
199
|
if (typeValues.length > 0) return typeValues;
|
|
193
|
-
const propertyName = node.
|
|
194
|
-
return extractObjectValues(node.
|
|
200
|
+
const propertyName = node.property.name;
|
|
201
|
+
return extractObjectValues(node.object, source, visited).filter((value) => typeof value === "object" && value !== null && propertyName in value).map((value) => value[propertyName]).filter((value) => typeof value === "string");
|
|
195
202
|
};
|
|
196
|
-
const extractElementAccessValues = (node, visited) => {
|
|
197
|
-
if (!
|
|
198
|
-
const typeValues = extractLiteralValuesFromType(node);
|
|
203
|
+
const extractElementAccessValues = (node, source, visited) => {
|
|
204
|
+
if (node.type !== "MemberExpression" || !node.computed) return null;
|
|
205
|
+
const typeValues = extractLiteralValuesFromType(node, source);
|
|
199
206
|
if (typeValues.length > 0) return typeValues;
|
|
200
|
-
return extractObjectValues(node.
|
|
207
|
+
return extractObjectValues(node.object, source, visited).flatMap((value) => {
|
|
201
208
|
if (typeof value === "string") return [value];
|
|
202
209
|
if (typeof value === "object" && value !== null) return Object.values(value).filter((entry) => typeof entry === "string");
|
|
203
210
|
return [];
|
|
204
211
|
});
|
|
205
212
|
};
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
213
|
+
const collectReturnExpressions = (node) => {
|
|
214
|
+
const returns = [];
|
|
215
|
+
forEachDescendant(node, (descendant) => {
|
|
216
|
+
if (descendant.type === "ReturnStatement" && descendant.argument) returns.push(descendant.argument);
|
|
217
|
+
});
|
|
218
|
+
return returns;
|
|
219
|
+
};
|
|
220
|
+
const extractCallExpressionValues = (node, source, visited) => {
|
|
221
|
+
if (node.type !== "CallExpression") return null;
|
|
222
|
+
const callee = node.callee;
|
|
223
|
+
const fromDefinitions = callee.type === "Identifier" ? findLocalDefinitions(source, callee).flatMap((definition) => {
|
|
224
|
+
if (definition.kind === "function") return collectReturnExpressions(definition.node).flatMap((expression) => extractStringLiterals(expression, source, visited));
|
|
225
|
+
if (definition.kind === "variable") return definition.init ? extractFromFunctionLike(definition.init, source, visited) : [];
|
|
218
226
|
return [];
|
|
219
227
|
}) : [];
|
|
220
|
-
const fromArguments = node.
|
|
228
|
+
const fromArguments = node.arguments.flatMap((arg) => extractStringLiterals(arg, source, visited));
|
|
221
229
|
return [...fromDefinitions, ...fromArguments];
|
|
222
230
|
};
|
|
223
231
|
/** Array methods that return (a subset of) the receiver's elements. */
|
|
@@ -235,19 +243,18 @@ const ARRAY_CHAIN_METHODS = new Set([
|
|
|
235
243
|
* If the definition is the first parameter of a `.map()`/`.flatMap()` callback,
|
|
236
244
|
* return the expression being mapped over (the array source).
|
|
237
245
|
*/
|
|
238
|
-
const getMappedArraySource = (definition) => {
|
|
239
|
-
if (!
|
|
240
|
-
const callback = definition.
|
|
241
|
-
if (
|
|
242
|
-
const callExpression = callback
|
|
243
|
-
if (!callExpression) return null;
|
|
244
|
-
const
|
|
245
|
-
if (
|
|
246
|
-
if (
|
|
247
|
-
if (
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return expression.getExpression();
|
|
246
|
+
const getMappedArraySource = (definition, source) => {
|
|
247
|
+
if (definition.kind !== "parameter" || !definition.paramFunction) return null;
|
|
248
|
+
const callback = definition.paramFunction;
|
|
249
|
+
if (callback.type !== "ArrowFunctionExpression") return null;
|
|
250
|
+
const callExpression = getParent(source, callback);
|
|
251
|
+
if (!callExpression || callExpression.type !== "CallExpression") return null;
|
|
252
|
+
const callee = callExpression.callee;
|
|
253
|
+
if (callee.type !== "MemberExpression" || callee.computed) return null;
|
|
254
|
+
if (callee.property.type !== "Identifier") return null;
|
|
255
|
+
if (!["map", "flatMap"].includes(callee.property.name)) return null;
|
|
256
|
+
if (definition.paramIndex !== 0) return null;
|
|
257
|
+
return callee.object;
|
|
251
258
|
};
|
|
252
259
|
/**
|
|
253
260
|
* Resolve a node to the object-literal AST nodes it may evaluate to.
|
|
@@ -255,25 +262,23 @@ const getMappedArraySource = (definition) => {
|
|
|
255
262
|
* array-method chains, and `.map()` callback parameters (UDS-2557 follow-up:
|
|
256
263
|
* variant arrays nested inside const structures).
|
|
257
264
|
*/
|
|
258
|
-
const resolveObjectLiteralNodes = (node, visited) => {
|
|
265
|
+
const resolveObjectLiteralNodes = (node, source, visited) => {
|
|
259
266
|
if (visited.has(node)) return [];
|
|
260
267
|
visited.add(node);
|
|
261
|
-
if (
|
|
262
|
-
if (
|
|
263
|
-
if (
|
|
264
|
-
if (
|
|
265
|
-
if (
|
|
266
|
-
|
|
267
|
-
|
|
268
|
+
if (node.type === "ChainExpression") return resolveObjectLiteralNodes(node.expression, source, visited);
|
|
269
|
+
if (node.type === "ObjectExpression") return [node];
|
|
270
|
+
if (node.type === "ParenthesizedExpression" || node.type === "TSAsExpression" || node.type === "TSTypeAssertion" || node.type === "TSSatisfiesExpression") return resolveObjectLiteralNodes(node.expression, source, visited);
|
|
271
|
+
if (node.type === "ArrayExpression") return node.elements.flatMap((element) => element ? resolveObjectLiteralNodes(element, source, visited) : []);
|
|
272
|
+
if (node.type === "MemberExpression" && node.computed) return resolveObjectLiteralNodes(node.object, source, visited);
|
|
273
|
+
if (node.type === "CallExpression") {
|
|
274
|
+
const callee = node.callee;
|
|
275
|
+
if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier" && ARRAY_CHAIN_METHODS.has(callee.property.name)) return resolveObjectLiteralNodes(callee.object, source, visited);
|
|
268
276
|
return [];
|
|
269
277
|
}
|
|
270
|
-
if (
|
|
271
|
-
if (
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
const mappedSource = getMappedArraySource(definition);
|
|
276
|
-
if (mappedSource) return resolveObjectLiteralNodes(mappedSource, visited);
|
|
278
|
+
if (node.type === "Identifier") return findLocalDefinitions(source, node).flatMap((definition) => {
|
|
279
|
+
if (definition.kind === "variable") return definition.init ? resolveObjectLiteralNodes(definition.init, source, visited) : [];
|
|
280
|
+
const mappedSource = getMappedArraySource(definition, source);
|
|
281
|
+
if (mappedSource) return resolveObjectLiteralNodes(mappedSource, source, visited);
|
|
277
282
|
return [];
|
|
278
283
|
});
|
|
279
284
|
return [];
|
|
@@ -281,74 +286,82 @@ const resolveObjectLiteralNodes = (node, visited) => {
|
|
|
281
286
|
/**
|
|
282
287
|
* Collect the initializers of a named property across object-literal candidates.
|
|
283
288
|
*/
|
|
284
|
-
const getPropertyInitializers = (objectLiterals, propertyName) => objectLiterals.flatMap((obj) => {
|
|
285
|
-
if (
|
|
286
|
-
return obj.
|
|
287
|
-
if (
|
|
288
|
-
|
|
289
|
-
return initializer ? [initializer] : [];
|
|
289
|
+
const getPropertyInitializers = (objectLiterals, propertyName, source) => objectLiterals.flatMap((obj) => {
|
|
290
|
+
if (obj.type !== "ObjectExpression") return [];
|
|
291
|
+
return obj.properties.flatMap((prop) => {
|
|
292
|
+
if (prop.type !== "Property" || prop.shorthand || getPropertyName(source, prop) !== propertyName) return [];
|
|
293
|
+
return prop.value ? [prop.value] : [];
|
|
290
294
|
});
|
|
291
295
|
});
|
|
292
296
|
/**
|
|
293
297
|
* For a destructured binding (e.g. `({ variants }) =>` map param or
|
|
294
298
|
* `const { variants } = GROUPS[0]`), resolve the bound property's initializers.
|
|
295
299
|
*/
|
|
296
|
-
const getBindingElementSources = (definition) => {
|
|
297
|
-
if (
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
300
|
+
const getBindingElementSources = (definition, source) => {
|
|
301
|
+
if (definition.kind !== "binding" || definition.bindingPattern?.type !== "ObjectPattern") return [];
|
|
302
|
+
const propertyName = definition.bindingPropertyName ?? definition.name;
|
|
303
|
+
const pattern = definition.bindingPattern;
|
|
304
|
+
const holder = getParent(source, pattern);
|
|
305
|
+
let arraySource = null;
|
|
306
|
+
if (holder && holder.type === "VariableDeclarator") arraySource = holder.init ?? null;
|
|
307
|
+
else {
|
|
308
|
+
const paramNode = holder && holder.type === "AssignmentPattern" ? holder : pattern;
|
|
309
|
+
const fn = holder && holder.type === "AssignmentPattern" ? getParent(source, holder) : holder;
|
|
310
|
+
if (fn && (fn.type === "ArrowFunctionExpression" || fn.type === "FunctionDeclaration" || fn.type === "FunctionExpression")) {
|
|
311
|
+
const paramIndex = fn.params.indexOf(paramNode);
|
|
312
|
+
if (paramIndex !== -1) arraySource = getMappedArraySource({
|
|
313
|
+
kind: "parameter",
|
|
314
|
+
name: propertyName,
|
|
315
|
+
start: pattern.start,
|
|
316
|
+
node: paramNode,
|
|
317
|
+
init: null,
|
|
318
|
+
typeNode: null,
|
|
319
|
+
paramFunction: fn,
|
|
320
|
+
paramIndex
|
|
321
|
+
}, source);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (!arraySource) return [];
|
|
325
|
+
return getPropertyInitializers(resolveObjectLiteralNodes(arraySource, source, /* @__PURE__ */ new Set()), propertyName, source);
|
|
307
326
|
};
|
|
308
|
-
const extractArrayElementValues = (node, visited) => {
|
|
309
|
-
if (
|
|
310
|
-
if (
|
|
311
|
-
if (
|
|
312
|
-
if (
|
|
313
|
-
if (
|
|
314
|
-
|
|
315
|
-
|
|
327
|
+
const extractArrayElementValues = (node, source, visited) => {
|
|
328
|
+
if (node.type === "ChainExpression") return extractArrayElementValues(node.expression, source, visited);
|
|
329
|
+
if (node.type === "ArrayExpression") return node.elements.flatMap((element) => element ? extractStringLiterals(element, source, visited) : []).filter((value, index, values) => values.indexOf(value) === index);
|
|
330
|
+
if (node.type === "ParenthesizedExpression") return extractArrayElementValues(node.expression, source, visited);
|
|
331
|
+
if (node.type === "TSAsExpression" || node.type === "TSTypeAssertion" || node.type === "TSSatisfiesExpression") return extractArrayElementValues(node.expression, source, visited);
|
|
332
|
+
if (node.type === "MemberExpression" && !node.computed) {
|
|
333
|
+
if (node.property.type !== "Identifier") return [];
|
|
334
|
+
return getPropertyInitializers(resolveObjectLiteralNodes(node.object, source, /* @__PURE__ */ new Set()), node.property.name, source).flatMap((initializer) => extractArrayElementValues(initializer, source, visited)).filter((value, index, values) => values.indexOf(value) === index);
|
|
335
|
+
}
|
|
336
|
+
if (node.type === "CallExpression") {
|
|
337
|
+
const callee = node.callee;
|
|
338
|
+
if (callee.type === "MemberExpression" && !callee.computed && callee.property.type === "Identifier" && ARRAY_CHAIN_METHODS.has(callee.property.name)) return extractArrayElementValues(callee.object, source, visited);
|
|
316
339
|
return [];
|
|
317
340
|
}
|
|
318
|
-
if (
|
|
319
|
-
if (
|
|
320
|
-
|
|
321
|
-
return initializer ? extractArrayElementValues(initializer, visited) : [];
|
|
322
|
-
}
|
|
323
|
-
return getBindingElementSources(definition).flatMap((source) => extractArrayElementValues(source, visited));
|
|
341
|
+
if (node.type === "Identifier") return findLocalDefinitions(source, node).flatMap((definition) => {
|
|
342
|
+
if (definition.kind === "variable") return definition.init ? extractArrayElementValues(definition.init, source, visited) : [];
|
|
343
|
+
return getBindingElementSources(definition, source).flatMap((elementSource) => extractArrayElementValues(elementSource, source, visited));
|
|
324
344
|
}).filter((value, index, values) => values.indexOf(value) === index);
|
|
325
345
|
return [];
|
|
326
346
|
};
|
|
327
|
-
const extractMappedParameterValues = (definition, visited) => {
|
|
328
|
-
const mappedSource = getMappedArraySource(definition);
|
|
347
|
+
const extractMappedParameterValues = (definition, source, visited) => {
|
|
348
|
+
const mappedSource = getMappedArraySource(definition, source);
|
|
329
349
|
if (!mappedSource) return [];
|
|
330
|
-
return extractArrayElementValues(mappedSource, visited);
|
|
350
|
+
return extractArrayElementValues(mappedSource, source, visited);
|
|
331
351
|
};
|
|
332
|
-
const extractIdentifierValues = (node, visited) => {
|
|
333
|
-
if (
|
|
334
|
-
const values =
|
|
335
|
-
const mappedParameterValues = extractMappedParameterValues(definition, visited);
|
|
352
|
+
const extractIdentifierValues = (node, source, visited) => {
|
|
353
|
+
if (node.type !== "Identifier") return null;
|
|
354
|
+
const values = findLocalDefinitions(source, node).flatMap((definition) => {
|
|
355
|
+
const mappedParameterValues = extractMappedParameterValues(definition, source, visited);
|
|
336
356
|
if (mappedParameterValues.length > 0) return mappedParameterValues;
|
|
337
|
-
if (
|
|
338
|
-
|
|
339
|
-
if (
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const
|
|
344
|
-
if (
|
|
345
|
-
const parentInit = parentDecl.getInitializer();
|
|
346
|
-
if (parentInit) {
|
|
347
|
-
const propName = definition.getNameNode().getText();
|
|
348
|
-
const propValues = extractObjectValues(parentInit, new Set(visited)).filter((obj) => typeof obj === "object" && obj !== null && propName in obj).map((obj) => obj[propName]).filter((v) => typeof v === "string");
|
|
349
|
-
if (propValues.length > 0) return propValues;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
357
|
+
if (definition.kind === "variable" || definition.kind === "parameter" || definition.kind === "binding") {
|
|
358
|
+
if (definition.init) return extractStringLiterals(definition.init, source, visited);
|
|
359
|
+
if (definition.kind === "binding" && definition.bindingPattern?.type === "ObjectPattern") {
|
|
360
|
+
const holder = getParent(source, definition.bindingPattern);
|
|
361
|
+
if (holder && holder.type === "VariableDeclarator" && holder.init) {
|
|
362
|
+
const propName = definition.name;
|
|
363
|
+
const propValues = extractObjectValues(holder.init, source, new Set(visited)).filter((obj) => typeof obj === "object" && obj !== null && propName in obj).map((obj) => obj[propName]).filter((v) => typeof v === "string");
|
|
364
|
+
if (propValues.length > 0) return propValues;
|
|
352
365
|
}
|
|
353
366
|
}
|
|
354
367
|
return [];
|
|
@@ -356,25 +369,23 @@ const extractIdentifierValues = (node, visited) => {
|
|
|
356
369
|
return [];
|
|
357
370
|
});
|
|
358
371
|
if (values.length > 0) return values;
|
|
359
|
-
const importedValues = extractImportedIdentifierValues(node, visited);
|
|
372
|
+
const importedValues = extractImportedIdentifierValues(node, source, visited);
|
|
360
373
|
if (importedValues.length > 0) return importedValues;
|
|
361
|
-
return extractLiteralValuesFromType(node);
|
|
374
|
+
return extractLiteralValuesFromType(node, source);
|
|
362
375
|
};
|
|
363
376
|
/**
|
|
364
377
|
* Extract string literals from arrow functions or function expressions
|
|
365
378
|
*/
|
|
366
|
-
const extractFromFunctionLike = (node, visited) => {
|
|
379
|
+
const extractFromFunctionLike = (node, source, visited) => {
|
|
367
380
|
const values = [];
|
|
368
|
-
if (
|
|
369
|
-
const body = node.
|
|
370
|
-
if (
|
|
371
|
-
|
|
372
|
-
if (returnExpr) values.push(...extractStringLiterals(returnExpr, visited));
|
|
381
|
+
if (node.type === "ArrowFunctionExpression") {
|
|
382
|
+
const body = node.body;
|
|
383
|
+
if (body.type === "BlockStatement") collectReturnExpressions(body).forEach((returnExpr) => {
|
|
384
|
+
values.push(...extractStringLiterals(returnExpr, source, visited));
|
|
373
385
|
});
|
|
374
|
-
else values.push(...extractStringLiterals(body, visited));
|
|
375
|
-
} else if (
|
|
376
|
-
|
|
377
|
-
if (returnExpr) values.push(...extractStringLiterals(returnExpr, visited));
|
|
386
|
+
else values.push(...extractStringLiterals(body, source, visited));
|
|
387
|
+
} else if (node.type === "FunctionExpression") collectReturnExpressions(node).forEach((returnExpr) => {
|
|
388
|
+
values.push(...extractStringLiterals(returnExpr, source, visited));
|
|
378
389
|
});
|
|
379
390
|
return values;
|
|
380
391
|
};
|
|
@@ -382,19 +393,20 @@ const extractFromFunctionLike = (node, visited) => {
|
|
|
382
393
|
* Extract object literal values from an expression.
|
|
383
394
|
* Returns an array of all possible object values (for union types or indexed access).
|
|
384
395
|
*/
|
|
385
|
-
const extractObjectValues = (node, visited) => {
|
|
396
|
+
const extractObjectValues = (node, source, visited) => {
|
|
386
397
|
if (visited.has(node)) return [];
|
|
387
398
|
visited.add(node);
|
|
399
|
+
if (node.type === "ChainExpression") return extractObjectValues(node.expression, source, visited);
|
|
388
400
|
const values = [];
|
|
389
|
-
if (
|
|
401
|
+
if (node.type === "ObjectExpression") {
|
|
390
402
|
const obj = {};
|
|
391
|
-
node.
|
|
392
|
-
if (
|
|
393
|
-
const name = prop
|
|
394
|
-
const init = prop.
|
|
395
|
-
if (init &&
|
|
396
|
-
else if (init &&
|
|
397
|
-
const nestedValues = extractObjectValues(init, visited);
|
|
403
|
+
node.properties.forEach((prop) => {
|
|
404
|
+
if (prop.type === "Property" && !prop.shorthand) {
|
|
405
|
+
const name = getPropertyName(source, prop);
|
|
406
|
+
const init = prop.value;
|
|
407
|
+
if (init && isStringLiteral(init)) obj[name] = getStringLiteralValue(init);
|
|
408
|
+
else if (init && init.type === "ObjectExpression") {
|
|
409
|
+
const nestedValues = extractObjectValues(init, source, visited);
|
|
398
410
|
if (nestedValues.length > 0) obj[name] = nestedValues[0];
|
|
399
411
|
}
|
|
400
412
|
}
|
|
@@ -402,58 +414,51 @@ const extractObjectValues = (node, visited) => {
|
|
|
402
414
|
values.push(obj);
|
|
403
415
|
return values;
|
|
404
416
|
}
|
|
405
|
-
if (
|
|
406
|
-
|
|
407
|
-
if (
|
|
408
|
-
const initializer = definition.
|
|
409
|
-
if (initializer)
|
|
410
|
-
|
|
411
|
-
values.push(...extractObjectValues(inner, visited));
|
|
412
|
-
} else values.push(...extractObjectValues(initializer, visited));
|
|
417
|
+
if (node.type === "Identifier") {
|
|
418
|
+
findLocalDefinitions(source, node).forEach((definition) => {
|
|
419
|
+
if (definition.kind === "variable" && definition.init) {
|
|
420
|
+
const initializer = definition.init;
|
|
421
|
+
if (initializer.type === "TSAsExpression") values.push(...extractObjectValues(initializer.expression, source, visited));
|
|
422
|
+
else values.push(...extractObjectValues(initializer, source, visited));
|
|
413
423
|
}
|
|
414
424
|
});
|
|
415
|
-
const enumDecl =
|
|
425
|
+
const enumDecl = source.topLevelEnums.get(node.name);
|
|
416
426
|
if (enumDecl) {
|
|
417
427
|
const obj = {};
|
|
418
|
-
enumDecl.
|
|
419
|
-
const initializer = member.
|
|
420
|
-
if (initializer &&
|
|
428
|
+
enumDecl.body.members.forEach((member) => {
|
|
429
|
+
const initializer = member.initializer;
|
|
430
|
+
if (initializer && isStringLiteral(initializer)) {
|
|
431
|
+
const memberName = member.id.type === "Identifier" ? member.id.name : isStringLiteral(member.id) ? getStringLiteralValue(member.id) : getText(source, member.id);
|
|
432
|
+
obj[memberName] = getStringLiteralValue(initializer);
|
|
433
|
+
}
|
|
421
434
|
});
|
|
422
435
|
if (Object.keys(obj).length > 0) values.push(obj);
|
|
423
436
|
}
|
|
424
437
|
return values;
|
|
425
438
|
}
|
|
426
|
-
if (
|
|
427
|
-
const
|
|
428
|
-
if (
|
|
429
|
-
if (
|
|
430
|
-
|
|
431
|
-
if (returnExpr) values.push(...extractObjectValues(returnExpr, visited));
|
|
439
|
+
if (node.type === "CallExpression") {
|
|
440
|
+
const callee = node.callee;
|
|
441
|
+
if (callee.type === "Identifier") findLocalDefinitions(source, callee).forEach((definition) => {
|
|
442
|
+
if (definition.kind === "function") collectReturnExpressions(definition.node).forEach((returnExpr) => {
|
|
443
|
+
values.push(...extractObjectValues(returnExpr, source, visited));
|
|
432
444
|
});
|
|
433
|
-
if (
|
|
434
|
-
const initializer = definition.
|
|
435
|
-
if (initializer) {
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
} else if (Node.isFunctionExpression(fn)) fn.getDescendantsOfKind(SyntaxKind.ReturnStatement).forEach((returnStmt) => {
|
|
445
|
-
const returnExpr = returnStmt.getExpression();
|
|
446
|
-
if (returnExpr) values.push(...extractObjectValues(returnExpr, visited));
|
|
447
|
-
});
|
|
448
|
-
};
|
|
449
|
-
extractFromFn(initializer);
|
|
450
|
-
}
|
|
445
|
+
if (definition.kind === "variable" && definition.init) {
|
|
446
|
+
const initializer = definition.init;
|
|
447
|
+
if (initializer.type === "ArrowFunctionExpression") {
|
|
448
|
+
const body = initializer.body;
|
|
449
|
+
if (body.type === "BlockStatement") collectReturnExpressions(body).forEach((returnExpr) => {
|
|
450
|
+
values.push(...extractObjectValues(returnExpr, source, visited));
|
|
451
|
+
});
|
|
452
|
+
else values.push(...extractObjectValues(body, source, visited));
|
|
453
|
+
} else if (initializer.type === "FunctionExpression") collectReturnExpressions(initializer).forEach((returnExpr) => {
|
|
454
|
+
values.push(...extractObjectValues(returnExpr, source, visited));
|
|
455
|
+
});
|
|
451
456
|
}
|
|
452
457
|
});
|
|
453
458
|
return values;
|
|
454
459
|
}
|
|
455
|
-
if (
|
|
456
|
-
extractObjectValues(node.
|
|
460
|
+
if (node.type === "MemberExpression" && node.computed) {
|
|
461
|
+
extractObjectValues(node.object, source, visited).forEach((obj) => {
|
|
457
462
|
if (typeof obj === "object" && obj !== null) Object.values(obj).forEach((value) => values.push(value));
|
|
458
463
|
});
|
|
459
464
|
return values;
|
|
@@ -462,46 +467,42 @@ const extractObjectValues = (node, visited) => {
|
|
|
462
467
|
};
|
|
463
468
|
/**
|
|
464
469
|
* Extract string literal values from a TypeScript type annotation AST node.
|
|
465
|
-
* Pure AST traversal —
|
|
470
|
+
* Pure AST traversal — no type checker involved.
|
|
466
471
|
*/
|
|
467
472
|
const extractStringLiteralsFromTypeNode = (typeNode) => {
|
|
468
|
-
if (
|
|
469
|
-
const literal = typeNode.
|
|
470
|
-
if (
|
|
473
|
+
if (typeNode.type === "TSLiteralType") {
|
|
474
|
+
const literal = typeNode.literal;
|
|
475
|
+
if (isStringLiteral(literal)) return [getStringLiteralValue(literal)];
|
|
471
476
|
return [];
|
|
472
477
|
}
|
|
473
|
-
if (
|
|
478
|
+
if (typeNode.type === "TSUnionType") return typeNode.types.flatMap(extractStringLiteralsFromTypeNode);
|
|
474
479
|
return [];
|
|
475
480
|
};
|
|
476
481
|
/**
|
|
477
482
|
* Extract literal values from a TypeScript type annotation (for union types like 'brand' | 'secondary').
|
|
478
|
-
* Uses pure AST traversal instead of the TS type checker
|
|
483
|
+
* Uses pure AST traversal instead of the TS type checker.
|
|
479
484
|
*/
|
|
480
|
-
const extractLiteralValuesFromType = (node) => {
|
|
481
|
-
if (
|
|
482
|
-
const definitions =
|
|
485
|
+
const extractLiteralValuesFromType = (node, source) => {
|
|
486
|
+
if (node.type === "Identifier") {
|
|
487
|
+
const definitions = findLocalDefinitions(source, node);
|
|
483
488
|
for (const def of definitions) {
|
|
484
|
-
if (
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const values = extractStringLiteralsFromTypeNode(typeNode);
|
|
489
|
+
if (def.kind === "parameter" || def.kind === "variable") {
|
|
490
|
+
if (def.typeNode) {
|
|
491
|
+
const values = extractStringLiteralsFromTypeNode(def.typeNode);
|
|
488
492
|
if (values.length > 0) return values;
|
|
489
493
|
}
|
|
490
494
|
}
|
|
491
|
-
if (
|
|
492
|
-
const
|
|
493
|
-
if (
|
|
494
|
-
const
|
|
495
|
-
if (
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
const
|
|
501
|
-
if (
|
|
502
|
-
const values = extractStringLiteralsFromTypeNode(memberType);
|
|
503
|
-
if (values.length > 0) return values;
|
|
504
|
-
}
|
|
495
|
+
if (def.kind === "binding" && def.bindingPattern) {
|
|
496
|
+
const holder = getParent(source, def.bindingPattern);
|
|
497
|
+
if (holder && holder.type === "VariableDeclarator") {
|
|
498
|
+
const parentType = holder.id.typeAnnotation?.typeAnnotation;
|
|
499
|
+
if (parentType && parentType.type === "TSTypeLiteral") {
|
|
500
|
+
const propName = def.name;
|
|
501
|
+
for (const member of parentType.members) if (member.type === "TSPropertySignature" && member.key.type === "Identifier" && member.key.name === propName) {
|
|
502
|
+
const memberType = member.typeAnnotation?.typeAnnotation;
|
|
503
|
+
if (memberType) {
|
|
504
|
+
const values = extractStringLiteralsFromTypeNode(memberType);
|
|
505
|
+
if (values.length > 0) return values;
|
|
505
506
|
}
|
|
506
507
|
}
|
|
507
508
|
}
|
|
@@ -509,7 +510,6 @@ const extractLiteralValuesFromType = (node) => {
|
|
|
509
510
|
}
|
|
510
511
|
}
|
|
511
512
|
}
|
|
512
|
-
if (Node.isPropertyAccessExpression(node) || Node.isElementAccessExpression(node)) return [];
|
|
513
513
|
return [];
|
|
514
514
|
};
|
|
515
515
|
//#endregion
|