styled-components-to-stylex-codemod 0.0.48 → 0.0.49
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/{extract-external-interface-CvkkJZb1.mjs → ast-walk-DigTJqU7.mjs} +26 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +164 -5
- package/dist/{compute-leaf-set-90UrZ9WP.mjs → prepass-parser-CwdDzSgx.mjs} +100 -2
- package/dist/{prop-usage-Bz3z0V2F.mjs → prop-usage-QtOSsKTr.mjs} +15 -1
- package/dist/{run-prepass-1yZOVT3P.mjs → run-prepass-BWQCThfh.mjs} +117 -106
- package/dist/{transform-types-Bshr_dBf.d.mts → transform-types-CHRHLCj_.d.mts} +10 -1
- package/dist/transform.d.mts +1 -1
- package/dist/transform.mjs +2160 -364
- package/dist/{typescript-analysis-C_25uBrt.mjs → typescript-analysis-CmIDxfLv.mjs} +117 -0
- package/package.json +1 -1
|
@@ -380,4 +380,29 @@ function getFileImportsFromRes(name) {
|
|
|
380
380
|
return cached;
|
|
381
381
|
}
|
|
382
382
|
//#endregion
|
|
383
|
-
|
|
383
|
+
//#region src/internal/utilities/ast-walk.ts
|
|
384
|
+
const SKIPPED_KEYS = new Set([
|
|
385
|
+
"loc",
|
|
386
|
+
"comments",
|
|
387
|
+
"leadingComments",
|
|
388
|
+
"trailingComments"
|
|
389
|
+
]);
|
|
390
|
+
function walkAst(root, visitor) {
|
|
391
|
+
const visit = (node) => {
|
|
392
|
+
if (!node || typeof node !== "object") return;
|
|
393
|
+
if (Array.isArray(node)) {
|
|
394
|
+
for (const child of node) visit(child);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const n = node;
|
|
398
|
+
visitor(n);
|
|
399
|
+
for (const key of Object.keys(n)) {
|
|
400
|
+
if (SKIPPED_KEYS.has(key)) continue;
|
|
401
|
+
const child = n[key];
|
|
402
|
+
if (child && typeof child === "object") visit(child);
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
visit(root);
|
|
406
|
+
}
|
|
407
|
+
//#endregion
|
|
408
|
+
export { getReExportedSourceName as a, Logger as c, findImportSource as i, UNSUPPORTED_SHOULD_FORWARD_PROP_WARNING as l, fileExports as n, resolveBarrelReExport as o, fileImportsFrom as r, resolveBarrelReExportBinding as s, walkAst as t };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as CollectedWarning, c as MarkerFileContext, l as defineAdapter, n as TransformMode, o as AdapterInput, s as ImportSource } from "./transform-types-
|
|
1
|
+
import { a as CollectedWarning, c as MarkerFileContext, l as defineAdapter, n as TransformMode, o as AdapterInput, s as ImportSource } from "./transform-types-CHRHLCj_.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/run.d.ts
|
|
4
4
|
interface RunTransformOptions {
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { c as describeValue, i as defineAdapter, n as mergeMarkerDeclarations, s as assertValidAdapterInput, t as transformedComponentAcceptsSx } from "./sx-surface-BzqO3hcC.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { c as Logger, o as resolveBarrelReExport, t as walkAst } from "./ast-walk-DigTJqU7.mjs";
|
|
3
|
+
import { i as extractStyledDefBasesFromSource, t as createPrepassParser } from "./prepass-parser-CwdDzSgx.mjs";
|
|
4
4
|
import { r as toRealPath } from "./path-utils-BC4U8X_q.mjs";
|
|
5
5
|
import jscodeshift from "jscodeshift";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -8,6 +8,162 @@ import { dirname, join, resolve } from "node:path";
|
|
|
8
8
|
import { existsSync, readFileSync } from "node:fs";
|
|
9
9
|
import { glob, readFile, writeFile } from "node:fs/promises";
|
|
10
10
|
import { spawn } from "node:child_process";
|
|
11
|
+
//#region src/internal/prepass/resolve-static-members.ts
|
|
12
|
+
/**
|
|
13
|
+
* Resolves the underlying component name(s) a static member access like `Select.Option` refers to,
|
|
14
|
+
* by structurally walking the defining module's AST.
|
|
15
|
+
*
|
|
16
|
+
* This replaces an earlier regex-over-source approach: regexes could not reliably skip type
|
|
17
|
+
* annotations, stop at the right `;`, or distinguish identifiers from type names. We parse once
|
|
18
|
+
* (cached per source) with the shared prepass parser and answer member lookups against real nodes.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Given the module `source`, the candidate root component names a local binding resolves to, and a
|
|
22
|
+
* static member path (`["Option"]` for `Select.Option`), returns every component name the member
|
|
23
|
+
* path can statically resolve to, plus the final member name as a fallback (matching the prior
|
|
24
|
+
* behavior so downstream metadata lookups still have something to try).
|
|
25
|
+
*/
|
|
26
|
+
function resolveStaticMemberComponentNames(source, rootNames, memberPath, parserName = "tsx") {
|
|
27
|
+
const program = parseProgram(source, parserName);
|
|
28
|
+
const fallbackMember = memberPath[memberPath.length - 1];
|
|
29
|
+
const fallback = fallbackMember ? [fallbackMember] : [];
|
|
30
|
+
if (!program) return [...new Set([...rootNames, ...fallback])];
|
|
31
|
+
const index = buildModuleIndex(program);
|
|
32
|
+
let owners = expandStaticComponentOwners(index, rootNames);
|
|
33
|
+
for (const memberName of memberPath) {
|
|
34
|
+
const nextOwners = /* @__PURE__ */ new Set();
|
|
35
|
+
for (const ownerName of owners) for (const target of findStaticMemberTargets(index, ownerName, memberName)) nextOwners.add(target);
|
|
36
|
+
owners = nextOwners;
|
|
37
|
+
if (owners.size === 0) break;
|
|
38
|
+
}
|
|
39
|
+
return [...new Set([...owners, ...fallback])];
|
|
40
|
+
}
|
|
41
|
+
const programCache = /* @__PURE__ */ new Map();
|
|
42
|
+
function parseProgram(source, parserName) {
|
|
43
|
+
const cached = programCache.get(source);
|
|
44
|
+
if (cached !== void 0) return cached;
|
|
45
|
+
const parsed = tryParse(source, parserName);
|
|
46
|
+
programCache.set(source, parsed);
|
|
47
|
+
return parsed;
|
|
48
|
+
}
|
|
49
|
+
function tryParse(source, parserName) {
|
|
50
|
+
for (const name of parserName === "tsx" ? ["tsx", "ts"] : [parserName]) try {
|
|
51
|
+
const ast = createPrepassParser(name).parse(source);
|
|
52
|
+
return ast.program ?? ast;
|
|
53
|
+
} catch {}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
function buildModuleIndex(program) {
|
|
57
|
+
const initializers = /* @__PURE__ */ new Map();
|
|
58
|
+
const memberAssignments = /* @__PURE__ */ new Map();
|
|
59
|
+
walkAst(program, (node) => {
|
|
60
|
+
if (node.type === "VariableDeclarator") {
|
|
61
|
+
const name = identifierName(node.id);
|
|
62
|
+
const init = node.init;
|
|
63
|
+
if (name && init) initializers.set(name, init);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (node.type === "AssignmentExpression" && node.operator === "=") {
|
|
67
|
+
const target = capitalizedIdentifierName(node.right);
|
|
68
|
+
const member = staticMemberAccess(node.left);
|
|
69
|
+
if (target && member) addMemberTarget(memberAssignments, member.object, member.property, target);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
initializers,
|
|
74
|
+
memberAssignments
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Expands the root binding names to every capitalized identifier transitively referenced from their
|
|
79
|
+
* initializers (e.g. `const X = cond ? A : B` pulls in `A` and `B`). Mirrors the breadth the member
|
|
80
|
+
* lookup needs without assuming a particular declaration shape.
|
|
81
|
+
*/
|
|
82
|
+
function expandStaticComponentOwners(index, rootNames) {
|
|
83
|
+
const owners = new Set(rootNames);
|
|
84
|
+
const visit = (name) => {
|
|
85
|
+
const init = index.initializers.get(name);
|
|
86
|
+
if (!init) return;
|
|
87
|
+
for (const referenced of collectCapitalizedIdentifiers(init)) if (!owners.has(referenced)) {
|
|
88
|
+
owners.add(referenced);
|
|
89
|
+
visit(referenced);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
for (const name of rootNames) visit(name);
|
|
93
|
+
return owners;
|
|
94
|
+
}
|
|
95
|
+
function findStaticMemberTargets(index, ownerName, memberName) {
|
|
96
|
+
const targets = new Set(index.memberAssignments.get(ownerName)?.get(memberName) ?? []);
|
|
97
|
+
const init = index.initializers.get(ownerName);
|
|
98
|
+
for (const objectLiteral of objectLiteralsFromInitializer(index, init)) collectMemberTargetsFromObjectLiteral(objectLiteral, memberName, targets);
|
|
99
|
+
return targets;
|
|
100
|
+
}
|
|
101
|
+
/** Object expressions an owner's value is built from: a direct literal or `Object.assign(...)` args. */
|
|
102
|
+
function objectLiteralsFromInitializer(index, init) {
|
|
103
|
+
if (!init) return [];
|
|
104
|
+
if (init.type === "ObjectExpression") return [init];
|
|
105
|
+
if (isObjectAssignCall(init)) {
|
|
106
|
+
const literals = [];
|
|
107
|
+
for (const arg of init.arguments ?? []) if (arg.type === "ObjectExpression") literals.push(arg);
|
|
108
|
+
else {
|
|
109
|
+
const referenced = identifierName(arg);
|
|
110
|
+
const referencedInit = referenced ? index.initializers.get(referenced) : void 0;
|
|
111
|
+
if (referencedInit?.type === "ObjectExpression") literals.push(referencedInit);
|
|
112
|
+
}
|
|
113
|
+
return literals;
|
|
114
|
+
}
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
function collectMemberTargetsFromObjectLiteral(objectLiteral, memberName, targets) {
|
|
118
|
+
for (const property of objectLiteral.properties ?? []) {
|
|
119
|
+
if (property.type !== "ObjectProperty" && property.type !== "Property") continue;
|
|
120
|
+
if (identifierName(property.key) !== memberName) continue;
|
|
121
|
+
const target = capitalizedIdentifierName(property.value);
|
|
122
|
+
if (target) targets.add(target);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function isObjectAssignCall(node) {
|
|
126
|
+
if (node.type !== "CallExpression") return false;
|
|
127
|
+
const member = staticMemberAccess(node.callee);
|
|
128
|
+
return member?.object === "Object" && member.property === "assign";
|
|
129
|
+
}
|
|
130
|
+
function collectCapitalizedIdentifiers(node) {
|
|
131
|
+
const names = /* @__PURE__ */ new Set();
|
|
132
|
+
walkAst(node, (child) => {
|
|
133
|
+
if (child.type === "Identifier" && /^[A-Z]/.test(String(child.name))) names.add(String(child.name));
|
|
134
|
+
});
|
|
135
|
+
return names;
|
|
136
|
+
}
|
|
137
|
+
function staticMemberAccess(node) {
|
|
138
|
+
if (!node || node.type !== "MemberExpression" && node.type !== "OptionalMemberExpression" || node.computed === true) return null;
|
|
139
|
+
const object = identifierName(node.object);
|
|
140
|
+
const property = identifierName(node.property);
|
|
141
|
+
return object && property ? {
|
|
142
|
+
object,
|
|
143
|
+
property
|
|
144
|
+
} : null;
|
|
145
|
+
}
|
|
146
|
+
function addMemberTarget(memberAssignments, object, property, target) {
|
|
147
|
+
let byMember = memberAssignments.get(object);
|
|
148
|
+
if (!byMember) {
|
|
149
|
+
byMember = /* @__PURE__ */ new Map();
|
|
150
|
+
memberAssignments.set(object, byMember);
|
|
151
|
+
}
|
|
152
|
+
let targets = byMember.get(property);
|
|
153
|
+
if (!targets) {
|
|
154
|
+
targets = /* @__PURE__ */ new Set();
|
|
155
|
+
byMember.set(property, targets);
|
|
156
|
+
}
|
|
157
|
+
targets.add(target);
|
|
158
|
+
}
|
|
159
|
+
function identifierName(node) {
|
|
160
|
+
return node?.type === "Identifier" && typeof node.name === "string" ? node.name : null;
|
|
161
|
+
}
|
|
162
|
+
function capitalizedIdentifierName(node) {
|
|
163
|
+
const name = identifierName(node);
|
|
164
|
+
return name && /^[A-Z]/.test(name) ? name : null;
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
11
167
|
//#region src/run.ts
|
|
12
168
|
/**
|
|
13
169
|
* Runs the codemod over input files with an adapter.
|
|
@@ -149,7 +305,7 @@ async function runTransform(options) {
|
|
|
149
305
|
const { createModuleResolver } = await import("./resolve-imports-DgSAddIF.mjs").then((n) => n.n);
|
|
150
306
|
const sharedResolver = createModuleResolver();
|
|
151
307
|
filePaths = orderFilesByLocalImportDependencies(filePaths, sharedResolver, toRealPath);
|
|
152
|
-
const { runPrepass } = await import("./run-prepass-
|
|
308
|
+
const { runPrepass } = await import("./run-prepass-BWQCThfh.mjs");
|
|
153
309
|
const absoluteFiles = filePaths.map((f) => resolve(f));
|
|
154
310
|
const absoluteConsumers = consumerFilePaths.map((f) => resolve(f));
|
|
155
311
|
let prepassResult;
|
|
@@ -249,12 +405,15 @@ async function runTransform(options) {
|
|
|
249
405
|
if (!resolvedImport) return;
|
|
250
406
|
const resolvedPath = toRealPath(resolvedImport);
|
|
251
407
|
const definitionSourcePath = resolveExistingSourcePath(resolveBarrelReExport(resolvedPath, ctx.importedName, prepassResolve, cachedRead) ?? resolvedPath);
|
|
252
|
-
const
|
|
408
|
+
const memberPath = ctx.memberPath ?? [];
|
|
409
|
+
const autoInterfaceNames = memberPath.length > 0 ? [ctx.localName, memberPath[memberPath.length - 1]] : ctx.importedName === "default" ? [ctx.localName, ctx.importedName] : [ctx.importedName];
|
|
253
410
|
const styledDefinitionNames = getStyledDefinitionNames(definitionSourcePath);
|
|
254
|
-
const
|
|
411
|
+
const rootSourceComponentNames = ctx.importedName === "default" ? [ctx.localName, getDefaultExportedName(definitionSourcePath)].filter((name) => typeof name === "string") : [ctx.importedName];
|
|
412
|
+
const sourceComponentNames = memberPath.length > 0 ? resolveStaticMemberComponentNames(cachedRead(definitionSourcePath), rootSourceComponentNames, memberPath) : rootSourceComponentNames;
|
|
255
413
|
const typedComponent = findTypedComponentMetadata(prepassResult.typeScriptMetadata, definitionSourcePath, sourceComponentNames);
|
|
256
414
|
if (typedComponent?.supportsSxProp === true) return {
|
|
257
415
|
acceptsSx: true,
|
|
416
|
+
...typedComponent.sxTarget ? { sxTarget: typedComponent.sxTarget } : {},
|
|
258
417
|
sxExcludedProperties: typedComponent.sxExcludedProperties,
|
|
259
418
|
sxAllowedProperties: typedComponent.sxAllowedProperties
|
|
260
419
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as findImportSource, s as resolveBarrelReExportBinding } from "./ast-walk-DigTJqU7.mjs";
|
|
2
|
+
import { parse } from "@babel/parser";
|
|
2
3
|
//#region src/internal/prepass/compute-leaf-set.ts
|
|
3
4
|
/**
|
|
4
5
|
* Computes which styled-component bindings are "leaves" for leaves-only mode:
|
|
@@ -239,4 +240,101 @@ function findDefaultExportedLocalName(source) {
|
|
|
239
240
|
return source.match(/\bexport\s+default\s+([A-Z][A-Za-z0-9]*)\b/)?.[1] ?? source.match(/\bexport\s*\{[^}]*\b([A-Z][A-Za-z0-9]*)\s+as\s+default\b[^}]*\}/)?.[1];
|
|
240
241
|
}
|
|
241
242
|
//#endregion
|
|
242
|
-
|
|
243
|
+
//#region src/internal/prepass/prepass-parser.ts
|
|
244
|
+
/**
|
|
245
|
+
* Shared babel parser for prepass modules.
|
|
246
|
+
*
|
|
247
|
+
* Uses @babel/parser directly with `tokens: false` for ~35% faster parsing
|
|
248
|
+
* compared to jscodeshift's getParser (which enables token generation).
|
|
249
|
+
*
|
|
250
|
+
* Both scan-cross-file-selectors and extract-external-interface can share this parser
|
|
251
|
+
* to avoid duplicate parser initialization.
|
|
252
|
+
*/
|
|
253
|
+
/**
|
|
254
|
+
* Create a babel parser with tokens disabled, matching jscodeshift's plugin set
|
|
255
|
+
* for the given parser name.
|
|
256
|
+
*
|
|
257
|
+
* - `tsx` / `ts`: TypeScript plugins (tsx also includes JSX)
|
|
258
|
+
* - `babel` / `babylon` / `flow`: Flow plugins + JSX
|
|
259
|
+
*
|
|
260
|
+
* Note: jscodeshift's `flow` parser uses the `flow-parser` package (not babel).
|
|
261
|
+
* We always use `@babel/parser` with the `flow` plugin instead, since it produces
|
|
262
|
+
* the same AST node types (ImportDeclaration, TaggedTemplateExpression) that the
|
|
263
|
+
* prepass walks, and avoids an extra parser dependency.
|
|
264
|
+
*/
|
|
265
|
+
function createPrepassParser(parserName = "tsx") {
|
|
266
|
+
const options = parserName === "ts" || parserName === "tsx" ? buildOptions(TS_PLUGINS, parserName === "tsx") : buildOptions(FLOW_PLUGINS, true);
|
|
267
|
+
return { parse(source) {
|
|
268
|
+
return parse(source, options);
|
|
269
|
+
} };
|
|
270
|
+
}
|
|
271
|
+
function buildOptions(plugins, includeJsx) {
|
|
272
|
+
return {
|
|
273
|
+
sourceType: "module",
|
|
274
|
+
allowImportExportEverywhere: true,
|
|
275
|
+
allowReturnOutsideFunction: true,
|
|
276
|
+
startLine: 1,
|
|
277
|
+
tokens: false,
|
|
278
|
+
plugins: includeJsx ? ["jsx", ...plugins] : plugins
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Plugins for TypeScript parsers (ts, tsx).
|
|
283
|
+
* Same as jscodeshift's tsOptions.plugins minus "jsx" (added conditionally).
|
|
284
|
+
*/
|
|
285
|
+
const TS_PLUGINS = [
|
|
286
|
+
"asyncGenerators",
|
|
287
|
+
"decoratorAutoAccessors",
|
|
288
|
+
"bigInt",
|
|
289
|
+
"classPrivateMethods",
|
|
290
|
+
"classPrivateProperties",
|
|
291
|
+
"classProperties",
|
|
292
|
+
"decorators-legacy",
|
|
293
|
+
"doExpressions",
|
|
294
|
+
"dynamicImport",
|
|
295
|
+
"exportDefaultFrom",
|
|
296
|
+
"exportNamespaceFrom",
|
|
297
|
+
"functionBind",
|
|
298
|
+
"functionSent",
|
|
299
|
+
"importAttributes",
|
|
300
|
+
"importMeta",
|
|
301
|
+
"nullishCoalescingOperator",
|
|
302
|
+
"numericSeparator",
|
|
303
|
+
"objectRestSpread",
|
|
304
|
+
"optionalCatchBinding",
|
|
305
|
+
"optionalChaining",
|
|
306
|
+
["pipelineOperator", { proposal: "minimal" }],
|
|
307
|
+
"throwExpressions",
|
|
308
|
+
"typescript"
|
|
309
|
+
];
|
|
310
|
+
/**
|
|
311
|
+
* Plugins for Flow/Babylon parsers (babel, babylon, flow).
|
|
312
|
+
* Same as jscodeshift's babylon parser plugins minus "jsx" (added conditionally).
|
|
313
|
+
*/
|
|
314
|
+
const FLOW_PLUGINS = [
|
|
315
|
+
["flow", { all: true }],
|
|
316
|
+
"flowComments",
|
|
317
|
+
"asyncGenerators",
|
|
318
|
+
"bigInt",
|
|
319
|
+
"classProperties",
|
|
320
|
+
"classPrivateProperties",
|
|
321
|
+
"classPrivateMethods",
|
|
322
|
+
["decorators", { decoratorsBeforeExport: false }],
|
|
323
|
+
"doExpressions",
|
|
324
|
+
"dynamicImport",
|
|
325
|
+
"exportDefaultFrom",
|
|
326
|
+
"exportNamespaceFrom",
|
|
327
|
+
"functionBind",
|
|
328
|
+
"functionSent",
|
|
329
|
+
"importMeta",
|
|
330
|
+
"logicalAssignment",
|
|
331
|
+
"nullishCoalescingOperator",
|
|
332
|
+
"numericSeparator",
|
|
333
|
+
"objectRestSpread",
|
|
334
|
+
"optionalCatchBinding",
|
|
335
|
+
"optionalChaining",
|
|
336
|
+
["pipelineOperator", { proposal: "minimal" }],
|
|
337
|
+
"throwExpressions"
|
|
338
|
+
];
|
|
339
|
+
//#endregion
|
|
340
|
+
export { extractStyledDefBasesFromSource as i, computeGlobalLeafKeys as n, extractStyledDefBasesFromAstProgram as r, createPrepassParser as t };
|
|
@@ -61,5 +61,19 @@ function mergeComponentPropUsage(info, usage) {
|
|
|
61
61
|
if (!propInfo.values.some((existing) => existing === value.value)) propInfo.values.push(value.value);
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Formats a `prop === value` JS condition for an observed static variant bucket,
|
|
66
|
+
* quoting strings and emitting numbers bare. Shared by every observed-variant emitter.
|
|
67
|
+
*/
|
|
68
|
+
function formatObservedVariantCondition(propName, value) {
|
|
69
|
+
return `${propName} === ${typeof value === "number" ? String(value) : JSON.stringify(value)}`;
|
|
70
|
+
}
|
|
71
|
+
function getExhaustiveObservedStaticValues(info, propName) {
|
|
72
|
+
const propUsage = info?.props[propName];
|
|
73
|
+
if (!info || info.hasUnknownUsage || !propUsage || propUsage.hasUnknown) return null;
|
|
74
|
+
if (propUsage.values.length < 1) return null;
|
|
75
|
+
const values = propUsage.values.filter((value) => typeof value === "string" || typeof value === "number");
|
|
76
|
+
return values.length === propUsage.values.length ? values : null;
|
|
77
|
+
}
|
|
64
78
|
//#endregion
|
|
65
|
-
export {
|
|
79
|
+
export { mergeComponentPropUsage as a, getExhaustiveObservedStaticValues as i, createComponentPropUsageInfo as n, readStaticJsxLiteral as o, formatObservedVariantCondition as r, KNOWN_NON_ELEMENT_PROPS as t };
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as
|
|
1
|
+
import { c as Logger, i as findImportSource, n as fileExports, o as resolveBarrelReExport, r as fileImportsFrom, s as resolveBarrelReExportBinding, t as walkAst } from "./ast-walk-DigTJqU7.mjs";
|
|
2
|
+
import { i as extractStyledDefBasesFromSource, n as computeGlobalLeafKeys, r as extractStyledDefBasesFromAstProgram, t as createPrepassParser } from "./prepass-parser-CwdDzSgx.mjs";
|
|
3
3
|
import { r as escapeRegex } from "./string-utils-DD9wdRHW.mjs";
|
|
4
4
|
import { n as isTemplatePlaceholderInSelectorContext, r as PLACEHOLDER_RE, t as isSelectorContext } from "./selector-context-heuristic-LVizWWOR.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { a as mergeComponentPropUsage, n as createComponentPropUsageInfo, o as readStaticJsxLiteral, t as KNOWN_NON_ELEMENT_PROPS } from "./prop-usage-QtOSsKTr.mjs";
|
|
6
6
|
import { relative, resolve } from "node:path";
|
|
7
7
|
import { readFileSync, realpathSync } from "node:fs";
|
|
8
8
|
import { execSync } from "node:child_process";
|
|
9
9
|
import { createHash } from "node:crypto";
|
|
10
|
-
import { parse } from "@babel/parser";
|
|
11
10
|
//#region src/internal/utilities/collection-utils.ts
|
|
12
11
|
/** Add a value to a Set stored in a Map, creating the Set if it doesn't exist. */
|
|
13
12
|
function addToSetMap(map, key, value) {
|
|
@@ -19,103 +18,6 @@ function addToSetMap(map, key, value) {
|
|
|
19
18
|
set.add(value);
|
|
20
19
|
}
|
|
21
20
|
//#endregion
|
|
22
|
-
//#region src/internal/prepass/prepass-parser.ts
|
|
23
|
-
/**
|
|
24
|
-
* Shared babel parser for prepass modules.
|
|
25
|
-
*
|
|
26
|
-
* Uses @babel/parser directly with `tokens: false` for ~35% faster parsing
|
|
27
|
-
* compared to jscodeshift's getParser (which enables token generation).
|
|
28
|
-
*
|
|
29
|
-
* Both scan-cross-file-selectors and extract-external-interface can share this parser
|
|
30
|
-
* to avoid duplicate parser initialization.
|
|
31
|
-
*/
|
|
32
|
-
/**
|
|
33
|
-
* Create a babel parser with tokens disabled, matching jscodeshift's plugin set
|
|
34
|
-
* for the given parser name.
|
|
35
|
-
*
|
|
36
|
-
* - `tsx` / `ts`: TypeScript plugins (tsx also includes JSX)
|
|
37
|
-
* - `babel` / `babylon` / `flow`: Flow plugins + JSX
|
|
38
|
-
*
|
|
39
|
-
* Note: jscodeshift's `flow` parser uses the `flow-parser` package (not babel).
|
|
40
|
-
* We always use `@babel/parser` with the `flow` plugin instead, since it produces
|
|
41
|
-
* the same AST node types (ImportDeclaration, TaggedTemplateExpression) that the
|
|
42
|
-
* prepass walks, and avoids an extra parser dependency.
|
|
43
|
-
*/
|
|
44
|
-
function createPrepassParser(parserName = "tsx") {
|
|
45
|
-
const options = parserName === "ts" || parserName === "tsx" ? buildOptions(TS_PLUGINS, parserName === "tsx") : buildOptions(FLOW_PLUGINS, true);
|
|
46
|
-
return { parse(source) {
|
|
47
|
-
return parse(source, options);
|
|
48
|
-
} };
|
|
49
|
-
}
|
|
50
|
-
function buildOptions(plugins, includeJsx) {
|
|
51
|
-
return {
|
|
52
|
-
sourceType: "module",
|
|
53
|
-
allowImportExportEverywhere: true,
|
|
54
|
-
allowReturnOutsideFunction: true,
|
|
55
|
-
startLine: 1,
|
|
56
|
-
tokens: false,
|
|
57
|
-
plugins: includeJsx ? ["jsx", ...plugins] : plugins
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Plugins for TypeScript parsers (ts, tsx).
|
|
62
|
-
* Same as jscodeshift's tsOptions.plugins minus "jsx" (added conditionally).
|
|
63
|
-
*/
|
|
64
|
-
const TS_PLUGINS = [
|
|
65
|
-
"asyncGenerators",
|
|
66
|
-
"decoratorAutoAccessors",
|
|
67
|
-
"bigInt",
|
|
68
|
-
"classPrivateMethods",
|
|
69
|
-
"classPrivateProperties",
|
|
70
|
-
"classProperties",
|
|
71
|
-
"decorators-legacy",
|
|
72
|
-
"doExpressions",
|
|
73
|
-
"dynamicImport",
|
|
74
|
-
"exportDefaultFrom",
|
|
75
|
-
"exportNamespaceFrom",
|
|
76
|
-
"functionBind",
|
|
77
|
-
"functionSent",
|
|
78
|
-
"importAttributes",
|
|
79
|
-
"importMeta",
|
|
80
|
-
"nullishCoalescingOperator",
|
|
81
|
-
"numericSeparator",
|
|
82
|
-
"objectRestSpread",
|
|
83
|
-
"optionalCatchBinding",
|
|
84
|
-
"optionalChaining",
|
|
85
|
-
["pipelineOperator", { proposal: "minimal" }],
|
|
86
|
-
"throwExpressions",
|
|
87
|
-
"typescript"
|
|
88
|
-
];
|
|
89
|
-
/**
|
|
90
|
-
* Plugins for Flow/Babylon parsers (babel, babylon, flow).
|
|
91
|
-
* Same as jscodeshift's babylon parser plugins minus "jsx" (added conditionally).
|
|
92
|
-
*/
|
|
93
|
-
const FLOW_PLUGINS = [
|
|
94
|
-
["flow", { all: true }],
|
|
95
|
-
"flowComments",
|
|
96
|
-
"asyncGenerators",
|
|
97
|
-
"bigInt",
|
|
98
|
-
"classProperties",
|
|
99
|
-
"classPrivateProperties",
|
|
100
|
-
"classPrivateMethods",
|
|
101
|
-
["decorators", { decoratorsBeforeExport: false }],
|
|
102
|
-
"doExpressions",
|
|
103
|
-
"dynamicImport",
|
|
104
|
-
"exportDefaultFrom",
|
|
105
|
-
"exportNamespaceFrom",
|
|
106
|
-
"functionBind",
|
|
107
|
-
"functionSent",
|
|
108
|
-
"importMeta",
|
|
109
|
-
"logicalAssignment",
|
|
110
|
-
"nullishCoalescingOperator",
|
|
111
|
-
"numericSeparator",
|
|
112
|
-
"objectRestSpread",
|
|
113
|
-
"optionalCatchBinding",
|
|
114
|
-
"optionalChaining",
|
|
115
|
-
["pipelineOperator", { proposal: "minimal" }],
|
|
116
|
-
"throwExpressions"
|
|
117
|
-
];
|
|
118
|
-
//#endregion
|
|
119
21
|
//#region src/internal/prepass/scan-cross-file-selectors.ts
|
|
120
22
|
/**
|
|
121
23
|
* Pre-filter: matches any bare `${Identifier}` template expression.
|
|
@@ -750,7 +652,7 @@ function isTypeScriptParser(parserName) {
|
|
|
750
652
|
}
|
|
751
653
|
async function loadTypeScriptAnalysis() {
|
|
752
654
|
try {
|
|
753
|
-
return await import("./typescript-analysis-
|
|
655
|
+
return await import("./typescript-analysis-CmIDxfLv.mjs");
|
|
754
656
|
} catch (err) {
|
|
755
657
|
const message = err instanceof Error ? err.message : String(err);
|
|
756
658
|
if (message.includes("typescript") && (message.includes("Cannot find") || message.includes("ERR_MODULE_NOT_FOUND"))) throw new Error(["TypeScript parser runs require the optional `typescript` package for compiler metadata.", "Install TypeScript in the project (supported range: >=5.0.0 <6), or use a non-TypeScript parser."].join("\n"));
|
|
@@ -883,14 +785,16 @@ function scanConsumerJsxUsages(filePath, source, componentNames, parser) {
|
|
|
883
785
|
}
|
|
884
786
|
const importNodes = [];
|
|
885
787
|
const jsxOpenings = [];
|
|
886
|
-
|
|
788
|
+
const program = ast.program ?? ast;
|
|
789
|
+
walkForImportsAndJsxOpenings(program, importNodes, jsxOpenings);
|
|
887
790
|
const importMap = buildImportMapFromNodes(importNodes);
|
|
791
|
+
const staticIdentifierValues = collectStaticIdentifierValues(program);
|
|
888
792
|
const resultMap = /* @__PURE__ */ new Map();
|
|
889
793
|
const staticPropUsages = [];
|
|
890
794
|
for (const opening of jsxOpenings) {
|
|
891
795
|
const resolvedComponent = resolveJsxOpeningComponent(opening.name, importMap, componentNames);
|
|
892
796
|
if (!resolvedComponent) continue;
|
|
893
|
-
const openingUsage = readConsumerOpeningUsage(opening);
|
|
797
|
+
const openingUsage = readConsumerOpeningUsage(opening, staticIdentifierValues);
|
|
894
798
|
const { externalProps } = openingUsage;
|
|
895
799
|
if (externalProps.className || externalProps.style || externalProps.spreadProps || externalProps.elementProps) {
|
|
896
800
|
const key = `${resolvedComponent.name}\0${resolvedComponent.importSource ?? ""}`;
|
|
@@ -941,7 +845,7 @@ function resolveJsxOpeningComponent(name, importMap, componentNames) {
|
|
|
941
845
|
importSource: importEntry.source
|
|
942
846
|
} : void 0;
|
|
943
847
|
}
|
|
944
|
-
function readConsumerOpeningUsage(opening) {
|
|
848
|
+
function readConsumerOpeningUsage(opening, staticIdentifierValues) {
|
|
945
849
|
const externalProps = {
|
|
946
850
|
className: false,
|
|
947
851
|
style: false,
|
|
@@ -964,7 +868,7 @@ function readConsumerOpeningUsage(opening) {
|
|
|
964
868
|
else if (propName === "style") externalProps.style = true;
|
|
965
869
|
else if (isElementConsumerProp(propName)) externalProps.elementProps = true;
|
|
966
870
|
if (!KNOWN_NON_ELEMENT_PROPS.has(propName)) {
|
|
967
|
-
const value = readStaticJsxLiteral(attr);
|
|
871
|
+
const value = readStaticJsxLiteral(attr) ?? readStaticIdentifierJsxValue(attr, staticIdentifierValues);
|
|
968
872
|
props[propName] = value === void 0 ? { kind: "unknown" } : {
|
|
969
873
|
kind: "static",
|
|
970
874
|
value
|
|
@@ -979,6 +883,113 @@ function readConsumerOpeningUsage(opening) {
|
|
|
979
883
|
}
|
|
980
884
|
};
|
|
981
885
|
}
|
|
886
|
+
function collectStaticIdentifierValues(program) {
|
|
887
|
+
const values = /* @__PURE__ */ new Map();
|
|
888
|
+
for (const declaration of moduleLevelVariableDeclarations(program)) {
|
|
889
|
+
if (declaration.kind !== "const") continue;
|
|
890
|
+
const declarators = declaration.declarations ?? [];
|
|
891
|
+
for (const decl of declarators) {
|
|
892
|
+
const name = decl.id?.type === "Identifier" ? decl.id.name : null;
|
|
893
|
+
const value = staticValueFromNode(decl.init);
|
|
894
|
+
if (name && value !== void 0) values.set(name, value);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
if (values.size === 0) return values;
|
|
898
|
+
const bindingCounts = countBoundNames(program);
|
|
899
|
+
const shadowedNames = [];
|
|
900
|
+
for (const [name] of values) if ((bindingCounts.get(name) ?? 0) > 1) shadowedNames.push(name);
|
|
901
|
+
for (const name of shadowedNames) values.delete(name);
|
|
902
|
+
return values;
|
|
903
|
+
}
|
|
904
|
+
/** Top-level `const`/`let`/`var` declarations, unwrapping `export` declarations. */
|
|
905
|
+
function moduleLevelVariableDeclarations(program) {
|
|
906
|
+
const body = program.body ?? [];
|
|
907
|
+
const declarations = [];
|
|
908
|
+
for (const node of body) if (node.type === "VariableDeclaration") declarations.push(node);
|
|
909
|
+
else if (node.type === "ExportNamedDeclaration") {
|
|
910
|
+
const inner = node.declaration;
|
|
911
|
+
if (inner?.type === "VariableDeclaration") declarations.push(inner);
|
|
912
|
+
}
|
|
913
|
+
return declarations;
|
|
914
|
+
}
|
|
915
|
+
/** Counts how many times each name is introduced as a binding anywhere in the module. */
|
|
916
|
+
function countBoundNames(program) {
|
|
917
|
+
const counts = /* @__PURE__ */ new Map();
|
|
918
|
+
const bump = (name) => {
|
|
919
|
+
if (name) counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
920
|
+
};
|
|
921
|
+
const bumpId = (node) => {
|
|
922
|
+
if (node?.type === "Identifier") bump(node.name ?? "");
|
|
923
|
+
};
|
|
924
|
+
walkAst(program, (node) => {
|
|
925
|
+
switch (node.type) {
|
|
926
|
+
case "VariableDeclarator":
|
|
927
|
+
collectPatternNames(node.id, bump);
|
|
928
|
+
break;
|
|
929
|
+
case "FunctionDeclaration":
|
|
930
|
+
case "FunctionExpression":
|
|
931
|
+
case "ArrowFunctionExpression":
|
|
932
|
+
bumpId(node.id);
|
|
933
|
+
for (const param of node.params ?? []) collectPatternNames(param, bump);
|
|
934
|
+
break;
|
|
935
|
+
case "ClassDeclaration":
|
|
936
|
+
case "ClassExpression":
|
|
937
|
+
bumpId(node.id);
|
|
938
|
+
break;
|
|
939
|
+
case "CatchClause":
|
|
940
|
+
collectPatternNames(node.param, bump);
|
|
941
|
+
break;
|
|
942
|
+
case "ImportSpecifier":
|
|
943
|
+
case "ImportDefaultSpecifier":
|
|
944
|
+
case "ImportNamespaceSpecifier":
|
|
945
|
+
bumpId(node.local);
|
|
946
|
+
break;
|
|
947
|
+
}
|
|
948
|
+
});
|
|
949
|
+
return counts;
|
|
950
|
+
}
|
|
951
|
+
/** Collects every identifier name bound by a (possibly destructured) binding pattern. */
|
|
952
|
+
function collectPatternNames(node, add) {
|
|
953
|
+
if (!node) return;
|
|
954
|
+
switch (node.type) {
|
|
955
|
+
case "Identifier":
|
|
956
|
+
add(node.name ?? "");
|
|
957
|
+
break;
|
|
958
|
+
case "ObjectPattern":
|
|
959
|
+
for (const property of node.properties ?? []) if (property.type === "RestElement") collectPatternNames(property.argument, add);
|
|
960
|
+
else collectPatternNames(property.value, add);
|
|
961
|
+
break;
|
|
962
|
+
case "ArrayPattern":
|
|
963
|
+
for (const element of node.elements ?? []) collectPatternNames(element ?? void 0, add);
|
|
964
|
+
break;
|
|
965
|
+
case "AssignmentPattern":
|
|
966
|
+
collectPatternNames(node.left, add);
|
|
967
|
+
break;
|
|
968
|
+
case "RestElement":
|
|
969
|
+
collectPatternNames(node.argument, add);
|
|
970
|
+
break;
|
|
971
|
+
case "TSParameterProperty":
|
|
972
|
+
collectPatternNames(node.parameter, add);
|
|
973
|
+
break;
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
function readStaticIdentifierJsxValue(attr, staticIdentifierValues) {
|
|
977
|
+
const value = attr.value;
|
|
978
|
+
const expr = value?.type === "JSXExpressionContainer" ? value.expression : null;
|
|
979
|
+
const name = expr?.type === "Identifier" ? expr.name : null;
|
|
980
|
+
return name ? staticIdentifierValues.get(name) : void 0;
|
|
981
|
+
}
|
|
982
|
+
const STATIC_LITERAL_NODE_TYPES = new Set([
|
|
983
|
+
"Literal",
|
|
984
|
+
"StringLiteral",
|
|
985
|
+
"NumericLiteral",
|
|
986
|
+
"BooleanLiteral"
|
|
987
|
+
]);
|
|
988
|
+
function staticValueFromNode(node) {
|
|
989
|
+
if (!node || !STATIC_LITERAL_NODE_TYPES.has(node.type)) return;
|
|
990
|
+
const value = node.value;
|
|
991
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? value : void 0;
|
|
992
|
+
}
|
|
982
993
|
function isElementConsumerProp(propName) {
|
|
983
994
|
return !KNOWN_NON_ELEMENT_PROPS.has(propName) && !propName.startsWith("$");
|
|
984
995
|
}
|