styled-components-to-stylex-codemod 0.0.49 → 0.0.50

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.
@@ -5,6 +5,7 @@ import { readFileSync } from "node:fs";
5
5
  * Logger and warning types for transform diagnostics.
6
6
  * Core concepts: severity classification and source context reporting.
7
7
  */
8
+ 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";
8
9
  const UNSUPPORTED_SHOULD_FORWARD_PROP_WARNING = "Unsupported shouldForwardProp pattern (only !prop.startsWith(), ![].includes(prop), and prop !== are supported)";
9
10
  /**
10
11
  * When fileCount <= this threshold, warnings are printed per-file inline and
@@ -134,6 +135,7 @@ function createContextReplacer() {
134
135
  return value;
135
136
  };
136
137
  }
138
+ const MAX_DEPENDED_FILE_GROUPS = 15;
137
139
  var LoggerReport = class {
138
140
  warnings;
139
141
  fileCount;
@@ -163,6 +165,20 @@ var LoggerReport = class {
163
165
  lines.push("");
164
166
  lines.push(`▸ ${group.message} (${group.warnings.length})`);
165
167
  lines.push("");
168
+ const dependedFileGroups = this.groupDependedFiles(group);
169
+ if (dependedFileGroups.length > 0) {
170
+ lines.push(" Top depended files:");
171
+ lines.push("");
172
+ for (const [index, dependedFileGroup] of dependedFileGroups.entries()) {
173
+ const usageCount = dependedFileGroup.usageFiles.length;
174
+ const usageLabel = usageCount === 1 ? "usage file" : "usage files";
175
+ lines.push(` ${index + 1}. ${dependedFileGroup.dependedFilePath} (${usageCount} ${usageLabel})`);
176
+ for (const usageFile of dependedFileGroup.usageFiles.slice(0, MAX_EXAMPLES)) lines.push(` ${usageFile}`);
177
+ const remainingUsageFiles = usageCount - MAX_EXAMPLES;
178
+ if (remainingUsageFiles > 0) lines.push(` ... and ${remainingUsageFiles} more usage file(s)`);
179
+ lines.push("");
180
+ }
181
+ }
166
182
  const seenFiles = /* @__PURE__ */ new Set();
167
183
  const uniqueLocations = [];
168
184
  for (const loc of group.warnings) if (!seenFiles.has(loc.filePath)) {
@@ -212,6 +228,21 @@ var LoggerReport = class {
212
228
  }
213
229
  return Array.from(groupMap.values()).sort((a, b) => b.warnings.length - a.warnings.length);
214
230
  }
231
+ groupDependedFiles(group) {
232
+ if (group.message !== "styled(ImportedComponent) wraps a component whose file uses styled-components — convert the base component's file first to avoid CSS cascade conflicts") return [];
233
+ const groupMap = /* @__PURE__ */ new Map();
234
+ for (const warning of group.warnings) {
235
+ const dependedFilePath = getCascadeDependedFilePath(warning);
236
+ if (!dependedFilePath) continue;
237
+ const dependedFileWarnings = groupMap.get(dependedFilePath) ?? [];
238
+ dependedFileWarnings.push(warning);
239
+ groupMap.set(dependedFilePath, dependedFileWarnings);
240
+ }
241
+ return Array.from(groupMap.entries()).map(([dependedFilePath, warnings]) => ({
242
+ dependedFilePath,
243
+ usageFiles: uniqueSorted(warnings.map((warning) => warning.filePath))
244
+ })).sort((a, b) => b.usageFiles.length - a.usageFiles.length).slice(0, MAX_DEPENDED_FILE_GROUPS);
245
+ }
215
246
  getSnippet(filePath, loc) {
216
247
  if (!loc) return;
217
248
  const lines = this.getFileLines(filePath);
@@ -240,6 +271,18 @@ var LoggerReport = class {
240
271
  }
241
272
  }
242
273
  };
274
+ function getCascadeDependedFilePath(warning) {
275
+ const context = warning.context;
276
+ if (!context || typeof context !== "object") return;
277
+ const record = context;
278
+ const definitionPath = record.definitionPath;
279
+ if (typeof definitionPath === "string") return definitionPath;
280
+ const importedPath = record.importedPath;
281
+ return typeof importedPath === "string" ? importedPath : void 0;
282
+ }
283
+ function uniqueSorted(values) {
284
+ return Array.from(new Set(values)).sort((a, b) => a.localeCompare(b));
285
+ }
243
286
  const WARN_BG_COLOR = "\x1B[43m";
244
287
  const WARN_TEXT_COLOR = "\x1B[30m";
245
288
  const ERROR_BG_COLOR = "\x1B[41m";
@@ -405,4 +448,4 @@ function walkAst(root, visitor) {
405
448
  visit(root);
406
449
  }
407
450
  //#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 };
451
+ export { getReExportedSourceName as a, CASCADE_CONFLICT_WARNING as c, findImportSource as i, Logger as l, fileExports as n, resolveBarrelReExport as o, fileImportsFrom as r, resolveBarrelReExportBinding as s, walkAst as t, UNSUPPORTED_SHOULD_FORWARD_PROP_WARNING as u };
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 { 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";
2
+ import { l as Logger, o as resolveBarrelReExport, t as walkAst } from "./ast-walk-DgShpexa.mjs";
3
+ import { i as extractStyledDefBasesFromSource, t as createPrepassParser } from "./prepass-parser-BRDlW3DI.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";
@@ -305,7 +305,7 @@ async function runTransform(options) {
305
305
  const { createModuleResolver } = await import("./resolve-imports-DgSAddIF.mjs").then((n) => n.n);
306
306
  const sharedResolver = createModuleResolver();
307
307
  filePaths = orderFilesByLocalImportDependencies(filePaths, sharedResolver, toRealPath);
308
- const { runPrepass } = await import("./run-prepass-BWQCThfh.mjs");
308
+ const { runPrepass } = await import("./run-prepass-Da3N174M.mjs");
309
309
  const absoluteFiles = filePaths.map((f) => resolve(f));
310
310
  const absoluteConsumers = consumerFilePaths.map((f) => resolve(f));
311
311
  let prepassResult;
@@ -498,7 +498,23 @@ async function runTransform(options) {
498
498
  transformedFiles: /* @__PURE__ */ new Set()
499
499
  },
500
500
  silent: true,
501
- isolateFiles: true
501
+ isolateFiles: true,
502
+ createIsolatedOptions(filePath) {
503
+ const isolatedTransformedFiles = /* @__PURE__ */ new Set();
504
+ return {
505
+ ...runnerOptions,
506
+ dry: true,
507
+ print: false,
508
+ sidecarFiles: /* @__PURE__ */ new Map(),
509
+ bridgeResults: /* @__PURE__ */ new Map(),
510
+ transformedFiles: isolatedTransformedFiles,
511
+ transformedFileSources: /* @__PURE__ */ new Map(),
512
+ transientPropRenames: /* @__PURE__ */ new Map(),
513
+ crossFilePrepassResult: createStandalonePrepassResult(crossFilePrepassResult, filePath, isolatedTransformedFiles),
514
+ silent: true,
515
+ isolateFiles: true
516
+ };
517
+ }
502
518
  });
503
519
  standaloneWarnings = Logger.createReport().getWarnings();
504
520
  Logger._clearCollected();
@@ -639,6 +655,46 @@ function readFileForOrdering(filePath) {
639
655
  return "";
640
656
  }
641
657
  }
658
+ function createStandalonePrepassResult(prepass, filePath, transformedFiles) {
659
+ const standaloneFile = toRealPath(resolve(filePath));
660
+ const selectorUsages = /* @__PURE__ */ new Map();
661
+ const componentsNeedingMarkerSidecar = /* @__PURE__ */ new Map();
662
+ const componentsNeedingGlobalSelectorBridge = /* @__PURE__ */ new Map();
663
+ for (const [consumerPath, usages] of prepass.selectorUsages) {
664
+ const consumerIsTransformed = toRealPath(consumerPath) === standaloneFile;
665
+ const isolatedUsages = usages.map((usage) => ({
666
+ ...usage,
667
+ consumerIsTransformed
668
+ }));
669
+ selectorUsages.set(consumerPath, isolatedUsages);
670
+ for (const usage of isolatedUsages) {
671
+ if (usage.bridgeComponentName) continue;
672
+ if (consumerIsTransformed) addSetMapEntry(componentsNeedingMarkerSidecar, usage.resolvedPath, usage.importedName);
673
+ addSetMapEntry(componentsNeedingGlobalSelectorBridge, usage.resolvedPath, usage.importedName);
674
+ }
675
+ }
676
+ return {
677
+ ...prepass,
678
+ selectorUsages,
679
+ componentsNeedingMarkerSidecar,
680
+ componentsNeedingGlobalSelectorBridge,
681
+ globalLeafKeys: getStandaloneGlobalLeafKeys(prepass.globalLeafKeys, standaloneFile),
682
+ transformedFiles
683
+ };
684
+ }
685
+ function getStandaloneGlobalLeafKeys(globalLeafKeys, standaloneFile) {
686
+ if (!globalLeafKeys) return;
687
+ const filePrefix = `${standaloneFile}:`;
688
+ return new Set([...globalLeafKeys].filter((key) => key.startsWith(filePrefix)));
689
+ }
690
+ function addSetMapEntry(map, key, value) {
691
+ const values = map.get(key);
692
+ if (values) {
693
+ values.add(value);
694
+ return;
695
+ }
696
+ map.set(key, new Set([value]));
697
+ }
642
698
  async function runTransformSequentially(transformModule, filePaths, options) {
643
699
  const transform = await loadTransformFunction(transformModule);
644
700
  const aggregate = {
@@ -650,20 +706,25 @@ async function runTransformSequentially(transformModule, filePaths, options) {
650
706
  files: []
651
707
  };
652
708
  const startedAt = performance.now();
653
- const j = jscodeshift.withParser(options.parser);
654
- const api = {
655
- j,
656
- jscodeshift: j,
657
- stats: () => {},
658
- report: (msg) => {
659
- if (!options.silent) process.stdout.write(`${msg}\n`);
660
- }
709
+ const createApi = () => {
710
+ const j = jscodeshift.withParser(options.parser);
711
+ return {
712
+ j,
713
+ jscodeshift: j,
714
+ stats: () => {},
715
+ report: (msg) => {
716
+ if (!options.silent) process.stdout.write(`${msg}\n`);
717
+ }
718
+ };
661
719
  };
720
+ const sharedApi = createApi();
662
721
  for (const filePath of filePaths) {
722
+ const fileOptions = options.createIsolatedOptions?.(filePath) ?? options;
723
+ const api = fileOptions.isolateFiles === true ? createApi() : sharedApi;
663
724
  if (options.isolateFiles === true) {
664
- options.transformedFiles.clear();
665
- options.transformedFileSources.clear();
666
- options.crossFilePrepassResult?.transformedFiles?.clear();
725
+ fileOptions.transformedFiles.clear();
726
+ fileOptions.transformedFileSources.clear();
727
+ fileOptions.crossFilePrepassResult?.transformedFiles?.clear();
667
728
  }
668
729
  let source;
669
730
  try {
@@ -681,8 +742,8 @@ async function runTransformSequentially(transformModule, filePaths, options) {
681
742
  const output = await transform({
682
743
  path: filePath,
683
744
  source
684
- }, api, options);
685
- if (output !== null) options.transformedFileSources.set(toRealPath(filePath), output);
745
+ }, api, fileOptions);
746
+ if (output !== null) fileOptions.transformedFileSources.set(toRealPath(filePath), output);
686
747
  if (output === null) {
687
748
  aggregate.skip += 1;
688
749
  aggregate.files.push({
@@ -699,8 +760,8 @@ async function runTransformSequentially(transformModule, filePaths, options) {
699
760
  });
700
761
  continue;
701
762
  }
702
- if (options.print) process.stdout.write(`${output}\n`);
703
- if (!options.dry) await writeFile(filePath, output, "utf-8");
763
+ if (fileOptions.print) process.stdout.write(`${output}\n`);
764
+ if (!fileOptions.dry) await writeFile(filePath, output, "utf-8");
704
765
  aggregate.ok += 1;
705
766
  aggregate.files.push({
706
767
  filePath,
@@ -1,4 +1,4 @@
1
- import { i as findImportSource, s as resolveBarrelReExportBinding } from "./ast-walk-DigTJqU7.mjs";
1
+ import { i as findImportSource, s as resolveBarrelReExportBinding } from "./ast-walk-DgShpexa.mjs";
2
2
  import { parse } from "@babel/parser";
3
3
  //#region src/internal/prepass/compute-leaf-set.ts
4
4
  /**
@@ -1,5 +1,5 @@
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";
1
+ import { i as findImportSource, l as Logger, n as fileExports, o as resolveBarrelReExport, r as fileImportsFrom, s as resolveBarrelReExportBinding, t as walkAst } from "./ast-walk-DgShpexa.mjs";
2
+ import { i as extractStyledDefBasesFromSource, n as computeGlobalLeafKeys, r as extractStyledDefBasesFromAstProgram, t as createPrepassParser } from "./prepass-parser-BRDlW3DI.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
5
  import { a as mergeComponentPropUsage, n as createComponentPropUsageInfo, o as readStaticJsxLiteral, t as KNOWN_NON_ELEMENT_PROPS } from "./prop-usage-QtOSsKTr.mjs";
@@ -1,6 +1,6 @@
1
1
  import { t as createModuleResolver } from "./resolve-imports-DgSAddIF.mjs";
2
2
  import { a as isDirectionalResult, n as mergeMarkerDeclarations, o as assertValidAdapter, r as DEFAULT_THEME_HOOK, t as transformedComponentAcceptsSx } from "./sx-surface-BzqO3hcC.mjs";
3
- import { a as getReExportedSourceName, c as Logger, i as findImportSource, l as UNSUPPORTED_SHOULD_FORWARD_PROP_WARNING, n as fileExports, s as resolveBarrelReExportBinding, t as walkAst } from "./ast-walk-DigTJqU7.mjs";
3
+ import { a as getReExportedSourceName, c as CASCADE_CONFLICT_WARNING, i as findImportSource, l as Logger, n as fileExports, s as resolveBarrelReExportBinding, t as walkAst, u as UNSUPPORTED_SHOULD_FORWARD_PROP_WARNING } from "./ast-walk-DgShpexa.mjs";
4
4
  import { n as resolveExistingFilePath, r as toRealPath, t as isRelativeSpecifier } from "./path-utils-BC4U8X_q.mjs";
5
5
  import { a as isBackgroundImageValue, c as isSingleBackgroundComponent, d as kebabToCamelCase, f as looksLikeLength, h as sanitizeIdentifier, i as getCommentBody, l as isStyleSectionMarkerComment, m as normalizeWhitespace, n as capitalize, o as isJSDocBlockComment, p as lowerFirst, r as escapeRegex, s as isPrettierIgnoreComment, t as camelToKebabCase, u as isValidIdentifierName } from "./string-utils-DD9wdRHW.mjs";
6
6
  import { a as terminateStandaloneInterpolationStatements, i as parseStyledTemplateLiteral, n as isTemplatePlaceholderInSelectorContext, r as PLACEHOLDER_RE } from "./selector-context-heuristic-LVizWWOR.mjs";
@@ -13988,6 +13988,7 @@ function detectCascadeConflictStep(ctx) {
13988
13988
  const transformedFiles = ctx.options.crossFileInfo?.transformedFiles;
13989
13989
  const skipImportedRoots = ctx.options.allowPartialMigration === true && ctx.options.transformMode !== "leavesOnly";
13990
13990
  const localStyledNames = new Set(styledDecls.map((d) => d.localName));
13991
+ let foundCascadeConflict = false;
13991
13992
  for (const decl of styledDecls) {
13992
13993
  if (decl.skipTransform) continue;
13993
13994
  if (decl.base.kind !== "component") continue;
@@ -14005,23 +14006,25 @@ function detectCascadeConflictStep(ctx) {
14005
14006
  if (ctx.options.transformMode === "leavesOnly" && ctx.options.globalLeafKeys?.size) {
14006
14007
  if (globalLeafKeyExists(ctx.options.globalLeafKeys, definition.path, definition.importedName, definition.importedName === "default")) continue;
14007
14008
  }
14008
- if (!(styledDefFiles && resolveStyledDefFile(definition.path, styledDefFiles) || scanFileForStyledDefs(definition.path, definition.importedName, ctx.options.resolveModule))) continue;
14009
+ const styledDefinitions = styledDefFiles && resolveStyledDefFile(definition.path, styledDefFiles) || scanFileForStyledDefs(definition.path, definition.importedName, ctx.options.resolveModule);
14010
+ if (!styledDefinitions) continue;
14009
14011
  ctx.warnings.push({
14010
14012
  severity: "warning",
14011
- type: "styled(ImportedComponent) wraps a component whose file uses styled-components — convert the base component's file first to avoid CSS cascade conflicts",
14013
+ type: CASCADE_CONFLICT_WARNING,
14012
14014
  loc: decl.loc,
14013
14015
  context: {
14014
14016
  component: decl.localName,
14015
14017
  base: baseIdent,
14016
14018
  importedPath,
14017
- definitionPath: definition.path
14019
+ definitionPath: styledDefinitions.path
14018
14020
  }
14019
14021
  });
14020
- return returnResult({
14021
- code: null,
14022
- warnings: ctx.warnings
14023
- }, "bail");
14022
+ foundCascadeConflict = true;
14024
14023
  }
14024
+ if (foundCascadeConflict) return returnResult({
14025
+ code: null,
14026
+ warnings: ctx.warnings
14027
+ }, "bail");
14025
14028
  return CONTINUE;
14026
14029
  }
14027
14030
  function resolveImportedDefinition(ctx, importedPath, importedName) {
@@ -14084,11 +14087,10 @@ function defaultReExportLeafKeyExists(keys, barrelPath, specifier) {
14084
14087
  * keys include the full extension. Try exact match first, then with common extensions.
14085
14088
  */
14086
14089
  function resolveStyledDefFile(importedPath, styledDefFiles) {
14087
- const exact = styledDefFiles.get(importedPath);
14088
- if (exact) return exact;
14090
+ if (styledDefFiles.get(importedPath)) return { path: importedPath };
14089
14091
  for (const ext of EXTENSIONS) {
14090
- const withExt = styledDefFiles.get(importedPath + ext);
14091
- if (withExt) return withExt;
14092
+ const pathWithExtension = importedPath + ext;
14093
+ if (styledDefFiles.get(pathWithExtension)) return { path: pathWithExtension };
14092
14094
  }
14093
14095
  }
14094
14096
  function pathSetContainsWithExtensionFallback(filePath, paths) {
@@ -14113,7 +14115,7 @@ function scanFileForStyledDefs(importedPath, importedName, resolveModule, visite
14113
14115
  STYLED_DEF_RE.lastIndex = 0;
14114
14116
  for (const m of source.matchAll(STYLED_DEF_RE)) if (m[1]) names.add(m[1]);
14115
14117
  }
14116
- if (names.size > 0) return names;
14118
+ if (names.size > 0) return { path: file.path };
14117
14119
  if (!importedName) return;
14118
14120
  for (const reExport of reExportPaths(source, file.path, importedName, resolveModule)) {
14119
14121
  const nested = scanFileForStyledDefs(reExport.path, reExport.importedName, resolveModule, visited);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "styled-components-to-stylex-codemod",
3
- "version": "0.0.49",
3
+ "version": "0.0.50",
4
4
  "description": "Codemod to transform styled-components to StyleX",
5
5
  "keywords": [
6
6
  "codemod",