styled-components-to-stylex-codemod 0.0.15 → 0.0.16

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/index.mjs CHANGED
@@ -194,7 +194,7 @@ async function runTransform(options) {
194
194
  ].join("\n"));
195
195
  const { createModuleResolver } = await import("./resolve-imports-BDk6Ms09.mjs");
196
196
  const sharedResolver = createModuleResolver();
197
- const { runPrepass } = await import("./run-prepass-BcidTT9f.mjs");
197
+ const { runPrepass } = await import("./run-prepass-DOMrzrA_.mjs");
198
198
  const absoluteFiles = filePaths.map((f) => resolve(f));
199
199
  const absoluteConsumers = consumerFilePaths.map((f) => resolve(f));
200
200
  let prepassResult;
@@ -237,8 +237,13 @@ function categorizeSelectorUsages(usages, componentsNeedingMarkerSidecar, compon
237
237
  else addToSetMap(componentsNeedingGlobalSelectorBridge, usage.resolvedPath, usage.importedName);
238
238
  }
239
239
  }
240
- /** Regex matching `export const XGlobalSelector = ".sc2sx-` pattern (global for matchAll). */
241
- const BRIDGE_EXPORT_RE = /export\s+const\s+(\w+GlobalSelector)\s*=\s*["']\.sc2sx-/g;
240
+ /**
241
+ * Regex matching bridge GlobalSelector export patterns (global for matchAll).
242
+ * Matches both:
243
+ * - Old format: `export const XGlobalSelector = ".sc2sx-..."`
244
+ * - New format: `` export const XGlobalSelector = `.${xBridgeClass}` ``
245
+ */
246
+ const BRIDGE_EXPORT_RE = /export\s+const\s+(\w+GlobalSelector)\s*=\s*(?:["']\.sc2sx-|`\.\$\{)/g;
242
247
  /**
243
248
  * Detect whether an imported name is a bridge GlobalSelector from an
244
249
  * already-converted StyleX file.
@@ -302,10 +307,7 @@ function walkForImportsAndTemplates(node, imports, templates) {
302
307
  imports.push(n);
303
308
  return;
304
309
  }
305
- if (n.type === "TaggedTemplateExpression") {
306
- templates.push(n);
307
- return;
308
- }
310
+ if (n.type === "TaggedTemplateExpression") templates.push(n);
309
311
  for (const key of Object.keys(n)) {
310
312
  if (key === "type" || key === "start" || key === "end" || key === "loc") continue;
311
313
  const val = n[key];
@@ -352,13 +354,32 @@ function findStyledImportNameFromNodes(importNodes) {
352
354
  }
353
355
  }
354
356
  /**
357
+ * Find local names of `css` imported from styled-components.
358
+ * Handles aliased imports like `import { css as sc } from "styled-components"`.
359
+ */
360
+ function findCssImportNamesFromNodes(importNodes) {
361
+ const names = /* @__PURE__ */ new Set();
362
+ for (const node of importNodes) {
363
+ if (node.source?.value !== "styled-components") continue;
364
+ const specifiers = node.specifiers;
365
+ if (!specifiers) continue;
366
+ for (const spec of specifiers) if (spec.type === "ImportSpecifier") {
367
+ if (getNodeName(spec.imported) === "css") {
368
+ const localName = getNodeName(spec.local);
369
+ if (localName) names.add(localName);
370
+ }
371
+ }
372
+ }
373
+ return names;
374
+ }
375
+ /**
355
376
  * Find local names of imported components used as selectors inside
356
- * styled-components template literals.
377
+ * styled-components template literals (both `styled` and `css` tagged templates).
357
378
  */
358
- function findComponentSelectorLocalsFromNodes(templateNodes, styledImportName) {
379
+ function findComponentSelectorLocalsFromNodes(templateNodes, styledImportName, cssImportNames) {
359
380
  const selectorLocals = /* @__PURE__ */ new Set();
360
381
  for (const node of templateNodes) {
361
- if (!isStyledTag(node.tag, styledImportName)) continue;
382
+ if (!isStyledTag(node.tag, styledImportName) && !isCssTag(node.tag, cssImportNames)) continue;
362
383
  const quasi = node.quasi;
363
384
  if (!quasi) continue;
364
385
  const quasis = quasi.quasis;
@@ -399,6 +420,11 @@ function isStyledTag(tag, styledName) {
399
420
  }
400
421
  return false;
401
422
  }
423
+ /** Check if a template tag is the `css` helper from styled-components. */
424
+ function isCssTag(tag, cssImportNames) {
425
+ if (!tag || !cssImportNames || cssImportNames.size === 0) return false;
426
+ return tag.type === "Identifier" && typeof tag.name === "string" && cssImportNames.has(tag.name);
427
+ }
402
428
  /** Check if a placeholder at the given position is in a CSS selector context. */
403
429
  function isPlaceholderInSelectorContext(rawCss, pos, length) {
404
430
  const after = rawCss.slice(pos + length).trimStart();
@@ -759,10 +785,12 @@ function scanFileForSelectorsAst(filePath, source, transformSet, resolver, parse
759
785
  const cached = cache && hash ? cache.get(hash) : void 0;
760
786
  let importMap;
761
787
  let styledImportName;
788
+ let cssImportNames;
762
789
  let selectorLocals;
763
790
  if (cached) {
764
791
  importMap = cached.importMap;
765
792
  styledImportName = cached.styledImportName;
793
+ cssImportNames = cached.cssImportNames;
766
794
  selectorLocals = cached.selectorLocals;
767
795
  } else {
768
796
  let ast;
@@ -781,14 +809,16 @@ function scanFileForSelectorsAst(filePath, source, transformSet, resolver, parse
781
809
  walkForImportsAndTemplates(program, importNodes, taggedTemplateNodes);
782
810
  importMap = buildImportMapFromNodes(importNodes);
783
811
  styledImportName = findStyledImportNameFromNodes(importNodes);
784
- selectorLocals = styledImportName ? findComponentSelectorLocalsFromNodes(taggedTemplateNodes, styledImportName) : /* @__PURE__ */ new Set();
812
+ cssImportNames = findCssImportNamesFromNodes(importNodes);
813
+ selectorLocals = styledImportName || cssImportNames.size > 0 ? findComponentSelectorLocalsFromNodes(taggedTemplateNodes, styledImportName ?? "", cssImportNames) : /* @__PURE__ */ new Set();
785
814
  if (cache && hash) cache.set(hash, {
786
815
  importMap,
787
816
  styledImportName,
817
+ cssImportNames,
788
818
  selectorLocals
789
819
  });
790
820
  }
791
- if (importMap.size === 0 || !styledImportName || selectorLocals.size === 0) return [];
821
+ if (importMap.size === 0 || !styledImportName && cssImportNames.size === 0 || selectorLocals.size === 0) return [];
792
822
  const consumerIsTransformed = transformSet.has(filePath);
793
823
  const usages = [];
794
824
  for (const localName of selectorLocals) {
@@ -1148,6 +1148,20 @@ function generateBridgeClassName(filePath, componentName) {
1148
1148
  function bridgeExportName(componentName) {
1149
1149
  return `${componentName}GlobalSelector`;
1150
1150
  }
1151
+ /**
1152
+ * Generate the internal const variable name for the bridge class value.
1153
+ * E.g., "Foo" → "fooBridgeClass", "ScrollableDiv" → "scrollableDivBridgeClass"
1154
+ */
1155
+ function bridgeClassVarName(componentName) {
1156
+ return `${componentName.charAt(0).toLowerCase()}${componentName.slice(1)}BridgeClass`;
1157
+ }
1158
+ /**
1159
+ * If a declaration has a bridge className, return the internal const variable
1160
+ * name that references it. Returns `undefined` when no bridge is needed.
1161
+ */
1162
+ function getBridgeClassVar(decl) {
1163
+ return decl.bridgeClassName ? bridgeClassVarName(decl.localName) : void 0;
1164
+ }
1151
1165
 
1152
1166
  //#endregion
1153
1167
  //#region src/internal/transform-steps/analyze-before-emit.ts
@@ -1232,15 +1246,7 @@ function analyzeBeforeEmitStep(ctx) {
1232
1246
  if (decl.base.kind === "intrinsic" && decl.withConfig?.componentId) decl.needsWrapperComponent = true;
1233
1247
  if (extendedBy.has(decl.localName) && componentsWithStaticProps.has(decl.localName)) decl.needsWrapperComponent = true;
1234
1248
  if (exportedComponents.has(decl.localName)) decl.needsWrapperComponent = true;
1235
- if (ctx.bridgeComponentNames?.has(decl.localName) || ctx.bridgeComponentNames?.has("default") && exportedComponents.get(decl.localName)?.isDefault) {
1236
- decl.bridgeClassName = generateBridgeClassName(resolve(file.path), decl.localName);
1237
- if (!decl.attrsInfo) decl.attrsInfo = {
1238
- staticAttrs: {},
1239
- conditionalAttrs: []
1240
- };
1241
- const existing = typeof decl.attrsInfo.staticAttrs.className === "string" ? decl.attrsInfo.staticAttrs.className + " " : "";
1242
- decl.attrsInfo.staticAttrs.className = existing + decl.bridgeClassName;
1243
- }
1249
+ if (ctx.bridgeComponentNames?.has(decl.localName) || ctx.bridgeComponentNames?.has("default") && exportedComponents.get(decl.localName)?.isDefault) decl.bridgeClassName = generateBridgeClassName(resolve(file.path), decl.localName);
1244
1250
  }
1245
1251
  const isUsedInJsx = (name) => isComponentUsedInJsx(root, j, name);
1246
1252
  const canInlineImportedComponentWrapper = (decl) => {
@@ -6077,17 +6083,28 @@ function emitBridgeExportsStep(ctx) {
6077
6083
  const { j, root } = ctx;
6078
6084
  const bridgeResults = [];
6079
6085
  for (const decl of bridgeDecls) {
6080
- const varName = bridgeExportName(decl.localName);
6086
+ const exportVarName = bridgeExportName(decl.localName);
6087
+ const internalVarName = bridgeClassVarName(decl.localName);
6081
6088
  const className = decl.bridgeClassName;
6082
- const declaration = j.variableDeclaration("const", [j.variableDeclarator(j.identifier(varName), j.stringLiteral(`.${className}`))]);
6083
- const exportDecl = j.exportNamedDeclaration(declaration);
6089
+ const internalDecl = j.variableDeclaration("const", [j.variableDeclarator(j.identifier(internalVarName), j.stringLiteral(className))]);
6090
+ const templateLiteral = j.templateLiteral([j.templateElement({
6091
+ raw: ".",
6092
+ cooked: "."
6093
+ }, false), j.templateElement({
6094
+ raw: "",
6095
+ cooked: ""
6096
+ }, true)], [j.identifier(internalVarName)]);
6097
+ const exportDeclaration = j.variableDeclaration("const", [j.variableDeclarator(j.identifier(exportVarName), templateLiteral)]);
6098
+ const exportDecl = j.exportNamedDeclaration(exportDeclaration);
6084
6099
  exportDecl.comments = [j.commentBlock("* @deprecated Migrate consumer to stylex.defineMarker() — bridge className for unconverted styled-components consumers ", true, false)];
6085
- root.find(j.Program).get().node.body.push(exportDecl);
6100
+ const body = root.find(j.Program).get().node.body;
6101
+ body.push(internalDecl);
6102
+ body.push(exportDecl);
6086
6103
  bridgeResults.push({
6087
6104
  componentName: decl.localName,
6088
6105
  exportName: ctx.exportedComponents?.get(decl.localName)?.exportName,
6089
6106
  className,
6090
- globalSelectorVarName: varName
6107
+ globalSelectorVarName: exportVarName
6091
6108
  });
6092
6109
  }
6093
6110
  ctx.bridgeResults = bridgeResults;
@@ -6836,7 +6853,8 @@ function emitSimpleWithConfigWrappers(ctx) {
6836
6853
  invertedBoolAttrs: d.attrsInfo?.invertedBoolAttrs ?? [],
6837
6854
  staticAttrs: d.attrsInfo?.staticAttrs ?? {},
6838
6855
  inlineStyleProps: d.inlineStyleProps ?? [],
6839
- attrsAsTag: d.attrsInfo?.attrsAsTag
6856
+ attrsAsTag: d.attrsInfo?.attrsAsTag,
6857
+ bridgeClassVar: getBridgeClassVar(d)
6840
6858
  }), d));
6841
6859
  continue;
6842
6860
  }
@@ -6852,7 +6870,7 @@ function emitSimpleWithConfigWrappers(ctx) {
6852
6870
  restId
6853
6871
  });
6854
6872
  const declStmt = j.variableDeclaration("const", [j.variableDeclarator(j.objectPattern(patternProps), propsId)]);
6855
- const { attrsInfo, staticClassNameExpr } = emitter.splitAttrsInfo(d.attrsInfo);
6873
+ const { attrsInfo, staticClassNameExpr } = emitter.splitAttrsInfo(d.attrsInfo, getBridgeClassVar(d));
6856
6874
  const merging = emitStyleMerging({
6857
6875
  j,
6858
6876
  emitter,
@@ -7146,7 +7164,7 @@ function emitSimpleExportedIntrinsicWrappers(ctx) {
7146
7164
  restId: restId ?? void 0
7147
7165
  });
7148
7166
  const declStmt = j.variableDeclaration("const", [j.variableDeclarator(j.objectPattern(patternProps), propsId)]);
7149
- const { attrsInfo, staticClassNameExpr } = emitter.splitAttrsInfo(d.attrsInfo);
7167
+ const { attrsInfo, staticClassNameExpr } = emitter.splitAttrsInfo(d.attrsInfo, getBridgeClassVar(d));
7150
7168
  const { attrsInfo: attrsInfoWithoutForwardedAsStatic, forwardedAsStaticFallback } = splitForwardedAsStaticAttrs({
7151
7169
  attrsInfo,
7152
7170
  includeForwardedAs: includesForwardedAs
@@ -7207,7 +7225,8 @@ function emitSimpleExportedIntrinsicWrappers(ctx) {
7207
7225
  invertedBoolAttrs: d.attrsInfo?.invertedBoolAttrs ?? [],
7208
7226
  staticAttrs: d.attrsInfo?.staticAttrs ?? {},
7209
7227
  inlineStyleProps: d.inlineStyleProps ?? [],
7210
- attrsAsTag: d.attrsInfo?.attrsAsTag
7228
+ attrsAsTag: d.attrsInfo?.attrsAsTag,
7229
+ bridgeClassVar: getBridgeClassVar(d)
7211
7230
  }), d));
7212
7231
  }
7213
7232
  }
@@ -7621,7 +7640,7 @@ function emitComponentWrappers(emitter) {
7621
7640
  if (!firstPart) jsxTagName = j.jsxIdentifier(renderedComponent);
7622
7641
  else jsxTagName = j.jsxMemberExpression(j.jsxIdentifier(firstPart), j.jsxIdentifier(parts.slice(1).join(".")));
7623
7642
  } else jsxTagName = j.jsxIdentifier(renderedComponent);
7624
- const { attrsInfo, staticClassNameExpr } = emitter.splitAttrsInfo(d.attrsInfo);
7643
+ const { attrsInfo, staticClassNameExpr } = emitter.splitAttrsInfo(d.attrsInfo, getBridgeClassVar(d));
7625
7644
  const defaultAttrs = attrsInfo?.defaultAttrs ?? [];
7626
7645
  const staticAttrs = attrsInfo?.staticAttrs ?? {};
7627
7646
  const needsSxVar = allowClassNameProp || allowStyleProp || !!d.inlineStyleProps?.length || !!staticClassNameExpr;
@@ -8566,7 +8585,7 @@ function emitIntrinsicPolymorphicWrappers(ctx) {
8566
8585
  }),
8567
8586
  j.restElement(restId)
8568
8587
  ]), propsId)]);
8569
- const { attrsInfo, staticClassNameExpr } = emitter.splitAttrsInfo(d.attrsInfo);
8588
+ const { attrsInfo, staticClassNameExpr } = emitter.splitAttrsInfo(d.attrsInfo, getBridgeClassVar(d));
8570
8589
  const { attrsInfo: attrsInfoWithoutForwardedAsStatic, forwardedAsStaticFallback } = splitForwardedAsStaticAttrs({
8571
8590
  attrsInfo,
8572
8591
  includeForwardedAs: includesForwardedAs
@@ -8840,7 +8859,7 @@ function emitShouldForwardPropWrappers(ctx) {
8840
8859
  });
8841
8860
  const declStmt = j.variableDeclaration("const", [j.variableDeclarator(j.objectPattern(patternProps), propsId)]);
8842
8861
  const cleanupPrefixStmt = dropPrefix && (isExportedComponent || shouldAllowAnyPrefixProps) && includeRest ? buildPrefixCleanupStatements(j, restId, dropPrefix) : null;
8843
- const { attrsInfo, staticClassNameExpr } = emitter.splitAttrsInfo(d.attrsInfo);
8862
+ const { attrsInfo, staticClassNameExpr } = emitter.splitAttrsInfo(d.attrsInfo, getBridgeClassVar(d));
8844
8863
  const { attrsInfo: attrsInfoWithoutForwardedAsStatic, forwardedAsStaticFallback } = splitForwardedAsStaticAttrs({
8845
8864
  attrsInfo,
8846
8865
  includeForwardedAs: includesForwardedAs
@@ -9280,32 +9299,59 @@ function splitExtraStyleArgs(j, stylesIdentifier, d) {
9280
9299
  };
9281
9300
  }
9282
9301
  /**
9302
+ * Build a static className expression combining an optional literal string
9303
+ * and/or a bridge class variable identifier.
9304
+ *
9305
+ * Returns `undefined` when neither is provided.
9306
+ */
9307
+ function buildStaticClassNameExpr(j, staticClassName, bridgeClassVar) {
9308
+ if (staticClassName && bridgeClassVar) {
9309
+ const raw = escapeTemplateRaw(`${staticClassName} `);
9310
+ return j.templateLiteral([j.templateElement({
9311
+ raw,
9312
+ cooked: `${staticClassName} `
9313
+ }, false), j.templateElement({
9314
+ raw: "",
9315
+ cooked: ""
9316
+ }, true)], [j.identifier(bridgeClassVar)]);
9317
+ }
9318
+ if (bridgeClassVar) return j.identifier(bridgeClassVar);
9319
+ if (staticClassName) return j.literal(staticClassName);
9320
+ }
9321
+ /**
9283
9322
  * Extracts a static className value (if present) from attrsInfo.staticAttrs
9284
9323
  * so it can be passed to the style merger separately, and returns the
9285
9324
  * remaining attrsInfo without that className entry.
9325
+ *
9326
+ * When `bridgeClassVar` is provided, it is used as an identifier expression
9327
+ * for the bridge class name. If a static className also exists, a template
9328
+ * literal combining both is produced.
9286
9329
  */
9287
- function splitAttrsInfo(j, attrsInfo) {
9330
+ function splitAttrsInfo(j, attrsInfo, bridgeClassVar) {
9288
9331
  const className = attrsInfo?.staticAttrs?.className;
9289
9332
  if (!attrsInfo) return {
9290
9333
  attrsInfo,
9291
- staticClassNameExpr: void 0
9334
+ staticClassNameExpr: buildStaticClassNameExpr(j, void 0, bridgeClassVar)
9292
9335
  };
9293
9336
  const normalized = {
9294
9337
  ...attrsInfo,
9295
9338
  staticAttrs: attrsInfo.staticAttrs ?? {},
9296
9339
  conditionalAttrs: attrsInfo.conditionalAttrs ?? []
9297
9340
  };
9298
- if (typeof className !== "string") return {
9341
+ const hasStaticClassName = typeof className === "string";
9342
+ if (!hasStaticClassName && !bridgeClassVar) return {
9299
9343
  attrsInfo: normalized,
9300
9344
  staticClassNameExpr: void 0
9301
9345
  };
9302
- const { className: _omit, ...rest } = normalized.staticAttrs;
9303
9346
  return {
9304
- attrsInfo: {
9305
- ...normalized,
9306
- staticAttrs: rest
9307
- },
9308
- staticClassNameExpr: j.literal(className)
9347
+ attrsInfo: hasStaticClassName ? (() => {
9348
+ const { className: _omit, ...rest } = normalized.staticAttrs;
9349
+ return {
9350
+ ...normalized,
9351
+ staticAttrs: rest
9352
+ };
9353
+ })() : normalized,
9354
+ staticClassNameExpr: buildStaticClassNameExpr(j, hasStaticClassName ? className : void 0, bridgeClassVar)
9309
9355
  };
9310
9356
  }
9311
9357
  /**
@@ -9499,6 +9545,10 @@ function collectDestructurePropsFromStyleFns(emitter, args) {
9499
9545
  }
9500
9546
  for (const prop of d.shouldForwardProp?.dropProps ?? []) if (prop && !destructureProps.includes(prop)) destructureProps.push(prop);
9501
9547
  }
9548
+ /** Escape characters that are special inside template literal quasi strings. */
9549
+ function escapeTemplateRaw(s) {
9550
+ return s.replace(/\\|`|\$\{/g, "\\$&");
9551
+ }
9502
9552
 
9503
9553
  //#endregion
9504
9554
  //#region src/internal/emit-wrappers/wrapper-emitter.ts
@@ -10014,7 +10064,7 @@ var WrapperEmitter = class {
10014
10064
  return false;
10015
10065
  }
10016
10066
  emitMinimalWrapper(args) {
10017
- const { localName, tagName, propsTypeName, inlineTypeText, styleArgs, destructureProps, propDefaults, allowClassNameProp = false, allowStyleProp = false, allowAsProp = false, includeRest = true, defaultAttrs = [], conditionalAttrs = [], invertedBoolAttrs = [], staticAttrs = {}, inlineStyleProps = [], attrsAsTag } = args;
10067
+ const { localName, tagName, propsTypeName, inlineTypeText, styleArgs, destructureProps, propDefaults, allowClassNameProp = false, allowStyleProp = false, allowAsProp = false, includeRest = true, defaultAttrs = [], conditionalAttrs = [], invertedBoolAttrs = [], staticAttrs = {}, inlineStyleProps = [], attrsAsTag, bridgeClassVar } = args;
10018
10068
  const { j } = this;
10019
10069
  const expandedDestructureProps = new Set(destructureProps.filter(Boolean));
10020
10070
  const collectCondIdentifiers = (node) => {
@@ -10097,6 +10147,7 @@ var WrapperEmitter = class {
10097
10147
  const { className: _omitClassName, ...rest } = staticAttrs;
10098
10148
  return rest;
10099
10149
  })();
10150
+ const staticClassNameExpr = buildStaticClassNameExpr(j, staticClassName, bridgeClassVar);
10100
10151
  const merging = emitStyleMerging({
10101
10152
  j,
10102
10153
  emitter: this,
@@ -10106,7 +10157,7 @@ var WrapperEmitter = class {
10106
10157
  allowClassNameProp,
10107
10158
  allowStyleProp,
10108
10159
  inlineStyleProps,
10109
- staticClassNameExpr: staticClassName ? j.literal(staticClassName) : void 0
10160
+ staticClassNameExpr
10110
10161
  });
10111
10162
  const jsxAttrs = [];
10112
10163
  for (const a of defaultAttrs) jsxAttrs.push(j.jsxAttribute(j.jsxIdentifier(a.attrName), j.jsxExpressionContainer(j.logicalExpression("??", j.identifier(a.jsxProp), this.literalExpr(a.value)))));
@@ -10187,8 +10238,8 @@ var WrapperEmitter = class {
10187
10238
  splitExtraStyleArgs(d) {
10188
10239
  return splitExtraStyleArgs(this.j, this.stylesIdentifier, d);
10189
10240
  }
10190
- splitAttrsInfo(attrsInfo) {
10191
- return splitAttrsInfo(this.j, attrsInfo);
10241
+ splitAttrsInfo(attrsInfo, bridgeClassVar) {
10242
+ return splitAttrsInfo(this.j, attrsInfo, bridgeClassVar);
10192
10243
  }
10193
10244
  buildVariantDimensionLookups(args) {
10194
10245
  buildVariantDimensionLookups(this.j, args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "styled-components-to-stylex-codemod",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "description": "Codemod to transform styled-components to StyleX",
5
5
  "keywords": [
6
6
  "codemod",