@typestyles/migrate 0.0.0-unstable.439960c19a5a → 0.0.0-unstable.446478552a48

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/cli.cjs CHANGED
@@ -9,7 +9,7 @@ var picomatch = require('picomatch');
9
9
  var parser = require('@babel/parser');
10
10
  var generate = require('@babel/generator');
11
11
  var traverse = require('@babel/traverse');
12
- var t2 = require('@babel/types');
12
+ var t4 = require('@babel/types');
13
13
  var postcss = require('postcss');
14
14
 
15
15
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -36,7 +36,7 @@ var fg__default = /*#__PURE__*/_interopDefault(fg);
36
36
  var picomatch__default = /*#__PURE__*/_interopDefault(picomatch);
37
37
  var generate__default = /*#__PURE__*/_interopDefault(generate);
38
38
  var traverse__default = /*#__PURE__*/_interopDefault(traverse);
39
- var t2__namespace = /*#__PURE__*/_interopNamespace(t2);
39
+ var t4__namespace = /*#__PURE__*/_interopNamespace(t4);
40
40
  var postcss__default = /*#__PURE__*/_interopDefault(postcss);
41
41
 
42
42
  var DEFAULT_EXCLUDES = ["**/node_modules/**", "**/dist/**", "**/.next/**", "**/.turbo/**"];
@@ -78,42 +78,53 @@ async function collectTargetFiles(cwd, targets, extensions, include, exclude) {
78
78
  function camelCaseProperty(property) {
79
79
  return property.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
80
80
  }
81
- function toValueNode(value) {
81
+ function toValueNode(value, varReplacements) {
82
82
  const trimmed = value.trim();
83
+ if (varReplacements?.has(trimmed)) {
84
+ return varReplacements.get(trimmed);
85
+ }
83
86
  if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
84
- return t2__namespace.numericLiteral(Number(trimmed));
87
+ return t4__namespace.numericLiteral(Number(trimmed));
85
88
  }
86
- return t2__namespace.stringLiteral(trimmed);
89
+ return t4__namespace.stringLiteral(trimmed);
87
90
  }
88
91
  function toKeyNode(key) {
89
92
  if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
90
- return t2__namespace.identifier(key);
93
+ return t4__namespace.identifier(key);
91
94
  }
92
- return t2__namespace.stringLiteral(key);
95
+ return t4__namespace.stringLiteral(key);
93
96
  }
94
- function nodesToObject(nodes, warnings) {
97
+ function nodesToObject(nodes, warnings, varReplacements) {
95
98
  const properties = [];
96
99
  for (const node of nodes ?? []) {
97
100
  if (node.type === "decl") {
98
101
  const normalized = node.prop.startsWith("--") ? node.prop : camelCaseProperty(node.prop);
99
- properties.push(t2__namespace.objectProperty(toKeyNode(normalized), toValueNode(node.value)));
102
+ properties.push(
103
+ t4__namespace.objectProperty(toKeyNode(normalized), toValueNode(node.value, varReplacements))
104
+ );
100
105
  continue;
101
106
  }
102
107
  if (node.type === "rule") {
103
- const nested = nodesToObject(node.nodes, warnings);
104
- properties.push(t2__namespace.objectProperty(t2__namespace.stringLiteral(node.selector.trim()), nested));
108
+ const nested = nodesToObject(node.nodes, warnings, varReplacements);
109
+ properties.push(t4__namespace.objectProperty(t4__namespace.stringLiteral(node.selector.trim()), nested));
110
+ continue;
111
+ }
112
+ if (node.type === "atrule") {
113
+ const nested = nodesToObject(node.nodes, warnings, varReplacements);
114
+ const atRuleKey = `@${node.name}${node.params ? ` ${node.params}` : ""}`;
115
+ properties.push(t4__namespace.objectProperty(t4__namespace.stringLiteral(atRuleKey), nested));
105
116
  continue;
106
117
  }
107
118
  warnings.push({
108
119
  message: `Unsupported CSS node "${node.type}" skipped.`
109
120
  });
110
121
  }
111
- return t2__namespace.objectExpression(properties);
122
+ return t4__namespace.objectExpression(properties);
112
123
  }
113
- function cssToObjectExpression(cssText, warnings) {
124
+ function cssToObjectExpression(cssText, warnings, varReplacements) {
114
125
  try {
115
126
  const root = postcss__default.default.parse(cssText);
116
- return nodesToObject(root.nodes, warnings);
127
+ return nodesToObject(root.nodes, warnings, varReplacements);
117
128
  } catch (error) {
118
129
  warnings.push({
119
130
  message: `Could not parse CSS template literal: ${error.message}`
@@ -121,30 +132,226 @@ function cssToObjectExpression(cssText, warnings) {
121
132
  return null;
122
133
  }
123
134
  }
135
+ var PLACEHOLDER_PREFIX = "__ts_migrate_var_";
136
+ function camelCaseProperty2(property) {
137
+ return property.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
138
+ }
139
+ function placeholderToken(index) {
140
+ return `${PLACEHOLDER_PREFIX}${index}__`;
141
+ }
142
+ function extractSuffixFromQuasi(quasiText) {
143
+ const match = quasiText.match(/^([^;\n]*)/);
144
+ return match?.[1] ?? "";
145
+ }
146
+ function literalToCssValue(node) {
147
+ if (t4__namespace.isStringLiteral(node)) return node.value;
148
+ if (t4__namespace.isNumericLiteral(node)) return String(node.value);
149
+ return null;
150
+ }
151
+ function unwrapExpression(expression) {
152
+ if (t4__namespace.isParenthesizedExpression(expression)) {
153
+ return unwrapExpression(expression.expression);
154
+ }
155
+ return expression;
156
+ }
157
+ function extractCssPropertyBeforeInterpolation(quasiText) {
158
+ const match = quasiText.match(/([a-zA-Z-]+)\s*:\s*$/);
159
+ return match?.[1] ?? null;
160
+ }
161
+ function stripTrailingPropertyDeclaration(quasiText) {
162
+ return quasiText.replace(/[a-zA-Z-]+\s*:\s*$/, "");
163
+ }
164
+ function stripLeadingDeclarationRemainder(quasiText) {
165
+ const semiIdx = quasiText.indexOf(";");
166
+ if (semiIdx === -1) return quasiText;
167
+ return quasiText.slice(semiIdx + 1).trimStart();
168
+ }
169
+ function parsePropInterpolation(expression) {
170
+ if (!t4__namespace.isArrowFunctionExpression(expression)) return null;
171
+ const param = expression.params[0];
172
+ if (!param) return null;
173
+ const body = unwrapExpression(expression.body);
174
+ if (!t4__namespace.isExpression(body)) return null;
175
+ if (t4__namespace.isIdentifier(param)) {
176
+ if (t4__namespace.isMemberExpression(body) && !body.computed && t4__namespace.isIdentifier(body.object, { name: param.name }) && t4__namespace.isIdentifier(body.property)) {
177
+ return { propName: body.property.name };
178
+ }
179
+ return null;
180
+ }
181
+ if (t4__namespace.isObjectPattern(param) && t4__namespace.isIdentifier(body)) {
182
+ const propName = body.name;
183
+ const hasProp = param.properties.some((property) => {
184
+ if (!t4__namespace.isObjectProperty(property)) return false;
185
+ const keyName = t4__namespace.isIdentifier(property.key) ? property.key.name : t4__namespace.isStringLiteral(property.key) ? property.key.value : null;
186
+ return keyName === propName;
187
+ });
188
+ if (!hasProp) return null;
189
+ return { propName };
190
+ }
191
+ return null;
192
+ }
193
+ function parseBooleanTernaryInterpolation(expression) {
194
+ if (!t4__namespace.isArrowFunctionExpression(expression)) return null;
195
+ const param = expression.params[0];
196
+ if (!t4__namespace.isIdentifier(param)) return null;
197
+ const body = unwrapExpression(expression.body);
198
+ if (!t4__namespace.isConditionalExpression(body)) return null;
199
+ if (!t4__namespace.isMemberExpression(body.test) || body.test.computed || !t4__namespace.isIdentifier(body.test.object, { name: param.name }) || !t4__namespace.isIdentifier(body.test.property)) {
200
+ return null;
201
+ }
202
+ const trueValue = literalToCssValue(body.consequent);
203
+ const falseValue = literalToCssValue(body.alternate);
204
+ if (trueValue === null || falseValue === null) return null;
205
+ return {
206
+ propName: body.test.property.name,
207
+ trueValue,
208
+ falseValue
209
+ };
210
+ }
211
+ function parseTemplateInterpolations(template) {
212
+ const interpolations = [];
213
+ for (let i = 0; i < template.expressions.length; i++) {
214
+ const expression = template.expressions[i];
215
+ if (!t4__namespace.isExpression(expression)) return null;
216
+ const parsed = parsePropInterpolation(expression);
217
+ if (!parsed) return null;
218
+ const followingQuasi = template.quasis[i + 1]?.value.cooked ?? "";
219
+ interpolations.push({
220
+ index: i,
221
+ propName: parsed.propName,
222
+ suffix: extractSuffixFromQuasi(followingQuasi)
223
+ });
224
+ }
225
+ return interpolations;
226
+ }
227
+ function parseBooleanTemplateInterpolations(template) {
228
+ const interpolations = [];
229
+ for (let i = 0; i < template.expressions.length; i++) {
230
+ const expression = template.expressions[i];
231
+ if (!t4__namespace.isExpression(expression)) return null;
232
+ const parsed = parseBooleanTernaryInterpolation(expression);
233
+ if (!parsed) return null;
234
+ const quasiBefore = template.quasis[i]?.value.cooked ?? "";
235
+ const cssProperty = extractCssPropertyBeforeInterpolation(quasiBefore);
236
+ if (!cssProperty) return null;
237
+ interpolations.push({
238
+ index: i,
239
+ propName: parsed.propName,
240
+ trueValue: parsed.trueValue,
241
+ falseValue: parsed.falseValue,
242
+ cssProperty: camelCaseProperty2(cssProperty)
243
+ });
244
+ }
245
+ return interpolations;
246
+ }
247
+ function reconstructInterpolatedCss(template, interpolations) {
248
+ let result = "";
249
+ for (let i = 0; i < template.quasis.length; i++) {
250
+ let quasiText = template.quasis[i].value.cooked ?? "";
251
+ if (i > 0) {
252
+ const interpolation = interpolations[i - 1];
253
+ if (interpolation?.suffix && quasiText.startsWith(interpolation.suffix)) {
254
+ quasiText = quasiText.slice(interpolation.suffix.length);
255
+ }
256
+ }
257
+ result += quasiText;
258
+ if (i < interpolations.length) {
259
+ result += placeholderToken(interpolations[i].index);
260
+ }
261
+ }
262
+ return result;
263
+ }
264
+ function reconstructStaticCssWithoutVariants(template, booleanInterpolations) {
265
+ const skipIndices = new Set(booleanInterpolations.map((interpolation) => interpolation.index));
266
+ let result = "";
267
+ for (let i = 0; i < template.quasis.length; i++) {
268
+ let quasiText = template.quasis[i].value.cooked ?? "";
269
+ if (skipIndices.has(i)) {
270
+ quasiText = stripTrailingPropertyDeclaration(quasiText);
271
+ }
272
+ if (i > 0 && skipIndices.has(i - 1)) {
273
+ quasiText = stripLeadingDeclarationRemainder(quasiText);
274
+ }
275
+ result += quasiText;
276
+ }
277
+ return result.trim();
278
+ }
279
+ function toVarDebugName(componentName, propName) {
280
+ return `${componentName}${propName.charAt(0).toUpperCase()}${propName.slice(1)}`;
281
+ }
282
+ function toComponentConstName(componentName) {
283
+ if (!componentName) return componentName;
284
+ return componentName.charAt(0).toLowerCase() + componentName.slice(1);
285
+ }
286
+ function toCssValueNode(value) {
287
+ if (/^-?\d+(\.\d+)?$/.test(value.trim())) {
288
+ return t4__namespace.numericLiteral(Number(value));
289
+ }
290
+ return t4__namespace.stringLiteral(value);
291
+ }
292
+ function toKeyNode2(key) {
293
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key)) {
294
+ return t4__namespace.identifier(key);
295
+ }
296
+ return t4__namespace.stringLiteral(key);
297
+ }
298
+ function buildVariantStyles(properties) {
299
+ return t4__namespace.objectExpression(
300
+ Object.entries(properties).map(
301
+ ([key, value]) => t4__namespace.objectProperty(toKeyNode2(key), toCssValueNode(value))
302
+ )
303
+ );
304
+ }
305
+ function buildVariantComponentConfig(baseObject, booleanInterpolations) {
306
+ const variantGroups = /* @__PURE__ */ new Map();
307
+ for (const interpolation of booleanInterpolations) {
308
+ const group = variantGroups.get(interpolation.propName) ?? { true: {}, false: {} };
309
+ group.true[interpolation.cssProperty] = interpolation.trueValue;
310
+ group.false[interpolation.cssProperty] = interpolation.falseValue;
311
+ variantGroups.set(interpolation.propName, group);
312
+ }
313
+ const variantsProperties = [...variantGroups.entries()].map(
314
+ ([propName, values]) => t4__namespace.objectProperty(
315
+ t4__namespace.identifier(propName),
316
+ t4__namespace.objectExpression([
317
+ t4__namespace.objectProperty(t4__namespace.identifier("true"), buildVariantStyles(values.true)),
318
+ t4__namespace.objectProperty(t4__namespace.identifier("false"), buildVariantStyles(values.false))
319
+ ])
320
+ )
321
+ );
322
+ const defaultVariantProperties = [...variantGroups.keys()].map(
323
+ (propName) => t4__namespace.objectProperty(t4__namespace.identifier(propName), t4__namespace.booleanLiteral(false))
324
+ );
325
+ return t4__namespace.objectExpression([
326
+ t4__namespace.objectProperty(t4__namespace.identifier("base"), baseObject),
327
+ t4__namespace.objectProperty(t4__namespace.identifier("variants"), t4__namespace.objectExpression(variantsProperties)),
328
+ t4__namespace.objectProperty(t4__namespace.identifier("defaultVariants"), t4__namespace.objectExpression(defaultVariantProperties))
329
+ ]);
330
+ }
124
331
 
125
332
  // src/transform.ts
126
333
  function toKebabCase(input) {
127
334
  return input.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[_\s]+/g, "-").toLowerCase();
128
335
  }
129
336
  function memberExpressionToJsxName(expression) {
130
- if (t2__namespace.isIdentifier(expression)) {
131
- return t2__namespace.jsxIdentifier(expression.name);
337
+ if (t4__namespace.isIdentifier(expression)) {
338
+ return t4__namespace.jsxIdentifier(expression.name);
132
339
  }
133
- if (t2__namespace.isMemberExpression(expression) && !expression.computed) {
340
+ if (t4__namespace.isMemberExpression(expression) && !expression.computed) {
134
341
  const left = memberExpressionToJsxName(expression.object);
135
- const right = t2__namespace.isIdentifier(expression.property) ? t2__namespace.jsxIdentifier(expression.property.name) : null;
342
+ const right = t4__namespace.isIdentifier(expression.property) ? t4__namespace.jsxIdentifier(expression.property.name) : null;
136
343
  if (!left || !right) return null;
137
- return t2__namespace.jsxMemberExpression(left, right);
344
+ return t4__namespace.jsxMemberExpression(left, right);
138
345
  }
139
346
  return null;
140
347
  }
141
348
  function parseStyledTarget(tag, styledNames) {
142
- if (t2__namespace.isMemberExpression(tag) && t2__namespace.isIdentifier(tag.object) && styledNames.has(tag.object.name) && t2__namespace.isIdentifier(tag.property)) {
143
- return { kind: "intrinsic", jsxName: t2__namespace.jsxIdentifier(tag.property.name) };
349
+ if (t4__namespace.isMemberExpression(tag) && t4__namespace.isIdentifier(tag.object) && styledNames.has(tag.object.name) && t4__namespace.isIdentifier(tag.property)) {
350
+ return { kind: "intrinsic", jsxName: t4__namespace.jsxIdentifier(tag.property.name) };
144
351
  }
145
- if (t2__namespace.isCallExpression(tag) && t2__namespace.isIdentifier(tag.callee) && styledNames.has(tag.callee.name)) {
352
+ if (t4__namespace.isCallExpression(tag) && t4__namespace.isIdentifier(tag.callee) && styledNames.has(tag.callee.name)) {
146
353
  const firstArg = tag.arguments[0];
147
- if (!firstArg || !t2__namespace.isExpression(firstArg)) return null;
354
+ if (!firstArg || !t4__namespace.isExpression(firstArg)) return null;
148
355
  const jsxName = memberExpressionToJsxName(firstArg);
149
356
  if (!jsxName) return null;
150
357
  return { kind: "component", jsxName };
@@ -154,75 +361,118 @@ function parseStyledTarget(tag, styledNames) {
154
361
  function addWarning(warnings, message, nodeName) {
155
362
  warnings.push({ message, nodeName });
156
363
  }
157
- function createMergedClassExpression(existing, classNameIdentifier) {
158
- return t2__namespace.callExpression(
159
- t2__namespace.memberExpression(
160
- t2__namespace.callExpression(
161
- t2__namespace.memberExpression(
162
- t2__namespace.arrayExpression([existing, classNameIdentifier]),
163
- t2__namespace.identifier("filter")
364
+ function createMergedClassExpression(existing, classNameExpression) {
365
+ return t4__namespace.callExpression(
366
+ t4__namespace.memberExpression(
367
+ t4__namespace.callExpression(
368
+ t4__namespace.memberExpression(
369
+ t4__namespace.arrayExpression([existing, classNameExpression]),
370
+ t4__namespace.identifier("filter")
164
371
  ),
165
- [t2__namespace.identifier("Boolean")]
372
+ [t4__namespace.identifier("Boolean")]
166
373
  ),
167
- t2__namespace.identifier("join")
374
+ t4__namespace.identifier("join")
168
375
  ),
169
- [t2__namespace.stringLiteral(" ")]
376
+ [t4__namespace.stringLiteral(" ")]
170
377
  );
171
378
  }
172
- function updateClassNameAttribute(openingElement, classNameIdentifier) {
379
+ function updateClassNameAttribute(openingElement, classNameExpression) {
173
380
  const existingAttr = openingElement.attributes.find(
174
- (attribute) => t2__namespace.isJSXAttribute(attribute) && t2__namespace.isJSXIdentifier(attribute.name, { name: "className" })
381
+ (attribute) => t4__namespace.isJSXAttribute(attribute) && t4__namespace.isJSXIdentifier(attribute.name, { name: "className" })
175
382
  );
176
383
  if (!existingAttr) {
177
384
  openingElement.attributes.push(
178
- t2__namespace.jsxAttribute(t2__namespace.jsxIdentifier("className"), t2__namespace.jsxExpressionContainer(classNameIdentifier))
385
+ t4__namespace.jsxAttribute(t4__namespace.jsxIdentifier("className"), t4__namespace.jsxExpressionContainer(classNameExpression))
179
386
  );
180
387
  return;
181
388
  }
182
389
  if (!existingAttr.value) {
183
- existingAttr.value = t2__namespace.jsxExpressionContainer(classNameIdentifier);
390
+ existingAttr.value = t4__namespace.jsxExpressionContainer(classNameExpression);
184
391
  return;
185
392
  }
186
- if (t2__namespace.isStringLiteral(existingAttr.value)) {
187
- existingAttr.value = t2__namespace.jsxExpressionContainer(
188
- createMergedClassExpression(t2__namespace.stringLiteral(existingAttr.value.value), classNameIdentifier)
393
+ if (t4__namespace.isStringLiteral(existingAttr.value)) {
394
+ existingAttr.value = t4__namespace.jsxExpressionContainer(
395
+ createMergedClassExpression(t4__namespace.stringLiteral(existingAttr.value.value), classNameExpression)
189
396
  );
190
397
  return;
191
398
  }
192
- if (t2__namespace.isJSXExpressionContainer(existingAttr.value)) {
193
- existingAttr.value = t2__namespace.jsxExpressionContainer(
399
+ if (t4__namespace.isJSXExpressionContainer(existingAttr.value)) {
400
+ existingAttr.value = t4__namespace.jsxExpressionContainer(
194
401
  createMergedClassExpression(
195
402
  existingAttr.value.expression,
196
- classNameIdentifier
403
+ classNameExpression
197
404
  )
198
405
  );
199
406
  }
200
407
  }
201
- function ensureStylesImport(ast) {
202
- let typestylesImport = null;
408
+ function hasLocalBinding(ast, name) {
203
409
  for (const statement of ast.program.body) {
204
- if (t2__namespace.isImportDeclaration(statement) && statement.source.value === "typestyles") {
205
- typestylesImport = statement;
206
- break;
410
+ if (!t4__namespace.isVariableDeclaration(statement)) continue;
411
+ for (const declarator of statement.declarations) {
412
+ if (t4__namespace.isIdentifier(declarator.id) && declarator.id.name === name) return true;
413
+ if (t4__namespace.isObjectPattern(declarator.id)) {
414
+ for (const prop of declarator.id.properties) {
415
+ if (t4__namespace.isObjectProperty(prop) && t4__namespace.isIdentifier(prop.value) && prop.value.name === name) {
416
+ return true;
417
+ }
418
+ }
419
+ }
420
+ }
421
+ }
422
+ return false;
423
+ }
424
+ function ensureTypestylesRuntime(ast, needs) {
425
+ const requiredSpecifiers = /* @__PURE__ */ new Set(["createTypeStyles"]);
426
+ if (needs.vars) {
427
+ requiredSpecifiers.add("createVar");
428
+ requiredSpecifiers.add("assignVars");
429
+ }
430
+ let typestylesImport = null;
431
+ let lastImportIndex = -1;
432
+ for (let i = 0; i < ast.program.body.length; i++) {
433
+ const statement = ast.program.body[i];
434
+ if (t4__namespace.isImportDeclaration(statement)) {
435
+ lastImportIndex = i;
436
+ if (statement.source.value === "typestyles" && typestylesImport == null) {
437
+ typestylesImport = statement;
438
+ }
207
439
  }
208
440
  }
441
+ const buildSpecifiers = (names) => [...names].map((name) => t4__namespace.importSpecifier(t4__namespace.identifier(name), t4__namespace.identifier(name)));
209
442
  if (!typestylesImport) {
210
443
  ast.program.body.unshift(
211
- t2__namespace.importDeclaration(
212
- [t2__namespace.importSpecifier(t2__namespace.identifier("styles"), t2__namespace.identifier("styles"))],
213
- t2__namespace.stringLiteral("typestyles")
214
- )
444
+ t4__namespace.importDeclaration(buildSpecifiers(requiredSpecifiers), t4__namespace.stringLiteral("typestyles"))
215
445
  );
216
- return;
446
+ lastImportIndex = 0;
447
+ } else {
448
+ for (const name of requiredSpecifiers) {
449
+ const hasSpecifier = typestylesImport.specifiers.some(
450
+ (specifier) => t4__namespace.isImportSpecifier(specifier) && t4__namespace.isIdentifier(specifier.imported, { name }) && t4__namespace.isIdentifier(specifier.local, { name })
451
+ );
452
+ if (!hasSpecifier) {
453
+ typestylesImport.specifiers.push(t4__namespace.importSpecifier(t4__namespace.identifier(name), t4__namespace.identifier(name)));
454
+ }
455
+ }
217
456
  }
218
- const hasStylesSpecifier = typestylesImport.specifiers.some(
219
- (specifier) => t2__namespace.isImportSpecifier(specifier) && t2__namespace.isIdentifier(specifier.imported, { name: "styles" }) && t2__namespace.isIdentifier(specifier.local, { name: "styles" })
220
- );
221
- if (!hasStylesSpecifier) {
222
- typestylesImport.specifiers.push(
223
- t2__namespace.importSpecifier(t2__namespace.identifier("styles"), t2__namespace.identifier("styles"))
457
+ const destructureProps = [];
458
+ if (needs.style && !hasLocalBinding(ast, "style")) {
459
+ destructureProps.push(
460
+ t4__namespace.objectProperty(t4__namespace.identifier("style"), t4__namespace.identifier("style"), false, true)
224
461
  );
225
462
  }
463
+ if (needs.recipe && !hasLocalBinding(ast, "recipe")) {
464
+ destructureProps.push(
465
+ t4__namespace.objectProperty(t4__namespace.identifier("recipe"), t4__namespace.identifier("recipe"), false, true)
466
+ );
467
+ }
468
+ if (destructureProps.length === 0) return;
469
+ const factoryDecl = t4__namespace.variableDeclaration("const", [
470
+ t4__namespace.variableDeclarator(
471
+ t4__namespace.objectPattern(destructureProps),
472
+ t4__namespace.callExpression(t4__namespace.identifier("createTypeStyles"), [])
473
+ )
474
+ ]);
475
+ ast.program.body.splice(lastImportIndex + 1, 0, factoryDecl);
226
476
  }
227
477
  function cleanupUnusedImports(ast) {
228
478
  traverse__default.default(ast, {
@@ -231,7 +481,7 @@ function cleanupUnusedImports(ast) {
231
481
  },
232
482
  ImportDeclaration(path) {
233
483
  const unusedLocals = path.node.specifiers.filter((specifier) => {
234
- if (path.node.source.value === "typestyles" && t2__namespace.isImportSpecifier(specifier) && t2__namespace.isIdentifier(specifier.local, { name: "styles" })) {
484
+ if (path.node.source.value === "typestyles" && t4__namespace.isImportSpecifier(specifier) && (specifier.local.name === "createTypeStyles" || specifier.local.name === "createVar" || specifier.local.name === "assignVars")) {
235
485
  return false;
236
486
  }
237
487
  const local = specifier.local.name;
@@ -248,6 +498,177 @@ function cleanupUnusedImports(ast) {
248
498
  }
249
499
  });
250
500
  }
501
+ function createAssignVarValue(propExpression, suffix) {
502
+ if (!suffix) return propExpression;
503
+ if (t4__namespace.isStringLiteral(propExpression)) {
504
+ return t4__namespace.stringLiteral(`${propExpression.value}${suffix}`);
505
+ }
506
+ return t4__namespace.binaryExpression("+", propExpression, t4__namespace.stringLiteral(suffix));
507
+ }
508
+ function buildAssignVarsCall(propVars, propValues) {
509
+ const properties = propVars.filter((binding) => propValues.has(binding.propName)).map((binding) => {
510
+ const propExpression = propValues.get(binding.propName);
511
+ return t4__namespace.objectProperty(
512
+ t4__namespace.identifier(binding.varConstName),
513
+ createAssignVarValue(propExpression, binding.suffix),
514
+ true
515
+ );
516
+ });
517
+ return t4__namespace.callExpression(t4__namespace.identifier("assignVars"), [t4__namespace.objectExpression(properties)]);
518
+ }
519
+ function mergeStyleExpressions(existing, assignVarsCall) {
520
+ return t4__namespace.objectExpression([t4__namespace.spreadElement(existing), t4__namespace.spreadElement(assignVarsCall)]);
521
+ }
522
+ function updateStyleAttribute(openingElement, assignVarsCall) {
523
+ const existingAttr = openingElement.attributes.find(
524
+ (attribute) => t4__namespace.isJSXAttribute(attribute) && t4__namespace.isJSXIdentifier(attribute.name, { name: "style" })
525
+ );
526
+ if (!existingAttr) {
527
+ openingElement.attributes.push(
528
+ t4__namespace.jsxAttribute(t4__namespace.jsxIdentifier("style"), t4__namespace.jsxExpressionContainer(assignVarsCall))
529
+ );
530
+ return;
531
+ }
532
+ if (!existingAttr.value) {
533
+ existingAttr.value = t4__namespace.jsxExpressionContainer(assignVarsCall);
534
+ return;
535
+ }
536
+ if (t4__namespace.isJSXExpressionContainer(existingAttr.value)) {
537
+ existingAttr.value = t4__namespace.jsxExpressionContainer(
538
+ mergeStyleExpressions(existingAttr.value.expression, assignVarsCall)
539
+ );
540
+ }
541
+ }
542
+ function collectPropValuesFromJsx(openingElement, propNames) {
543
+ const values = /* @__PURE__ */ new Map();
544
+ for (const attribute of openingElement.attributes) {
545
+ if (!t4__namespace.isJSXAttribute(attribute) || !t4__namespace.isJSXIdentifier(attribute.name)) continue;
546
+ if (!propNames.has(attribute.name.name)) continue;
547
+ if (!attribute.value) {
548
+ values.set(attribute.name.name, t4__namespace.booleanLiteral(true));
549
+ continue;
550
+ }
551
+ if (t4__namespace.isStringLiteral(attribute.value)) {
552
+ values.set(attribute.name.name, t4__namespace.stringLiteral(attribute.value.value));
553
+ continue;
554
+ }
555
+ if (t4__namespace.isJSXExpressionContainer(attribute.value)) {
556
+ values.set(attribute.name.name, attribute.value.expression);
557
+ }
558
+ }
559
+ return values;
560
+ }
561
+ function removeStyledProps(openingElement, propNames) {
562
+ openingElement.attributes = openingElement.attributes.filter((attribute) => {
563
+ if (!t4__namespace.isJSXAttribute(attribute) || !t4__namespace.isJSXIdentifier(attribute.name)) return true;
564
+ return !propNames.has(attribute.name.name);
565
+ });
566
+ }
567
+ function buildComponentClassNameExpression(componentConstName, variantProps, propValues) {
568
+ const properties = variantProps.filter((propName) => propValues.has(propName)).map((propName) => t4__namespace.objectProperty(t4__namespace.identifier(propName), propValues.get(propName)));
569
+ if (properties.length === 0) {
570
+ return t4__namespace.callExpression(t4__namespace.identifier(componentConstName), []);
571
+ }
572
+ return t4__namespace.callExpression(t4__namespace.identifier(componentConstName), [t4__namespace.objectExpression(properties)]);
573
+ }
574
+ function migrateBooleanVariantTemplate(path, variableName, template, styledTarget, warnings, styledTransforms) {
575
+ const booleanInterpolations = parseBooleanTemplateInterpolations(template);
576
+ if (!booleanInterpolations) return false;
577
+ const staticCss = reconstructStaticCssWithoutVariants(template, booleanInterpolations);
578
+ const baseObject = cssToObjectExpression(staticCss, warnings);
579
+ if (!baseObject) {
580
+ addWarning(warnings, "Skipped because CSS could not be parsed.", variableName);
581
+ return false;
582
+ }
583
+ const componentConstName = path.scope.generateUidIdentifier(
584
+ toComponentConstName(variableName)
585
+ ).name;
586
+ const componentConfig = buildVariantComponentConfig(baseObject, booleanInterpolations);
587
+ const variantProps = [
588
+ ...new Set(booleanInterpolations.map((interpolation) => interpolation.propName))
589
+ ];
590
+ const declaration = path.parentPath;
591
+ if (!declaration.isVariableDeclaration()) return false;
592
+ declaration.node.declarations = [
593
+ t4__namespace.variableDeclarator(
594
+ t4__namespace.identifier(componentConstName),
595
+ t4__namespace.callExpression(t4__namespace.identifier("recipe"), [
596
+ t4__namespace.stringLiteral(toKebabCase(variableName)),
597
+ componentConfig
598
+ ])
599
+ )
600
+ ];
601
+ styledTransforms.set(variableName, {
602
+ originalName: variableName,
603
+ mode: "component",
604
+ classConstName: componentConstName,
605
+ target: styledTarget,
606
+ propVars: [],
607
+ variantProps
608
+ });
609
+ return true;
610
+ }
611
+ function migrateInterpolatedTemplate(path, variableName, template, styledTarget, warnings, styledTransforms) {
612
+ const interpolations = parseTemplateInterpolations(template);
613
+ if (!interpolations) {
614
+ addWarning(
615
+ warnings,
616
+ "Skipped template literal with unsupported interpolations. Only prop-based patterns like `${props => props.color}` are migrated.",
617
+ variableName
618
+ );
619
+ return false;
620
+ }
621
+ const cssText = reconstructInterpolatedCss(template, interpolations);
622
+ const propVars = interpolations.map((interpolation) => ({
623
+ propName: interpolation.propName,
624
+ varConstName: path.scope.generateUidIdentifier(
625
+ `${variableName}${interpolation.propName.charAt(0).toUpperCase()}${interpolation.propName.slice(1)}Var`
626
+ ).name,
627
+ suffix: interpolation.suffix
628
+ }));
629
+ const varReplacements = /* @__PURE__ */ new Map();
630
+ for (let i = 0; i < interpolations.length; i++) {
631
+ varReplacements.set(
632
+ placeholderToken(interpolations[i].index),
633
+ t4__namespace.identifier(propVars[i].varConstName)
634
+ );
635
+ }
636
+ const objectExpression4 = cssToObjectExpression(cssText, warnings, varReplacements);
637
+ if (!objectExpression4) {
638
+ addWarning(warnings, "Skipped because CSS could not be parsed.", variableName);
639
+ return false;
640
+ }
641
+ const classConstName = path.scope.generateUidIdentifier(`${variableName}Class`).name;
642
+ const varDeclarators = propVars.map(
643
+ (propVar) => t4__namespace.variableDeclarator(
644
+ t4__namespace.identifier(propVar.varConstName),
645
+ t4__namespace.callExpression(t4__namespace.identifier("createVar"), [
646
+ t4__namespace.stringLiteral(toVarDebugName(variableName, propVar.propName))
647
+ ])
648
+ )
649
+ );
650
+ const declaration = path.parentPath;
651
+ if (!declaration.isVariableDeclaration()) return false;
652
+ declaration.node.declarations = [
653
+ ...varDeclarators,
654
+ t4__namespace.variableDeclarator(
655
+ t4__namespace.identifier(classConstName),
656
+ t4__namespace.callExpression(t4__namespace.identifier("style"), [
657
+ t4__namespace.stringLiteral(toKebabCase(variableName)),
658
+ objectExpression4
659
+ ])
660
+ )
661
+ ];
662
+ styledTransforms.set(variableName, {
663
+ originalName: variableName,
664
+ mode: "class",
665
+ classConstName,
666
+ target: styledTarget,
667
+ propVars,
668
+ variantProps: []
669
+ });
670
+ return true;
671
+ }
251
672
  function isOnlyJsxReferences(binding) {
252
673
  return binding.referencePaths.every((referencePath) => {
253
674
  const parent = referencePath.parentPath;
@@ -265,21 +686,24 @@ function migrateSource(filePath, source) {
265
686
  const styledNames = /* @__PURE__ */ new Set();
266
687
  const cssTagNames = /* @__PURE__ */ new Set();
267
688
  const styledTransforms = /* @__PURE__ */ new Map();
689
+ let needsVars = false;
690
+ let needsStyle = false;
691
+ let needsRecipe = false;
268
692
  traverse__default.default(ast, {
269
693
  ImportDeclaration(path) {
270
694
  if (path.node.source.value === "styled-components" || path.node.source.value === "@emotion/styled") {
271
695
  for (const specifier of path.node.specifiers) {
272
- if (t2__namespace.isImportDefaultSpecifier(specifier)) {
696
+ if (t4__namespace.isImportDefaultSpecifier(specifier)) {
273
697
  styledNames.add(specifier.local.name);
274
698
  }
275
- if (t2__namespace.isImportSpecifier(specifier) && t2__namespace.isIdentifier(specifier.imported, { name: "css" })) {
699
+ if (t4__namespace.isImportSpecifier(specifier) && t4__namespace.isIdentifier(specifier.imported, { name: "css" })) {
276
700
  cssTagNames.add(specifier.local.name);
277
701
  }
278
702
  }
279
703
  }
280
704
  if (path.node.source.value === "@emotion/react" || path.node.source.value === "@emotion/css") {
281
705
  for (const specifier of path.node.specifiers) {
282
- if (t2__namespace.isImportSpecifier(specifier) && t2__namespace.isIdentifier(specifier.imported, { name: "css" })) {
706
+ if (t4__namespace.isImportSpecifier(specifier) && t4__namespace.isIdentifier(specifier.imported, { name: "css" })) {
283
707
  cssTagNames.add(specifier.local.name);
284
708
  }
285
709
  }
@@ -288,27 +712,68 @@ function migrateSource(filePath, source) {
288
712
  });
289
713
  traverse__default.default(ast, {
290
714
  VariableDeclarator(path) {
291
- if (!t2__namespace.isIdentifier(path.node.id)) return;
292
- if (!t2__namespace.isTaggedTemplateExpression(path.node.init)) return;
715
+ if (!t4__namespace.isIdentifier(path.node.id)) return;
716
+ if (!t4__namespace.isTaggedTemplateExpression(path.node.init)) return;
293
717
  const variableName = path.node.id.name;
294
718
  const binding = path.scope.getBinding(variableName);
295
719
  if (!binding) return;
296
720
  const template = path.node.init.quasi;
297
- if (template.expressions.length > 0) {
298
- addWarning(
721
+ const hasInterpolations = template.expressions.length > 0;
722
+ const styledTarget = parseStyledTarget(path.node.init.tag, styledNames);
723
+ if (hasInterpolations) {
724
+ if (!styledTarget) {
725
+ addWarning(
726
+ warnings,
727
+ "Skipped template literal with interpolations. Only styled-components prop patterns are migrated.",
728
+ variableName
729
+ );
730
+ return;
731
+ }
732
+ const declaration = path.parentPath;
733
+ if (declaration.parentPath?.isExportNamedDeclaration() || declaration.parentPath?.isExportDefaultDeclaration()) {
734
+ addWarning(
735
+ warnings,
736
+ "Skipped exported styled component to avoid changing external API shape.",
737
+ variableName
738
+ );
739
+ return;
740
+ }
741
+ if (!isOnlyJsxReferences(binding)) {
742
+ addWarning(warnings, "Skipped styled component with non-JSX references.", variableName);
743
+ return;
744
+ }
745
+ if (migrateBooleanVariantTemplate(
746
+ path,
747
+ variableName,
748
+ template,
749
+ styledTarget,
299
750
  warnings,
300
- "Skipped template literal with interpolations. Only static templates are migrated.",
301
- variableName
302
- );
751
+ styledTransforms
752
+ )) {
753
+ needsRecipe = true;
754
+ changed = true;
755
+ return;
756
+ }
757
+ if (migrateInterpolatedTemplate(
758
+ path,
759
+ variableName,
760
+ template,
761
+ styledTarget,
762
+ warnings,
763
+ styledTransforms
764
+ )) {
765
+ needsVars = true;
766
+ needsStyle = true;
767
+ changed = true;
768
+ }
303
769
  return;
304
770
  }
305
771
  const cssText = template.quasis.map((quasi) => quasi.value.cooked ?? "").join("");
306
- const objectExpression2 = cssToObjectExpression(cssText, warnings);
307
- if (!objectExpression2) {
772
+ const objectExpression4 = cssToObjectExpression(cssText, warnings);
773
+ if (!objectExpression4) {
308
774
  addWarning(warnings, "Skipped because CSS could not be parsed.", variableName);
309
775
  return;
310
776
  }
311
- const styledTarget = parseStyledTarget(path.node.init.tag, styledNames);
312
777
  if (styledTarget) {
313
778
  const declaration = path.parentPath;
314
779
  if (declaration.parentPath?.isExportNamedDeclaration() || declaration.parentPath?.isExportDefaultDeclaration()) {
@@ -324,24 +789,29 @@ function migrateSource(filePath, source) {
324
789
  return;
325
790
  }
326
791
  const classConstName = path.scope.generateUidIdentifier(`${variableName}Class`).name;
327
- path.node.id = t2__namespace.identifier(classConstName);
328
- path.node.init = t2__namespace.callExpression(
329
- t2__namespace.memberExpression(t2__namespace.identifier("styles"), t2__namespace.identifier("class")),
330
- [t2__namespace.stringLiteral(toKebabCase(variableName)), objectExpression2]
331
- );
792
+ path.node.id = t4__namespace.identifier(classConstName);
793
+ path.node.init = t4__namespace.callExpression(t4__namespace.identifier("style"), [
794
+ t4__namespace.stringLiteral(toKebabCase(variableName)),
795
+ objectExpression4
796
+ ]);
332
797
  styledTransforms.set(variableName, {
333
798
  originalName: variableName,
799
+ mode: "class",
334
800
  classConstName,
335
- target: styledTarget
801
+ target: styledTarget,
802
+ propVars: [],
803
+ variantProps: []
336
804
  });
805
+ needsStyle = true;
337
806
  changed = true;
338
807
  return;
339
808
  }
340
- if (t2__namespace.isIdentifier(path.node.init.tag) && cssTagNames.has(path.node.init.tag.name)) {
341
- path.node.init = t2__namespace.callExpression(
342
- t2__namespace.memberExpression(t2__namespace.identifier("styles"), t2__namespace.identifier("class")),
343
- [t2__namespace.stringLiteral(toKebabCase(variableName)), objectExpression2]
344
- );
809
+ if (t4__namespace.isIdentifier(path.node.init.tag) && cssTagNames.has(path.node.init.tag.name)) {
810
+ path.node.init = t4__namespace.callExpression(t4__namespace.identifier("style"), [
811
+ t4__namespace.stringLiteral(toKebabCase(variableName)),
812
+ objectExpression4
813
+ ]);
814
+ needsStyle = true;
345
815
  changed = true;
346
816
  }
347
817
  }
@@ -350,19 +820,40 @@ function migrateSource(filePath, source) {
350
820
  traverse__default.default(ast, {
351
821
  JSXElement(path) {
352
822
  const openingName = path.node.openingElement.name;
353
- if (!t2__namespace.isJSXIdentifier(openingName)) return;
823
+ if (!t4__namespace.isJSXIdentifier(openingName)) return;
354
824
  const transform = styledTransforms.get(openingName.name);
355
825
  if (!transform) return;
356
826
  path.node.openingElement.name = transform.target.jsxName;
357
827
  if (path.node.closingElement) {
358
828
  path.node.closingElement.name = transform.target.jsxName;
359
829
  }
360
- updateClassNameAttribute(path.node.openingElement, t2__namespace.identifier(transform.classConstName));
830
+ const classNameExpression = transform.mode === "component" ? buildComponentClassNameExpression(
831
+ transform.classConstName,
832
+ transform.variantProps,
833
+ collectPropValuesFromJsx(path.node.openingElement, new Set(transform.variantProps))
834
+ ) : t4__namespace.identifier(transform.classConstName);
835
+ updateClassNameAttribute(path.node.openingElement, classNameExpression);
836
+ if (transform.propVars.length > 0) {
837
+ const propNames = new Set(transform.propVars.map((propVar) => propVar.propName));
838
+ const propValues = collectPropValuesFromJsx(path.node.openingElement, propNames);
839
+ if (propValues.size > 0) {
840
+ const assignVarsCall = buildAssignVarsCall(transform.propVars, propValues);
841
+ updateStyleAttribute(path.node.openingElement, assignVarsCall);
842
+ removeStyledProps(path.node.openingElement, propNames);
843
+ }
844
+ }
845
+ if (transform.variantProps.length > 0) {
846
+ removeStyledProps(path.node.openingElement, new Set(transform.variantProps));
847
+ }
361
848
  }
362
849
  });
363
850
  }
364
851
  if (changed) {
365
- ensureStylesImport(ast);
852
+ ensureTypestylesRuntime(ast, {
853
+ style: needsStyle,
854
+ recipe: needsRecipe,
855
+ vars: needsVars
856
+ });
366
857
  cleanupUnusedImports(ast);
367
858
  }
368
859
  const output = generate__default.default(