styled-components-to-stylex-codemod 0.0.56 → 0.0.58
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/README.md +1 -1
- package/dist/{ast-walk-CLvMH7Lm.mjs → ast-walk-CWKn9bmi.mjs} +82 -30
- package/dist/{bridge-consumer-patcher-B__X3jOg.mjs → bridge-consumer-patcher-jeeDUlId.mjs} +2 -2
- package/dist/{forwarded-as-consumer-patcher-Bs9ymhBa.mjs → forwarded-as-consumer-patcher-Do4PI4Qs.mjs} +2 -2
- package/dist/index.d.mts +1 -12
- package/dist/index.mjs +180 -28
- package/dist/{transform-types-BGGNjb8R.d.mts → logger-B9VD--XI.d.mts} +2 -199
- package/dist/{prop-usage-z-bcXTOD.mjs → prop-usage-Dkz3W7gJ.mjs} +118 -98
- package/dist/{run-prepass-BueJvYyf.mjs → run-prepass-QjRV8IcO.mjs} +29 -102
- package/dist/{sx-surface-_Hjc6ZDq.mjs → sx-surface-Kv8zK8L4.mjs} +32 -2
- package/dist/transform.d.mts +180 -2
- package/dist/transform.mjs +4396 -3946
- package/dist/{transient-prop-consumer-patcher-BDruM1OI.mjs → transient-prop-consumer-patcher-D-iqO8-T.mjs} +2 -2
- package/dist/{typescript-analysis-BzsnorIV.mjs → typescript-analysis-DZF-HSEM.mjs} +23 -51
- package/package.json +1 -1
- package/dist/compute-leaf-set-D5GvkV-H.mjs +0 -255
- /package/dist/{path-utils-ByFNVtHo.mjs → path-utils-BC4U8X_q.mjs} +0 -0
- /package/dist/{string-utils-4eeXGa48.mjs → string-utils-Bo3cWgss.mjs} +0 -0
package/README.md
CHANGED
|
@@ -384,7 +384,7 @@ await runTransform({
|
|
|
384
384
|
|
|
385
385
|
### Planning manual conversions
|
|
386
386
|
|
|
387
|
-
`analyzeMigrationPlan` runs the codemod in analysis-only mode (it never writes files) and returns the bottom-up ordered list of files you must convert by hand — the genuine blockers the codemod can't convert — each with its consumer count, the exports to convert,
|
|
387
|
+
`analyzeMigrationPlan` runs the codemod in analysis-only mode (it never writes files) and returns the bottom-up ordered list of files you must convert by hand — the genuine blockers the codemod can't convert — each with its consumer count, the exports to convert, direct auto-migration payoff, secondary blocker-chain context, and the bail reasons. `formatMigrationPlan` renders it as a report with direct unlocks emphasized first so raw chain involvement is not mistaken for files unlocked by one blocker alone.
|
|
388
388
|
|
|
389
389
|
```ts
|
|
390
390
|
import { analyzeMigrationPlan, formatMigrationPlan } from "styled-components-to-stylex-codemod";
|
|
@@ -1,7 +1,60 @@
|
|
|
1
1
|
import { n as isTemplatePlaceholderInSelectorContext, r as PLACEHOLDER_RE } from "./selector-context-heuristic-Dptd93Xe.mjs";
|
|
2
|
-
import path, { resolve } from "node:path";
|
|
2
|
+
import path, { relative, resolve } from "node:path";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { parse } from "@babel/parser";
|
|
5
|
+
//#region src/internal/utilities/ast-safety.ts
|
|
6
|
+
/**
|
|
7
|
+
* AST safety checks for null nodes and structural issues.
|
|
8
|
+
* Core concepts: recursive validation and error reporting.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* `JSON.stringify` replacer that drops noisy AST positional metadata
|
|
12
|
+
* (`loc`/`tokens`/`comments`/`start`/`end`) and replaces circular references
|
|
13
|
+
* with `"[Circular]"`, so AST nodes can be serialized for logging or as a
|
|
14
|
+
* stable structural key. Create a fresh replacer per `stringify` call — it
|
|
15
|
+
* keeps per-serialization state.
|
|
16
|
+
*/
|
|
17
|
+
function createAstSafeJsonReplacer() {
|
|
18
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
19
|
+
return (key, value) => {
|
|
20
|
+
if (key === "loc" || key === "tokens" || key === "comments" || key === "start" || key === "end") return;
|
|
21
|
+
if (value && typeof value === "object") {
|
|
22
|
+
if (seen.has(value)) return "[Circular]";
|
|
23
|
+
seen.add(value);
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** AST node types whose `elements` array legitimately contains `null` (holes/elisions). */
|
|
29
|
+
const ARRAY_ELEMENT_HOLE_TYPES = new Set(["ArrayPattern", "ArrayExpression"]);
|
|
30
|
+
function assertNoNullNodesInArrays(node) {
|
|
31
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
32
|
+
const visit = (cur, path, allowNulls) => {
|
|
33
|
+
if (cur === null || cur === void 0) return;
|
|
34
|
+
if (Array.isArray(cur)) {
|
|
35
|
+
for (let i = 0; i < cur.length; i++) {
|
|
36
|
+
if (cur[i] === null) {
|
|
37
|
+
if (!allowNulls) throw new Error(`Null AST node in array at ${path}[${i}]`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
visit(cur[i], `${path}[${i}]`, false);
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (typeof cur !== "object") return;
|
|
45
|
+
const curObj = cur;
|
|
46
|
+
if (seen.has(curObj)) return;
|
|
47
|
+
seen.add(curObj);
|
|
48
|
+
const nodeType = cur.type;
|
|
49
|
+
const hasValidElementHoles = typeof nodeType === "string" && ARRAY_ELEMENT_HOLE_TYPES.has(nodeType);
|
|
50
|
+
for (const [k, v] of Object.entries(cur)) {
|
|
51
|
+
if (v === null) continue;
|
|
52
|
+
if (typeof v === "object") visit(v, `${path}.${k}`, hasValidElementHoles && k === "elements");
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
visit(node, "root", false);
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
5
58
|
//#region src/internal/logger.ts
|
|
6
59
|
/**
|
|
7
60
|
* Logger and warning types for transform diagnostics.
|
|
@@ -9,6 +62,7 @@ import { parse } from "@babel/parser";
|
|
|
9
62
|
*/
|
|
10
63
|
const CASCADE_CONFLICT_WARNING = "styled(ImportedComponent) wraps a component whose file uses styled-components — convert the base component's file first to avoid CSS cascade conflicts";
|
|
11
64
|
const PARTIAL_MIGRATION_INCOMPLETE_WARNING = "Partial migration left styled-components declarations unconverted";
|
|
65
|
+
const PARTIAL_PRESERVED_ANCESTOR_REVEAL_WARNING = "Partial transform would leave a StyleX child reveal targeting a styled-components ancestor — the component-selector ancestor was not converted, so it cannot render the marker the child's stylex.when.ancestor() reveal needs; the child is preserved as styled-components to keep the reveal working";
|
|
12
66
|
const UNSUPPORTED_SHOULD_FORWARD_PROP_WARNING = "Unsupported shouldForwardProp pattern (only !prop.startsWith(), ![].includes(prop), and prop !== are supported)";
|
|
13
67
|
/**
|
|
14
68
|
* When fileCount <= this threshold, warnings are printed per-file inline and
|
|
@@ -132,20 +186,9 @@ var Logger = class Logger {
|
|
|
132
186
|
}
|
|
133
187
|
static formatContext(context) {
|
|
134
188
|
if (typeof context === "undefined") return null;
|
|
135
|
-
return JSON.stringify(context,
|
|
189
|
+
return JSON.stringify(context, createAstSafeJsonReplacer(), 2);
|
|
136
190
|
}
|
|
137
191
|
};
|
|
138
|
-
function createContextReplacer() {
|
|
139
|
-
const seen = /* @__PURE__ */ new WeakSet();
|
|
140
|
-
return (key, value) => {
|
|
141
|
-
if (key === "loc" || key === "tokens" || key === "comments" || key === "start" || key === "end") return;
|
|
142
|
-
if (value && typeof value === "object") {
|
|
143
|
-
if (seen.has(value)) return "[Circular]";
|
|
144
|
-
seen.add(value);
|
|
145
|
-
}
|
|
146
|
-
return value;
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
192
|
const MAX_DEPENDED_FILE_GROUPS = 15;
|
|
150
193
|
var LoggerReport = class {
|
|
151
194
|
warnings;
|
|
@@ -785,6 +828,31 @@ function isCssTag(tag, cssImportNames) {
|
|
|
785
828
|
if (!tag || !cssImportNames || cssImportNames.size === 0) return false;
|
|
786
829
|
return tag.type === "Identifier" && typeof tag.name === "string" && cssImportNames.has(tag.name);
|
|
787
830
|
}
|
|
831
|
+
/**
|
|
832
|
+
* Build the shared `DEBUG_CODEMOD` report lines for cross-file prepass info.
|
|
833
|
+
* `header` distinguishes the standalone scan from the unified prepass; callers
|
|
834
|
+
* may append their own sections before writing.
|
|
835
|
+
*/
|
|
836
|
+
function buildCrossFileDebugLines(header, scannedFiles, info) {
|
|
837
|
+
const cwd = process.cwd();
|
|
838
|
+
const rel = (p) => relative(cwd, p);
|
|
839
|
+
const lines = [header];
|
|
840
|
+
lines.push(` Scanned ${scannedFiles.length} file(s)`);
|
|
841
|
+
if (info.selectorUsages.size === 0) lines.push(" No cross-file selector usages found.");
|
|
842
|
+
else {
|
|
843
|
+
lines.push(` Found cross-file selector usages in ${info.selectorUsages.size} file(s):`);
|
|
844
|
+
for (const [consumer, usages] of info.selectorUsages) for (const u of usages) lines.push(` ${rel(consumer)} → ${u.importedName} (from ${rel(u.resolvedPath)}, transformed=${u.consumerIsTransformed})`);
|
|
845
|
+
}
|
|
846
|
+
if (info.componentsNeedingMarkerSidecar.size > 0) {
|
|
847
|
+
lines.push(" Components needing marker sidecar (both consumer and target transformed):");
|
|
848
|
+
for (const [file, names] of info.componentsNeedingMarkerSidecar) lines.push(` ${rel(file)}: ${[...names].join(", ")}`);
|
|
849
|
+
}
|
|
850
|
+
if (info.componentsNeedingGlobalSelectorBridge.size > 0) {
|
|
851
|
+
lines.push(" Components needing global selector bridge className (consumer not transformed):");
|
|
852
|
+
for (const [file, names] of info.componentsNeedingGlobalSelectorBridge) lines.push(` ${rel(file)}: ${[...names].join(", ")}`);
|
|
853
|
+
}
|
|
854
|
+
return lines;
|
|
855
|
+
}
|
|
788
856
|
/** Safely extract the name string from an AST identifier-like node. */
|
|
789
857
|
function getNodeName(node) {
|
|
790
858
|
if (!node || typeof node !== "object") return;
|
|
@@ -811,22 +879,6 @@ function deduplicateAndResolve(filesToTransform, consumerPaths) {
|
|
|
811
879
|
return result;
|
|
812
880
|
}
|
|
813
881
|
//#endregion
|
|
814
|
-
//#region src/internal/utilities/default-export-name.ts
|
|
815
|
-
/**
|
|
816
|
-
* Regex helpers for inspecting a module's default export by source text.
|
|
817
|
-
*
|
|
818
|
-
* These operate on raw source strings (not the AST) so they can be shared by
|
|
819
|
-
* both prepass and transform-step layers without import-graph coupling.
|
|
820
|
-
*/
|
|
821
|
-
/**
|
|
822
|
-
* Returns the local name of a PascalCase default export, supporting both
|
|
823
|
-
* `export default Name` and `export { Name as default }` forms. Returns
|
|
824
|
-
* `undefined` when no PascalCase default export is found.
|
|
825
|
-
*/
|
|
826
|
-
function findDefaultExportedLocalName(source) {
|
|
827
|
-
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];
|
|
828
|
-
}
|
|
829
|
-
//#endregion
|
|
830
882
|
//#region src/internal/utilities/ast-walk.ts
|
|
831
883
|
const SKIPPED_KEYS = new Set([
|
|
832
884
|
"loc",
|
|
@@ -852,4 +904,4 @@ function walkAst(root, visitor) {
|
|
|
852
904
|
visit(root);
|
|
853
905
|
}
|
|
854
906
|
//#endregion
|
|
855
|
-
export { PARTIAL_MIGRATION_INCOMPLETE_WARNING as C, Logger as S,
|
|
907
|
+
export { PARTIAL_MIGRATION_INCOMPLETE_WARNING as C, assertNoNullNodesInArrays as D, getCascadeDependedFilePath as E, createAstSafeJsonReplacer as O, Logger as S, UNSUPPORTED_SHOULD_FORWARD_PROP_WARNING as T, findImportSource as _, buildImportMapFromNodes as a, resolveBarrelReExportBinding as b, deduplicateAndResolve as c, findStyledImportNameFromNodes as d, walkForImportsAndTemplates as f, fileImportsFrom as g, fileExports as h, buildCrossFileDebugLines as i, findComponentSelectorLocalsFromNodes as l, createPrepassParser as m, BARE_TEMPLATE_IDENTIFIER_RE as n, categorizeSelectorUsages as o, addToSetMap as p, applyBridgeFields as r, collectStyledLocalBindingNames as s, walkAst as t, findCssImportNamesFromNodes as u, getReExportedSourceName as v, PARTIAL_PRESERVED_ANCESTOR_REVEAL_WARNING as w, CASCADE_CONFLICT_WARNING as x, resolveBarrelReExport as y };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as isSelectorContext } from "./selector-context-heuristic-Dptd93Xe.mjs";
|
|
2
|
-
import { r as toRealPath } from "./path-utils-
|
|
3
|
-
import { r as escapeRegex } from "./string-utils-
|
|
2
|
+
import { r as toRealPath } from "./path-utils-BC4U8X_q.mjs";
|
|
3
|
+
import { r as escapeRegex } from "./string-utils-Bo3cWgss.mjs";
|
|
4
4
|
import { readFileSync } from "node:fs";
|
|
5
5
|
//#region src/internal/bridge-consumer-patcher.ts
|
|
6
6
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { r as toRealPath } from "./path-utils-
|
|
2
|
-
import { r as escapeRegex } from "./string-utils-
|
|
1
|
+
import { r as toRealPath } from "./path-utils-BC4U8X_q.mjs";
|
|
2
|
+
import { r as escapeRegex } from "./string-utils-Bo3cWgss.mjs";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
//#region src/internal/forwarded-as-consumer-patcher.ts
|
|
5
5
|
/**
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as ImportSource, i as AdapterInput, o as MarkerFileContext, s as defineAdapter, t as CollectedWarning } from "./logger-B9VD--XI.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/run.d.ts
|
|
4
4
|
interface RunTransformOptions {
|
|
@@ -69,17 +69,6 @@ interface RunTransformOptions {
|
|
|
69
69
|
* @default false
|
|
70
70
|
*/
|
|
71
71
|
silent?: boolean;
|
|
72
|
-
/**
|
|
73
|
-
* Controls which styled declarations are eligible for conversion.
|
|
74
|
-
*
|
|
75
|
-
* - `"all"` converts every supported styled declaration.
|
|
76
|
-
* - `"leavesOnly"` only converts declarations whose render base is intrinsic
|
|
77
|
-
* after adapter resolution, or that wrap another leaf styled declaration in
|
|
78
|
-
* the transform run (including cross-file imports).
|
|
79
|
-
*
|
|
80
|
-
* @default "all"
|
|
81
|
-
*/
|
|
82
|
-
transformMode?: TransformMode;
|
|
83
72
|
/**
|
|
84
73
|
* When true, allow the codemod to leave individual styled declarations as-is when
|
|
85
74
|
* they hit an unsupported pattern while transforming the rest of the file. This
|
package/dist/index.mjs
CHANGED
|
@@ -1,14 +1,173 @@
|
|
|
1
1
|
import { t as createModuleResolver } from "./resolve-imports-DgSAddIF.mjs";
|
|
2
|
-
import { $ as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { r as toRealPath } from "./path-utils-ByFNVtHo.mjs";
|
|
2
|
+
import { $ as assertValidAdapterInput, J as mergeMarkerDeclarations, X as defineAdapter, et as describeValue, t as transformedComponentAcceptsSx, x as identifierName } from "./sx-surface-Kv8zK8L4.mjs";
|
|
3
|
+
import { E as getCascadeDependedFilePath, S as Logger, a as buildImportMapFromNodes, b as resolveBarrelReExportBinding, f as walkForImportsAndTemplates, m as createPrepassParser, s as collectStyledLocalBindingNames, t as walkAst, y as resolveBarrelReExport } from "./ast-walk-CWKn9bmi.mjs";
|
|
4
|
+
import { r as toRealPath } from "./path-utils-BC4U8X_q.mjs";
|
|
6
5
|
import jscodeshift from "jscodeshift";
|
|
7
6
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
7
|
import { dirname, join, relative, resolve } from "node:path";
|
|
9
8
|
import { existsSync, readFileSync } from "node:fs";
|
|
10
9
|
import { glob, readFile, writeFile } from "node:fs/promises";
|
|
11
10
|
import { spawn } from "node:child_process";
|
|
11
|
+
//#region src/internal/prepass/styled-def-bases.ts
|
|
12
|
+
/**
|
|
13
|
+
* Extracts styled-component definition bases for prepass consumers that need
|
|
14
|
+
* component names and their root shape.
|
|
15
|
+
*/
|
|
16
|
+
const RX_EXPORT_DECL = String.raw`(?:export\s+)?(?:const|let|var)\s+`;
|
|
17
|
+
/** `const Name = styled.tag` — intrinsic HTML/SVG tag member. */
|
|
18
|
+
const STYLED_INTRINSIC_MEMBER_RE = new RegExp(String.raw`\b${RX_EXPORT_DECL}([A-Z][A-Za-z0-9]*)\b[^=]*=\s*styled\.([a-z][a-zA-Z0-9]*)\b`, "g");
|
|
19
|
+
/** `const Name = styled("tag")` — intrinsic string tag. */
|
|
20
|
+
const STYLED_INTRINSIC_STRING_RE = new RegExp(String.raw`\b${RX_EXPORT_DECL}([A-Z][A-Za-z0-9]*)\b[^=]*=\s*styled\s*\(\s*["']([^"']+)["']`, "g");
|
|
21
|
+
/** `const Name = styled(Component)` — wraps another component identifier. */
|
|
22
|
+
const STYLED_COMPONENT_RE = new RegExp(String.raw`\b${RX_EXPORT_DECL}([A-Z][A-Za-z0-9]*)\b[^=]*=\s*styled\s*\(\s*([A-Z][A-Za-z0-9]*)\s*\)`, "g");
|
|
23
|
+
/**
|
|
24
|
+
* Regex-derived styled definition bases for files in the transform set.
|
|
25
|
+
* Later entries for the same component name overwrite earlier ones (rare).
|
|
26
|
+
*/
|
|
27
|
+
function extractStyledDefBasesFromSource(filePath, source, into) {
|
|
28
|
+
let map = into.get(filePath);
|
|
29
|
+
if (!map) {
|
|
30
|
+
map = /* @__PURE__ */ new Map();
|
|
31
|
+
into.set(filePath, map);
|
|
32
|
+
}
|
|
33
|
+
STYLED_INTRINSIC_MEMBER_RE.lastIndex = 0;
|
|
34
|
+
for (const m of source.matchAll(STYLED_INTRINSIC_MEMBER_RE)) {
|
|
35
|
+
const name = m[1];
|
|
36
|
+
if (name) map.set(name, { kind: "intrinsic" });
|
|
37
|
+
}
|
|
38
|
+
STYLED_INTRINSIC_STRING_RE.lastIndex = 0;
|
|
39
|
+
for (const m of source.matchAll(STYLED_INTRINSIC_STRING_RE)) {
|
|
40
|
+
const name = m[1];
|
|
41
|
+
if (name) map.set(name, { kind: "intrinsic" });
|
|
42
|
+
}
|
|
43
|
+
STYLED_COMPONENT_RE.lastIndex = 0;
|
|
44
|
+
for (const m of source.matchAll(STYLED_COMPONENT_RE)) {
|
|
45
|
+
const name = m[1];
|
|
46
|
+
const ident = m[2];
|
|
47
|
+
if (name && ident) map.set(name, {
|
|
48
|
+
kind: "component",
|
|
49
|
+
ident
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Regex baseline for styled defs, then an AST pass overrides/adds rows when the
|
|
55
|
+
* source parses. The AST pass understands aliased/named `styled` imports
|
|
56
|
+
* (`import { styled as sc }`) that the regexes (which assume the literal `styled`)
|
|
57
|
+
* miss, so callers that only ran the regex extractor under-report components.
|
|
58
|
+
*/
|
|
59
|
+
function extractStyledDefBases(filePath, source, parser, into) {
|
|
60
|
+
extractStyledDefBasesFromSource(filePath, source, into);
|
|
61
|
+
try {
|
|
62
|
+
const ast = parser.parse(source);
|
|
63
|
+
const program = ast.program ?? ast;
|
|
64
|
+
const importNodes = [];
|
|
65
|
+
walkForImportsAndTemplates(program, importNodes, []);
|
|
66
|
+
extractStyledDefBasesFromAstProgram(filePath, program, collectStyledLocalBindingNames(importNodes), into);
|
|
67
|
+
} catch {}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* AST-based extraction: understands `let`/`var`, export blocks, named `styled` imports,
|
|
71
|
+
* and `.attrs` / `.withConfig` chains before the tagged template.
|
|
72
|
+
* Results merge into `into`; bindings found here override regex entries for the same name.
|
|
73
|
+
*/
|
|
74
|
+
function extractStyledDefBasesFromAstProgram(filePath, program, styledLocalNames, into) {
|
|
75
|
+
if (styledLocalNames.size === 0) return;
|
|
76
|
+
let map = into.get(filePath);
|
|
77
|
+
if (!map) {
|
|
78
|
+
map = /* @__PURE__ */ new Map();
|
|
79
|
+
into.set(filePath, map);
|
|
80
|
+
}
|
|
81
|
+
const body = program.body;
|
|
82
|
+
if (!body) return;
|
|
83
|
+
for (const stmt of body) walkStatement(stmt);
|
|
84
|
+
function walkStatement(stmt) {
|
|
85
|
+
if (stmt.type === "VariableDeclaration") {
|
|
86
|
+
for (const d of stmt.declarations ?? []) processDeclarator(d);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) walkStatement(stmt.declaration);
|
|
90
|
+
}
|
|
91
|
+
function processDeclarator(decl) {
|
|
92
|
+
if (decl.type !== "VariableDeclarator") return;
|
|
93
|
+
const id = decl.id;
|
|
94
|
+
if (id.type !== "Identifier" || typeof id.name !== "string") return;
|
|
95
|
+
const tpl = findTaggedTemplate(unwrapInitializer(decl.init));
|
|
96
|
+
if (!tpl || tpl.type !== "TaggedTemplateExpression") return;
|
|
97
|
+
const base = classifyStyledTemplateTag(tpl.tag, styledLocalNames);
|
|
98
|
+
if (base) map.set(id.name, base);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function unwrapInitializer(node) {
|
|
102
|
+
let cur = node ?? void 0;
|
|
103
|
+
while (cur) {
|
|
104
|
+
if (cur.type === "TSAsExpression" || cur.type === "AsExpression") {
|
|
105
|
+
cur = cur.expression;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (cur.type === "ParenthesizedExpression") {
|
|
109
|
+
cur = cur.expression;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
return cur;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function findTaggedTemplate(node) {
|
|
116
|
+
const n = unwrapInitializer(node);
|
|
117
|
+
if (!n) return;
|
|
118
|
+
if (n.type === "TaggedTemplateExpression") return n;
|
|
119
|
+
}
|
|
120
|
+
/** Peel `.attrs` / `.withConfig` / nested calls down to `styled.div` or `styled(X)`. */
|
|
121
|
+
function peelStyledApplication(tag, styledNames) {
|
|
122
|
+
let cur = tag;
|
|
123
|
+
while (cur) {
|
|
124
|
+
if (cur.type === "CallExpression") {
|
|
125
|
+
const callee = cur.callee;
|
|
126
|
+
if (callee?.type === "MemberExpression") {
|
|
127
|
+
cur = callee;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (callee?.type === "Identifier" && typeof callee.name === "string" && styledNames.has(callee.name)) return cur;
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
if (cur.type === "MemberExpression") {
|
|
134
|
+
const obj = cur.object;
|
|
135
|
+
if (obj?.type === "Identifier" && typeof obj.name === "string" && styledNames.has(obj.name)) return cur;
|
|
136
|
+
cur = obj;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
function classifyStyledTemplateTag(tag, styledNames) {
|
|
144
|
+
const root = peelStyledApplication(tag, styledNames);
|
|
145
|
+
if (!root) return null;
|
|
146
|
+
if (root.type === "MemberExpression") {
|
|
147
|
+
const obj = root.object;
|
|
148
|
+
const prop = root.property;
|
|
149
|
+
const objName = obj?.type === "Identifier" ? obj.name : void 0;
|
|
150
|
+
if (obj?.type !== "Identifier" || typeof objName !== "string" || !styledNames.has(objName)) return null;
|
|
151
|
+
const isComputed = Boolean(root.computed);
|
|
152
|
+
if (isComputed && prop?.type === "StringLiteral" && typeof prop.value === "string") return { kind: "intrinsic" };
|
|
153
|
+
if (!isComputed && prop?.type === "Identifier" && typeof prop.name === "string") return { kind: "intrinsic" };
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
if (root.type === "CallExpression") {
|
|
157
|
+
const callee = root.callee;
|
|
158
|
+
const arg0 = root.arguments?.[0];
|
|
159
|
+
const calleeName = callee?.type === "Identifier" ? callee.name : void 0;
|
|
160
|
+
if (callee?.type !== "Identifier" || typeof calleeName !== "string" || !styledNames.has(calleeName) || !arg0) return null;
|
|
161
|
+
if (arg0.type === "Identifier" && typeof arg0.name === "string") return {
|
|
162
|
+
kind: "component",
|
|
163
|
+
ident: arg0.name
|
|
164
|
+
};
|
|
165
|
+
if (arg0.type === "StringLiteral" && typeof arg0.value === "string") return { kind: "intrinsic" };
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
//#endregion
|
|
12
171
|
//#region src/internal/prepass/resolve-static-members.ts
|
|
13
172
|
/**
|
|
14
173
|
* Resolves the underlying component name(s) a static member access like `Select.Option` refers to,
|
|
@@ -248,9 +407,6 @@ async function runTransform(options) {
|
|
|
248
407
|
"Example: consumerPaths: \"src/**/*.tsx\" // scan for cross-file usage",
|
|
249
408
|
"Example: consumerPaths: null // opt out"
|
|
250
409
|
].join("\n"));
|
|
251
|
-
const transformModeRaw = options.transformMode;
|
|
252
|
-
if (transformModeRaw !== void 0 && transformModeRaw !== "all" && transformModeRaw !== "leavesOnly") throw new Error(["runTransform(options): `transformMode` must be one of: \"all\", \"leavesOnly\".", `Received: transformMode=${describeValue(transformModeRaw)}`].join("\n"));
|
|
253
|
-
const leavesOnly = options.transformMode === "leavesOnly";
|
|
254
410
|
const { files, consumerPaths: consumerPathsOption, dryRun = false, print = false, parser = "tsx", formatterCommands, maxExamples } = options;
|
|
255
411
|
if (maxExamples !== void 0) Logger.setMaxExamples(maxExamples);
|
|
256
412
|
const adapterInput = options.adapter;
|
|
@@ -329,7 +485,7 @@ async function runTransform(options) {
|
|
|
329
485
|
const { createModuleResolver } = await import("./resolve-imports-DgSAddIF.mjs").then((n) => n.n);
|
|
330
486
|
const sharedResolver = createModuleResolver();
|
|
331
487
|
filePaths = orderFilesByLocalImportDependencies(filePaths, sharedResolver, toRealPath);
|
|
332
|
-
const { runPrepass } = await import("./run-prepass-
|
|
488
|
+
const { runPrepass } = await import("./run-prepass-QjRV8IcO.mjs");
|
|
333
489
|
const absoluteFiles = filePaths.map((f) => resolve(f));
|
|
334
490
|
const absoluteConsumers = consumerFilePaths.map((f) => resolve(f));
|
|
335
491
|
let prepassResult;
|
|
@@ -342,9 +498,7 @@ async function runTransform(options) {
|
|
|
342
498
|
resolver: sharedResolver,
|
|
343
499
|
parserName: parser,
|
|
344
500
|
createExternalInterface: adapterInput.externalInterface === "auto",
|
|
345
|
-
enableAstCache: true
|
|
346
|
-
leavesOnly,
|
|
347
|
-
resolveBaseComponent: adapterInput.resolveBaseComponent
|
|
501
|
+
enableAstCache: true
|
|
348
502
|
});
|
|
349
503
|
Logger.info(`Prepass: completed in ${formatElapsedSeconds(prepassStartedAt)}s\n`);
|
|
350
504
|
} catch (err) {
|
|
@@ -356,8 +510,7 @@ async function runTransform(options) {
|
|
|
356
510
|
componentsNeedingMarkerSidecar: /* @__PURE__ */ new Map(),
|
|
357
511
|
componentsNeedingGlobalSelectorBridge: /* @__PURE__ */ new Map(),
|
|
358
512
|
propUsageByFile: /* @__PURE__ */ new Map(),
|
|
359
|
-
stylexComponentFiles: /* @__PURE__ */ new Map()
|
|
360
|
-
globalLeafKeys: leavesOnly ? /* @__PURE__ */ new Set() : void 0
|
|
513
|
+
stylexComponentFiles: /* @__PURE__ */ new Map()
|
|
361
514
|
},
|
|
362
515
|
consumerAnalysis: void 0,
|
|
363
516
|
forwardedAsConsumers: /* @__PURE__ */ new Map(),
|
|
@@ -517,9 +670,7 @@ async function runTransform(options) {
|
|
|
517
670
|
transformedComponents,
|
|
518
671
|
transformedFileSources,
|
|
519
672
|
transientPropRenames,
|
|
520
|
-
allowPartialMigration: options.allowPartialMigration ??
|
|
521
|
-
transformMode: leavesOnly ? "leavesOnly" : options.transformMode ?? "all",
|
|
522
|
-
globalLeafKeys: crossFilePrepassResult.globalLeafKeys,
|
|
673
|
+
allowPartialMigration: options.allowPartialMigration ?? false,
|
|
523
674
|
resolveModule: (fromFile, specifier) => sharedResolver.resolve(resolve(fromFile), specifier),
|
|
524
675
|
runInBand: true,
|
|
525
676
|
silent: options.silent ?? false
|
|
@@ -570,7 +721,7 @@ async function runTransform(options) {
|
|
|
570
721
|
const result = await runTransformSequentially(transformModule, filePaths, runnerOptions);
|
|
571
722
|
if (sidecarFiles.size > 0 && !dryRun) for (const [sidecarPath, content] of sidecarFiles) await writeFile(sidecarPath, mergeSidecarContent(sidecarPath, content), "utf-8");
|
|
572
723
|
if (bridgeResults.size > 0 && !dryRun) {
|
|
573
|
-
const { buildConsumerReplacements, patchConsumerFile } = await import("./bridge-consumer-patcher-
|
|
724
|
+
const { buildConsumerReplacements, patchConsumerFile } = await import("./bridge-consumer-patcher-jeeDUlId.mjs");
|
|
574
725
|
const consumerReplacements = buildConsumerReplacements(crossFilePrepassResult.selectorUsages, bridgeResults, transformedFiles);
|
|
575
726
|
const patchedFiles = [];
|
|
576
727
|
for (const [consumerPath, replacements] of consumerReplacements) {
|
|
@@ -583,7 +734,7 @@ async function runTransform(options) {
|
|
|
583
734
|
if (formatterCommands && patchedFiles.length > 0) await runFormatters(formatterCommands, patchedFiles);
|
|
584
735
|
}
|
|
585
736
|
if (prepassResult.forwardedAsConsumers.size > 0 && !dryRun) {
|
|
586
|
-
const { buildForwardedAsReplacements, patchConsumerForwardedAs } = await import("./forwarded-as-consumer-patcher-
|
|
737
|
+
const { buildForwardedAsReplacements, patchConsumerForwardedAs } = await import("./forwarded-as-consumer-patcher-Do4PI4Qs.mjs");
|
|
587
738
|
const forwardedAsReplacements = buildForwardedAsReplacements(prepassResult.forwardedAsConsumers, transformedFiles);
|
|
588
739
|
const patchedFiles = [];
|
|
589
740
|
for (const [consumerPath, entries] of forwardedAsReplacements) {
|
|
@@ -596,7 +747,7 @@ async function runTransform(options) {
|
|
|
596
747
|
if (formatterCommands && patchedFiles.length > 0) await runFormatters(formatterCommands, patchedFiles);
|
|
597
748
|
}
|
|
598
749
|
if (transientPropRenames.size > 0 && !dryRun) {
|
|
599
|
-
const { collectTransientPropPatches } = await import("./transient-prop-consumer-patcher-
|
|
750
|
+
const { collectTransientPropPatches } = await import("./transient-prop-consumer-patcher-D-iqO8-T.mjs");
|
|
600
751
|
const patches = collectTransientPropPatches({
|
|
601
752
|
transientPropRenames,
|
|
602
753
|
consumerFilePaths: consumerFilePaths.map((p) => resolve(p)),
|
|
@@ -726,16 +877,10 @@ function createStandalonePrepassResult(prepass, filePath, transformedFiles, tran
|
|
|
726
877
|
selectorUsages,
|
|
727
878
|
componentsNeedingMarkerSidecar,
|
|
728
879
|
componentsNeedingGlobalSelectorBridge,
|
|
729
|
-
globalLeafKeys: getStandaloneGlobalLeafKeys(prepass.globalLeafKeys, standaloneFile),
|
|
730
880
|
transformedFiles,
|
|
731
881
|
transformedComponents
|
|
732
882
|
};
|
|
733
883
|
}
|
|
734
|
-
function getStandaloneGlobalLeafKeys(globalLeafKeys, standaloneFile) {
|
|
735
|
-
if (!globalLeafKeys) return;
|
|
736
|
-
const filePrefix = `${standaloneFile}:`;
|
|
737
|
-
return new Set([...globalLeafKeys].filter((key) => key.startsWith(filePrefix)));
|
|
738
|
-
}
|
|
739
884
|
function addSetMapEntry(map, key, value) {
|
|
740
885
|
const values = map.get(key);
|
|
741
886
|
if (values) {
|
|
@@ -950,7 +1095,10 @@ function formatMigrationPlan(plan) {
|
|
|
950
1095
|
lines.push("Manual conversion plan");
|
|
951
1096
|
lines.push("======================");
|
|
952
1097
|
lines.push(`${manualConversionFiles.length} of ${totalFiles} file(s) need manual conversion.`);
|
|
953
|
-
if (unlocksFileCount > 0)
|
|
1098
|
+
if (unlocksFileCount > 0) {
|
|
1099
|
+
lines.push(`Focus on the ${priority.length} file(s) below. Direct payoff: converting all listed blockers lets ${unlocksFileCount} file(s) auto-migrate.`);
|
|
1100
|
+
lines.push(`Per-file "directly unlocks" counts are sole-blocker payoffs; chain context is secondary and may need other blockers or manual fixes too.`);
|
|
1101
|
+
}
|
|
954
1102
|
lines.push("");
|
|
955
1103
|
if (priority.length > 0) {
|
|
956
1104
|
const positionByPath = new Map(priority.map((file, index) => [file.filePath, index + 1]));
|
|
@@ -1052,8 +1200,12 @@ function collectFocusPaths(files) {
|
|
|
1052
1200
|
function appendFileEntry(lines, file, position, positionByPath) {
|
|
1053
1201
|
lines.push(`${position}. ${file.filePath}`);
|
|
1054
1202
|
const impact = [];
|
|
1055
|
-
if (file.
|
|
1056
|
-
|
|
1203
|
+
if (file.blockedFileCount > 0) impact.push(`directly unlocks ${file.soleBlockerFileCount} file(s)`);
|
|
1204
|
+
const chainOnlyFileCount = file.blockedFileCount - file.soleBlockerFileCount;
|
|
1205
|
+
if (chainOnlyFileCount > 0) {
|
|
1206
|
+
const prefix = file.soleBlockerFileCount > 0 ? "also " : "";
|
|
1207
|
+
impact.push(`chain context: ${chainOnlyFileCount} file(s) ${prefix}bail through this file but are not unlocked by it alone`);
|
|
1208
|
+
}
|
|
1057
1209
|
if (file.consumerCount > 0) impact.push(`imported by ${file.consumerCount} file(s)`);
|
|
1058
1210
|
if (impact.length > 0) lines.push(` → ${impact.join(" · ")}`);
|
|
1059
1211
|
if (file.dependsOn.length > 0) {
|