styled-components-to-stylex-codemod 0.0.55 → 0.0.57

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.
@@ -1,290 +1,120 @@
1
- import { n as createPrepassParser } from "./ast-walk-C226poBl.mjs";
2
- import { n as isTemplatePlaceholderInSelectorContext, r as PLACEHOLDER_RE } from "./selector-context-heuristic-LVizWWOR.mjs";
3
- import { resolve } from "node:path";
4
- import "node:fs";
5
- //#region src/internal/utilities/collection-utils.ts
6
- /** Add a value to a Set stored in a Map, creating the Set if it doesn't exist. */
7
- function addToSetMap(map, key, value) {
8
- let set = map.get(key);
9
- if (!set) {
10
- set = /* @__PURE__ */ new Set();
11
- map.set(key, set);
12
- }
13
- set.add(value);
14
- }
15
- //#endregion
16
- //#region src/internal/prepass/scan-cross-file-selectors.ts
1
+ import { m as createPrepassParser } from "./ast-walk-DVmYZ2mK.mjs";
2
+ //#region src/internal/prepass/prepass-ast-utils.ts
17
3
  /**
18
- * Pre-filter: matches any bare `${Identifier}` template expression.
19
- * Used to skip files that only contain arrow functions or member expressions
20
- * in template literals (e.g. `${props => ...}`, `${theme.color}`).
21
- */
22
- const BARE_TEMPLATE_IDENTIFIER_RE = /\$\{\s*[a-zA-Z_$][\w$]*\s*\}/;
23
- /**
24
- * Categorize cross-file selector usages into marker sidecar and global selector bridge maps.
4
+ * Shared structural AST helpers for prepass modules that walk raw babel ASTs
5
+ * to relate exports, local bindings, and named references.
25
6
  *
26
- * Bridge usages (from already-converted files) are skipped — the consumer handles marker
27
- * generation via the forward selector handler, so no sidecar/bridge is needed on the target.
7
+ * These were duplicated verbatim across `component-styled-dependencies` and
8
+ * `stylex-component-exports`; centralizing keeps the traversal semantics
9
+ * (which keys to skip, how to read a node name) consistent.
28
10
  */
29
- function categorizeSelectorUsages(usages, componentsNeedingMarkerSidecar, componentsNeedingGlobalSelectorBridge) {
30
- for (const usage of usages) {
31
- if (usage.bridgeComponentName) continue;
32
- if (usage.consumerIsTransformed) addToSetMap(componentsNeedingMarkerSidecar, usage.resolvedPath, usage.importedName);
33
- addToSetMap(componentsNeedingGlobalSelectorBridge, usage.resolvedPath, usage.importedName);
34
- }
11
+ /** Parse a module `source` into its Program node, trying `tsx` then `babel`. */
12
+ function parseProgram(source) {
13
+ for (const parserName of ["tsx", "babel"]) try {
14
+ const ast = createPrepassParser(parserName).parse(source);
15
+ return ast.program ?? ast;
16
+ } catch {}
17
+ return null;
35
18
  }
36
- /**
37
- * Regex matching bridge GlobalSelector export patterns (global for matchAll).
38
- * Matches both:
39
- * - Old format: `export const XGlobalSelector = ".sc2sx-..."`
40
- * - New format: `` export const XGlobalSelector = `.${xBridgeClass}` ``
41
- */
42
- const BRIDGE_EXPORT_RE = /export\s+const\s+(\w+GlobalSelector)\s*=\s*(?:["']\.sc2sx-|`\.\$\{)/g;
43
- /**
44
- * Detect whether an imported name is a bridge GlobalSelector from an
45
- * already-converted StyleX file.
46
- *
47
- * Detection criteria (hybrid fast + safe):
48
- * 1. Variable name ends with "GlobalSelector" AND the stripped name starts uppercase
49
- * 2. Target file contains "@stylexjs/stylex" (string check, no parse)
50
- * 3. Target file has a matching `export const XGlobalSelector = ".sc2sx-"` pattern
51
- *
52
- * @returns The stripped component name (e.g., "CollapseArrowIcon" for
53
- * "CollapseArrowIconGlobalSelector"), or null if not a bridge.
54
- */
55
- function detectBridgeGlobalSelector(importedName, resolvedPath, readFile) {
56
- if (!importedName.endsWith("GlobalSelector")) return null;
57
- const stripped = importedName.slice(0, -14);
58
- if (!stripped || !/^[A-Z]/.test(stripped)) return null;
59
- const content = readFile(resolvedPath);
60
- if (!content || !content.includes("@stylexjs/stylex")) return null;
61
- let found = false;
62
- for (const m of content.matchAll(BRIDGE_EXPORT_RE)) if (m[1] === importedName) {
63
- found = true;
64
- break;
65
- }
66
- if (!found) return null;
67
- return stripped;
19
+ function programBody(program) {
20
+ return astArray(program.body);
68
21
  }
69
- /**
70
- * If `importedName` is a bridge GlobalSelector, populate bridge fields on `usage`
71
- * and find the corresponding component import from the same source.
72
- */
73
- function applyBridgeFields(usage, importedName, localName, resolvedPath, importMap, readFile) {
74
- const bridgeName = detectBridgeGlobalSelector(importedName, resolvedPath, readFile);
75
- if (!bridgeName) return;
76
- usage.bridgeComponentName = bridgeName;
77
- const imp = importMap.get(localName);
78
- if (!imp) return;
79
- let defaultImportLocal;
80
- for (const [otherLocal, otherImp] of importMap) {
81
- if (otherImp.source !== imp.source || otherLocal === localName) continue;
82
- if (otherImp.importedName === bridgeName) {
83
- usage.bridgeComponentLocalName = otherLocal;
84
- defaultImportLocal = void 0;
85
- break;
86
- }
87
- if (otherImp.importedName === "default" && defaultImportLocal === void 0) defaultImportLocal = otherLocal;
88
- }
89
- if (defaultImportLocal !== void 0) usage.bridgeComponentLocalName = defaultImportLocal;
22
+ function astArray(value) {
23
+ return Array.isArray(value) ? value.filter(isAstNode) : [];
24
+ }
25
+ function nodeName(node) {
26
+ if (!node) return;
27
+ if (node.type === "Identifier" || node.type === "JSXIdentifier" || node.type === "StringLiteral") return typeof node.name === "string" ? node.name : typeof node.value === "string" ? node.value : void 0;
90
28
  }
91
- /** Global version for matchAll/replace operations */
92
- const PLACEHOLDER_RE_G = new RegExp(PLACEHOLDER_RE.source, "g");
93
29
  /**
94
- * Walk the AST collecting ImportDeclaration and TaggedTemplateExpression nodes.
95
- *
96
- * Uses a targeted recursive walk — only descends into node types that can
97
- * contain these targets (skips type annotations, comments, etc.).
30
+ * Top-level binding name initializer/body node for function, class, and
31
+ * variable declarations (including those behind a named export).
98
32
  */
99
- function walkForImportsAndTemplates(node, imports, templates) {
100
- if (!node || typeof node !== "object") return;
101
- const n = node;
102
- if (n.type === "ImportDeclaration") {
103
- imports.push(n);
104
- return;
105
- }
106
- if (n.type === "TaggedTemplateExpression") templates.push(n);
107
- for (const key of Object.keys(n)) {
108
- if (key === "type" || key === "start" || key === "end" || key === "loc") continue;
109
- const val = n[key];
110
- if (Array.isArray(val)) for (const child of val) walkForImportsAndTemplates(child, imports, templates);
111
- else if (val && typeof val === "object" && val.type) walkForImportsAndTemplates(val, imports, templates);
112
- }
113
- }
114
- /** Build a map of localName → import info from raw ImportDeclaration nodes. */
115
- function buildImportMapFromNodes(importNodes) {
116
- const map = /* @__PURE__ */ new Map();
117
- for (const node of importNodes) {
118
- const sourceValue = node.source?.value;
119
- if (typeof sourceValue !== "string") continue;
120
- const specifiers = node.specifiers;
121
- if (!specifiers) continue;
122
- for (const spec of specifiers) {
123
- const localName = getNodeName(spec.local);
124
- if (!localName) continue;
125
- if (spec.type === "ImportDefaultSpecifier") map.set(localName, {
126
- source: sourceValue,
127
- importedName: "default"
33
+ function localBindings(program) {
34
+ const bindings = [];
35
+ for (const stmt of programBody(program)) {
36
+ const declaration = stmt.type === "ExportNamedDeclaration" ? stmt.declaration : stmt;
37
+ if (!declaration) continue;
38
+ if (declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration") {
39
+ const name = nodeName(declaration.id);
40
+ const body = declaration.body;
41
+ if (name && body) bindings.push({
42
+ name,
43
+ node: body
128
44
  });
129
- else if (spec.type === "ImportNamespaceSpecifier") map.set(localName, {
130
- source: sourceValue,
131
- importedName: "*"
45
+ continue;
46
+ }
47
+ if (declaration.type === "VariableDeclaration") for (const declarator of astArray(declaration.declarations)) {
48
+ const name = nodeName(declarator.id);
49
+ const init = declarator.init;
50
+ if (name && init) bindings.push({
51
+ name,
52
+ node: init
132
53
  });
133
- else if (spec.type === "ImportSpecifier") {
134
- const importedName = getNodeName(spec.imported) ?? localName;
135
- map.set(localName, {
136
- source: sourceValue,
137
- importedName
138
- });
139
- }
140
54
  }
141
55
  }
142
- return map;
56
+ return bindings;
143
57
  }
144
58
  /**
145
- * Local identifiers that refer to `styled` from `"styled-components"` (default and/or
146
- * `import { styled }` / `import { styled as sc }`).
59
+ * Depth-first walk that skips metadata (`loc`/comments), type annotations, and
60
+ * the non-computed `key`/`property` of object members and member expressions,
61
+ * so visitors only see value-position nodes.
147
62
  */
148
- function collectStyledLocalBindingNames(importNodes) {
149
- const names = /* @__PURE__ */ new Set();
150
- for (const node of importNodes) {
151
- if (node.source?.value !== "styled-components") continue;
152
- const specifiers = node.specifiers;
153
- if (!specifiers) continue;
154
- for (const spec of specifiers) if (spec.type === "ImportDefaultSpecifier") {
155
- const name = getNodeName(spec.local);
156
- if (name) names.add(name);
157
- } else if (spec.type === "ImportSpecifier") {
158
- if (getNodeName(spec.imported) === "styled") {
159
- const localName = getNodeName(spec.local);
160
- if (localName) names.add(localName);
161
- }
63
+ function walkValueAst(root, visitor) {
64
+ const visit = (node) => {
65
+ if (!node || typeof node !== "object") return;
66
+ if (Array.isArray(node)) {
67
+ for (const child of node) visit(child);
68
+ return;
162
69
  }
163
- }
164
- return names;
165
- }
166
- /** Find the local name for the styled-components default import. */
167
- function findStyledImportNameFromNodes(importNodes) {
168
- let namedStyledLocal;
169
- for (const node of importNodes) {
170
- if (node.source?.value !== "styled-components") continue;
171
- const specifiers = node.specifiers;
172
- if (!specifiers) continue;
173
- for (const spec of specifiers) if (spec.type === "ImportDefaultSpecifier") {
174
- const name = getNodeName(spec.local);
175
- if (name) return name;
176
- } else if (spec.type === "ImportSpecifier") {
177
- if (getNodeName(spec.imported) === "styled") {
178
- const localName = getNodeName(spec.local);
179
- if (localName) namedStyledLocal = localName;
180
- }
70
+ const astNode = node;
71
+ visitor(astNode);
72
+ for (const key of Object.keys(astNode)) {
73
+ if (shouldSkipChild(astNode, key)) continue;
74
+ const child = astNode[key];
75
+ if (child && typeof child === "object") visit(child);
181
76
  }
182
- }
183
- return namedStyledLocal;
77
+ };
78
+ visit(root);
184
79
  }
185
80
  /**
186
- * Find local names of `css` imported from styled-components.
187
- * Handles aliased imports like `import { css as sc } from "styled-components"`.
81
+ * Whether `node` references any of `localNames`. `whenUndefined` is the
82
+ * conservative answer when `node` is absent dependency checks treat a missing
83
+ * node as dependent (`true`), surface checks as not referencing (`false`).
188
84
  */
189
- function findCssImportNamesFromNodes(importNodes) {
190
- const names = /* @__PURE__ */ new Set();
191
- for (const node of importNodes) {
192
- if (node.source?.value !== "styled-components") continue;
193
- const specifiers = node.specifiers;
194
- if (!specifiers) continue;
195
- for (const spec of specifiers) if (spec.type === "ImportSpecifier") {
196
- if (getNodeName(spec.imported) === "css") {
197
- const localName = getNodeName(spec.local);
198
- if (localName) names.add(localName);
199
- }
200
- }
201
- }
202
- return names;
85
+ function nodeReferencesLocalNames$1(node, localNames, whenUndefined) {
86
+ if (!node) return whenUndefined;
87
+ let found = false;
88
+ walkValueAst(node, (candidate) => {
89
+ if (!found && isNamedReference(candidate, localNames)) found = true;
90
+ });
91
+ return found;
203
92
  }
204
- /**
205
- * Find local names of imported components used as selectors inside
206
- * styled-components template literals (both `styled` and `css` tagged templates).
207
- */
208
- function findComponentSelectorLocalsFromNodes(templateNodes, styledImportName, cssImportNames) {
209
- const selectorLocals = /* @__PURE__ */ new Set();
210
- for (const node of templateNodes) {
211
- if (!isStyledTag(node.tag, styledImportName) && !isCssTag(node.tag, cssImportNames)) continue;
212
- const quasi = node.quasi;
213
- if (!quasi) continue;
214
- const quasis = quasi.quasis;
215
- const expressions = quasi.expressions;
216
- if (!quasis || !expressions) continue;
217
- const rawParts = [];
218
- for (let i = 0; i < quasis.length; i++) {
219
- const value = quasis[i]?.value;
220
- rawParts.push(value?.raw ?? "");
221
- if (i < expressions.length) rawParts.push(`__SC_EXPR_${i}__`);
222
- }
223
- const rawCss = rawParts.join("");
224
- for (const match of rawCss.matchAll(PLACEHOLDER_RE_G)) {
225
- const exprIndex = Number(match[1]);
226
- const pos = match.index;
227
- if (isTemplatePlaceholderInSelectorContext(rawCss, pos, match[0].length)) {
228
- const expr = expressions[exprIndex];
229
- if (expr?.type === "Identifier" && typeof expr.name === "string") selectorLocals.add(expr.name);
230
- }
231
- }
232
- }
233
- return selectorLocals;
93
+ function isAstNode(value) {
94
+ return Boolean(value && typeof value === "object");
234
95
  }
235
- /**
236
- * Check whether a styled-components tag expression is a styled call.
237
- * Matches: styled.div, styled(X), styled.div.attrs(...), styled(X).withConfig(...), etc.
238
- */
239
- function isStyledTag(tag, styledName) {
240
- if (!tag || typeof tag !== "object") return false;
241
- if (tag.type === "MemberExpression") {
242
- const obj = tag.object;
243
- if (obj?.type === "Identifier" && obj.name === styledName) return true;
244
- }
245
- if (tag.type === "CallExpression") {
246
- const callee = tag.callee;
247
- if (callee?.type === "Identifier" && callee.name === styledName) return true;
248
- if (callee?.type === "MemberExpression" && callee.object) return isStyledTag(callee.object, styledName);
249
- }
96
+ function isNamedReference(node, localNames) {
97
+ if (node.type === "Identifier" || node.type === "JSXIdentifier") return typeof node.name === "string" && localNames.has(node.name);
250
98
  return false;
251
99
  }
252
- /** Check if a template tag is the `css` helper from styled-components. */
253
- function isCssTag(tag, cssImportNames) {
254
- if (!tag || !cssImportNames || cssImportNames.size === 0) return false;
255
- return tag.type === "Identifier" && typeof tag.name === "string" && cssImportNames.has(tag.name);
256
- }
257
- /** Safely extract the name string from an AST identifier-like node. */
258
- function getNodeName(node) {
259
- if (!node || typeof node !== "object") return;
260
- if (node.type === "Identifier" && typeof node.name === "string") return node.name;
261
- }
262
- /** Deduplicate and resolve two file lists into a single array of absolute paths. */
263
- function deduplicateAndResolve(filesToTransform, consumerPaths) {
264
- const seen = /* @__PURE__ */ new Set();
265
- const result = [];
266
- for (const f of filesToTransform) {
267
- const abs = resolve(f);
268
- if (!seen.has(abs)) {
269
- seen.add(abs);
270
- result.push(abs);
271
- }
272
- }
273
- for (const f of consumerPaths) {
274
- const abs = resolve(f);
275
- if (!seen.has(abs)) {
276
- seen.add(abs);
277
- result.push(abs);
278
- }
279
- }
280
- return result;
100
+ function shouldSkipChild(node, key) {
101
+ if ([
102
+ "loc",
103
+ "comments",
104
+ "leadingComments",
105
+ "trailingComments"
106
+ ].includes(key)) return true;
107
+ if ([
108
+ "typeAnnotation",
109
+ "typeParameters",
110
+ "returnType"
111
+ ].includes(key)) return true;
112
+ if (key === "key" && (node.type === "ObjectProperty" || node.type === "Property") && node.computed !== true) return true;
113
+ if (key === "property" && node.type === "MemberExpression" && node.computed !== true) return true;
114
+ return false;
281
115
  }
282
116
  //#endregion
283
117
  //#region src/internal/prepass/stylex-component-exports.ts
284
- /**
285
- * Prepass helpers for identifying exported components that already apply StyleX.
286
- * Core concepts: StyleX import bindings, export names, and local binding traces.
287
- */
288
118
  function collectStylexExportNames(source) {
289
119
  const parsed = parseProgram(source);
290
120
  if (!parsed) return /* @__PURE__ */ new Set();
@@ -297,13 +127,6 @@ function collectStylexExportNames(source) {
297
127
  }
298
128
  return names;
299
129
  }
300
- function parseProgram(source) {
301
- for (const parserName of ["tsx", "babel"]) try {
302
- const ast = createPrepassParser(parserName).parse(source);
303
- return ast.program ?? ast;
304
- } catch {}
305
- return null;
306
- }
307
130
  function collectStylexNamedExports(program, stmt, stylexUsage, names) {
308
131
  if (stmt.type !== "ExportNamedDeclaration") return;
309
132
  const declaration = stmt.declaration;
@@ -388,36 +211,6 @@ function candidateUsesStylex(candidate, stylexUsage) {
388
211
  function findLocalBindingNode(program, localName) {
389
212
  for (const binding of localBindings(program)) if (binding.name === localName) return binding.node;
390
213
  }
391
- function localBindings(program) {
392
- const bindings = [];
393
- for (const stmt of programBody(program)) {
394
- const declaration = stmt.type === "ExportNamedDeclaration" ? stmt.declaration : stmt;
395
- if (!declaration) continue;
396
- const directBinding = localBindingFromDeclaration(declaration);
397
- if (directBinding) {
398
- bindings.push(directBinding);
399
- continue;
400
- }
401
- if (declaration.type === "VariableDeclaration") for (const declarator of astArray(declaration.declarations)) {
402
- const name = nodeName(declarator.id);
403
- const init = declarator.init;
404
- if (name && init) bindings.push({
405
- name,
406
- node: init
407
- });
408
- }
409
- }
410
- return bindings;
411
- }
412
- function localBindingFromDeclaration(declaration) {
413
- if (declaration.type !== "FunctionDeclaration" && declaration.type !== "ClassDeclaration") return null;
414
- const name = nodeName(declaration.id);
415
- const body = declaration.body;
416
- return name && body ? {
417
- name,
418
- node: body
419
- } : null;
420
- }
421
214
  function referencedLocalNames(program, node) {
422
215
  const localNameSet = new Set(localBindings(program).map((binding) => binding.name));
423
216
  const referenced = /* @__PURE__ */ new Set();
@@ -460,63 +253,9 @@ function isStylexSxAttribute(node, styleObjectNames) {
460
253
  if (!value) return false;
461
254
  return nodeReferencesLocalNames(value.type === "JSXExpressionContainer" ? value.expression : value, styleObjectNames);
462
255
  }
256
+ /** StyleX surface checks treat a missing node as not referencing the names. */
463
257
  function nodeReferencesLocalNames(node, localNames) {
464
- if (!node) return false;
465
- let found = false;
466
- walkValueAst(node, (candidate) => {
467
- if (!found && isNamedReference(candidate, localNames)) found = true;
468
- });
469
- return found;
470
- }
471
- function walkValueAst(root, visitor) {
472
- const visit = (node) => {
473
- if (!node || typeof node !== "object") return;
474
- if (Array.isArray(node)) {
475
- for (const child of node) visit(child);
476
- return;
477
- }
478
- const astNode = node;
479
- visitor(astNode);
480
- for (const key of Object.keys(astNode)) {
481
- if (shouldSkipChild(astNode, key)) continue;
482
- const child = astNode[key];
483
- if (child && typeof child === "object") visit(child);
484
- }
485
- };
486
- visit(root);
487
- }
488
- function shouldSkipChild(node, key) {
489
- if ([
490
- "loc",
491
- "comments",
492
- "leadingComments",
493
- "trailingComments"
494
- ].includes(key)) return true;
495
- if ([
496
- "typeAnnotation",
497
- "typeParameters",
498
- "returnType"
499
- ].includes(key)) return true;
500
- if (key === "key" && (node.type === "ObjectProperty" || node.type === "Property") && node.computed !== true) return true;
501
- if (key === "property" && node.type === "MemberExpression" && node.computed !== true) return true;
502
- return false;
503
- }
504
- function isNamedReference(node, localNames) {
505
- if (node.type === "Identifier" || node.type === "JSXIdentifier") return typeof node.name === "string" && localNames.has(node.name);
506
- return false;
507
- }
508
- function programBody(program) {
509
- return astArray(program.body);
510
- }
511
- function astArray(value) {
512
- return Array.isArray(value) ? value.filter(isAstNode) : [];
513
- }
514
- function isAstNode(value) {
515
- return Boolean(value && typeof value === "object");
516
- }
517
- function nodeName(node) {
518
- if (!node) return;
519
- if (node.type === "Identifier" || node.type === "JSXIdentifier" || node.type === "StringLiteral") return typeof node.name === "string" ? node.name : typeof node.value === "string" ? node.value : void 0;
258
+ return nodeReferencesLocalNames$1(node, localNames, false);
520
259
  }
521
260
  //#endregion
522
261
  //#region src/internal/utilities/jsx-static-literal.ts
@@ -597,4 +336,4 @@ function getExhaustiveObservedStaticValues(info, propName) {
597
336
  return values.length === propUsage.values.length ? values : null;
598
337
  }
599
338
  //#endregion
600
- export { walkForImportsAndTemplates as _, mergeComponentPropUsage as a, BARE_TEMPLATE_IDENTIFIER_RE as c, categorizeSelectorUsages as d, collectStyledLocalBindingNames as f, findStyledImportNameFromNodes as g, findCssImportNamesFromNodes as h, getExhaustiveObservedStaticValues as i, applyBridgeFields as l, findComponentSelectorLocalsFromNodes as m, createComponentPropUsageInfo as n, readStaticJsxLiteral as o, deduplicateAndResolve as p, formatObservedVariantCondition as r, collectStylexExportNames as s, KNOWN_NON_ELEMENT_PROPS as t, buildImportMapFromNodes as u, addToSetMap as v };
339
+ export { mergeComponentPropUsage as a, astArray as c, nodeReferencesLocalNames$1 as d, parseProgram as f, getExhaustiveObservedStaticValues as i, localBindings as l, createComponentPropUsageInfo as n, readStaticJsxLiteral as o, programBody as p, formatObservedVariantCondition as r, collectStylexExportNames as s, KNOWN_NON_ELEMENT_PROPS as t, nodeName as u };