@plumeria/codemod 18.2.22 → 18.2.24
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.js +5 -2
- package/dist/migrate.js +20 -1
- package/dist/transforms/adopt-styles.js +35 -6
- package/dist/transforms/release-styles.js +62 -18
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -302,8 +302,11 @@ async function migrateFromCssModules(options) {
|
|
|
302
302
|
return 0;
|
|
303
303
|
}
|
|
304
304
|
for (const sheet of stylesheets) {
|
|
305
|
+
if (!modules[sheet.source])
|
|
306
|
+
continue;
|
|
305
307
|
console.log(` ${relative(sheet.source)} -> ${relative(sheet.target)}`);
|
|
306
308
|
}
|
|
309
|
+
const converting = Object.keys(modules).length;
|
|
307
310
|
const config = {
|
|
308
311
|
overrideConfigFile: true,
|
|
309
312
|
overrideConfig: migrateConfig(modules),
|
|
@@ -312,7 +315,7 @@ async function migrateFromCssModules(options) {
|
|
|
312
315
|
if (dryRun) {
|
|
313
316
|
const results = await new eslint_1.ESLint(config).lintFiles(targets);
|
|
314
317
|
const touched = results.filter((r) => r.messages.length > 0);
|
|
315
|
-
console.log(`\n${
|
|
318
|
+
console.log(`\n${converting} stylesheet(s) would be converted, ${touched.length} consumer(s) rewritten.`);
|
|
316
319
|
console.log('Run without --dry-run to apply.');
|
|
317
320
|
}
|
|
318
321
|
else {
|
|
@@ -321,7 +324,7 @@ async function migrateFromCssModules(options) {
|
|
|
321
324
|
const applied = await new eslint_1.ESLint({ ...config, fix: true }).lintFiles(targets);
|
|
322
325
|
await eslint_1.ESLint.outputFixes(applied);
|
|
323
326
|
const touched = applied.filter((r) => r.output !== undefined);
|
|
324
|
-
console.log(`\n✔ converted ${
|
|
327
|
+
console.log(`\n✔ converted ${converting} stylesheet(s) and rewrote ${touched.length} consumer(s).`);
|
|
325
328
|
}
|
|
326
329
|
const lines = (0, migrate_1.formatReports)(stylesheets, cwd);
|
|
327
330
|
if (lines.length === 0)
|
package/dist/migrate.js
CHANGED
|
@@ -82,6 +82,23 @@ function plan(targets) {
|
|
|
82
82
|
for (const source of findStylesheets(targets)) {
|
|
83
83
|
const converted = (0, css_modules_1.convertStylesheet)(fs.readFileSync(source, 'utf-8'));
|
|
84
84
|
const target = (0, exports.targetPath)(source);
|
|
85
|
+
if (fs.existsSync(target)) {
|
|
86
|
+
stylesheets.push({
|
|
87
|
+
source,
|
|
88
|
+
target,
|
|
89
|
+
reports: [
|
|
90
|
+
...converted.reports,
|
|
91
|
+
{
|
|
92
|
+
line: 0,
|
|
93
|
+
column: 0,
|
|
94
|
+
kind: 'target-exists',
|
|
95
|
+
source: '',
|
|
96
|
+
hint: `${path.basename(target)} already exists and was not overwritten.`,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
});
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
85
102
|
stylesheets.push({ source, target, reports: converted.reports });
|
|
86
103
|
modules[source] = {
|
|
87
104
|
source: `./${path.basename(target, '.ts')}`,
|
|
@@ -96,6 +113,8 @@ function plan(targets) {
|
|
|
96
113
|
function write(targets) {
|
|
97
114
|
const result = plan(targets);
|
|
98
115
|
for (const sheet of result.stylesheets) {
|
|
116
|
+
if (!result.modules[sheet.source])
|
|
117
|
+
continue;
|
|
99
118
|
const converted = (0, css_modules_1.convertStylesheet)(fs.readFileSync(sheet.source, 'utf-8'));
|
|
100
119
|
fs.writeFileSync(sheet.target, converted.code, 'utf-8');
|
|
101
120
|
}
|
|
@@ -108,7 +127,7 @@ function formatReports(sheets, cwd) {
|
|
|
108
127
|
continue;
|
|
109
128
|
lines.push(path.relative(cwd, sheet.source) || sheet.source);
|
|
110
129
|
for (const report of sheet.reports) {
|
|
111
|
-
lines.push(` ${report.line}:${report.column} ${report.kind} ${report.source}`);
|
|
130
|
+
lines.push(` ${report.line}:${report.column} ${report.kind}${report.source ? ` ${report.source}` : ''}`);
|
|
112
131
|
lines.push(` ${report.hint}`);
|
|
113
132
|
}
|
|
114
133
|
}
|
|
@@ -55,6 +55,7 @@ exports.adoptStyles = {
|
|
|
55
55
|
source: 'Import the generated styles from "{{source}}" instead of the stylesheet.',
|
|
56
56
|
core: `Add \`import '${CORE}';\` — the compiler collects a file only when it imports the package.`,
|
|
57
57
|
key: 'The generated key for "{{from}}" is "{{to}}".',
|
|
58
|
+
binding: 'The stylesheet import is now "{{local}}".',
|
|
58
59
|
prop: 'Styles are carried by the "{{prop}}" prop.',
|
|
59
60
|
composes: '"{{key}}" composed other classes; pass them as an array.',
|
|
60
61
|
value: 'A style read as a value goes back through `css.use`.',
|
|
@@ -138,9 +139,9 @@ exports.adoptStyles = {
|
|
|
138
139
|
: !read.computed && read.property.type === 'Identifier'
|
|
139
140
|
? read.property.name
|
|
140
141
|
: undefined;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
const access = written === undefined
|
|
143
|
+
? `${binding.local}${context.sourceCode.text.slice(read.object.range[1], read.range[1])}`
|
|
144
|
+
: `${binding.local}.${binding.entry.names[written] ?? written}`;
|
|
144
145
|
return element.type === 'LogicalExpression'
|
|
145
146
|
? `${context.sourceCode.getText(element.left)} ${element.operator} ${access}`
|
|
146
147
|
: access;
|
|
@@ -150,6 +151,26 @@ exports.adoptStyles = {
|
|
|
150
151
|
: node?.type === 'MemberExpression'
|
|
151
152
|
? node
|
|
152
153
|
: undefined;
|
|
154
|
+
const unguarded = (node) => {
|
|
155
|
+
if (node?.type !== 'ConditionalExpression')
|
|
156
|
+
return node;
|
|
157
|
+
const { test, consequent, alternate } = node;
|
|
158
|
+
if (test.type !== 'BinaryExpression' ||
|
|
159
|
+
test.operator !== '===' ||
|
|
160
|
+
test.left.type !== 'UnaryExpression' ||
|
|
161
|
+
test.left.operator !== 'typeof' ||
|
|
162
|
+
test.right.type !== 'Literal' ||
|
|
163
|
+
test.right.value !== 'number' ||
|
|
164
|
+
consequent.type !== 'TemplateLiteral' ||
|
|
165
|
+
consequent.expressions.length !== 1 ||
|
|
166
|
+
consequent.quasis.at(-1)?.value.raw !== 'px')
|
|
167
|
+
return node;
|
|
168
|
+
const written = context.sourceCode.getText(alternate);
|
|
169
|
+
return context.sourceCode.getText(test.left.argument) === written &&
|
|
170
|
+
context.sourceCode.getText(consequent.expressions[0]) === written
|
|
171
|
+
? alternate
|
|
172
|
+
: node;
|
|
173
|
+
};
|
|
153
174
|
const insideUse = (node) => {
|
|
154
175
|
let current = node;
|
|
155
176
|
while (current.parent &&
|
|
@@ -291,15 +312,23 @@ exports.adoptStyles = {
|
|
|
291
312
|
: undefined;
|
|
292
313
|
if (!binding)
|
|
293
314
|
return;
|
|
315
|
+
const local = binding.local;
|
|
294
316
|
const written = node.computed && node.property.type === 'Literal'
|
|
295
317
|
? String(node.property.value)
|
|
296
318
|
: !node.computed && node.property.type === 'Identifier'
|
|
297
319
|
? node.property.name
|
|
298
320
|
: null;
|
|
299
|
-
if (written === null)
|
|
321
|
+
if (written === null) {
|
|
322
|
+
if (local !== node.object.name)
|
|
323
|
+
context.report({
|
|
324
|
+
node: node.object,
|
|
325
|
+
messageId: 'binding',
|
|
326
|
+
data: { local },
|
|
327
|
+
fix: (fixer) => fixer.replaceText(node.object, local),
|
|
328
|
+
});
|
|
300
329
|
return;
|
|
330
|
+
}
|
|
301
331
|
const key = binding.entry.names[written] ?? written;
|
|
302
|
-
const local = binding.local;
|
|
303
332
|
if (coreLocal && !stylingPosition(node) && !insideUse(node)) {
|
|
304
333
|
context.report({
|
|
305
334
|
node,
|
|
@@ -359,7 +388,7 @@ exports.adoptStyles = {
|
|
|
359
388
|
for (const variable of variables)
|
|
360
389
|
consumed.add(named.get(variable));
|
|
361
390
|
return `${binding.local}.${key}(${variables
|
|
362
|
-
.map((variable) => context.sourceCode.getText(named.get(variable).value))
|
|
391
|
+
.map((variable) => context.sourceCode.getText(unguarded(named.get(variable).value)))
|
|
363
392
|
.join(', ')})`;
|
|
364
393
|
};
|
|
365
394
|
const expression = node.value.expression;
|
|
@@ -100,6 +100,7 @@ exports.releaseStyles = {
|
|
|
100
100
|
token: 'Inline the value this token resolved to.',
|
|
101
101
|
styleType: 'A released style is a class name.',
|
|
102
102
|
folded: 'This only named a style key, which the export has resolved.',
|
|
103
|
+
read: 'Read this style by the name the export gave it.',
|
|
103
104
|
functionStyle: 'Use the CSS Module class and pass function arguments as custom properties.',
|
|
104
105
|
unsupportedFunctionStyle: 'This function style call cannot be converted without changing its behavior.',
|
|
105
106
|
},
|
|
@@ -269,13 +270,16 @@ exports.releaseStyles = {
|
|
|
269
270
|
}
|
|
270
271
|
return undefined;
|
|
271
272
|
};
|
|
273
|
+
const moduleVariable = (local) => {
|
|
274
|
+
const scope = sourceCode.getScope(sourceCode.ast);
|
|
275
|
+
const moduleScope = scope.type === 'global' ? (scope.childScopes[0] ?? scope) : scope;
|
|
276
|
+
return moduleScope.variables.find((candidate) => candidate.name === local);
|
|
277
|
+
};
|
|
272
278
|
const onlyUsedByReleasedStyles = (local) => {
|
|
273
279
|
const range = releasedRange();
|
|
274
280
|
if (!range)
|
|
275
281
|
return false;
|
|
276
|
-
const
|
|
277
|
-
const moduleScope = scope.type === 'global' ? (scope.childScopes[0] ?? scope) : scope;
|
|
278
|
-
const variable = moduleScope.variables.find((candidate) => candidate.name === local);
|
|
282
|
+
const variable = moduleVariable(local);
|
|
279
283
|
if (!variable || variable.references.length === 0)
|
|
280
284
|
return false;
|
|
281
285
|
return variable.references.every((reference) => reference.identifier.range[0] >= range[0] &&
|
|
@@ -291,12 +295,20 @@ exports.releaseStyles = {
|
|
|
291
295
|
if (removed.length === 0)
|
|
292
296
|
return false;
|
|
293
297
|
const retained = node.specifiers.filter((specifier) => !removed.includes(specifier));
|
|
298
|
+
const named = node.specifiers.every((specifier) => specifier.type === 'ImportSpecifier');
|
|
294
299
|
context.report({
|
|
295
300
|
node,
|
|
296
301
|
messageId,
|
|
297
302
|
fix: (fixer) => retained.length === 0
|
|
298
303
|
? removeStatement(fixer, node)
|
|
299
|
-
:
|
|
304
|
+
: named
|
|
305
|
+
? fixer.replaceTextRange([
|
|
306
|
+
node.specifiers[0].range[0],
|
|
307
|
+
node.specifiers[node.specifiers.length - 1].range[1],
|
|
308
|
+
], retained
|
|
309
|
+
.map((specifier) => sourceCode.getText(specifier))
|
|
310
|
+
.join(', '))
|
|
311
|
+
: fixer.replaceText(node, importOf(retained, node.source)),
|
|
300
312
|
});
|
|
301
313
|
return true;
|
|
302
314
|
};
|
|
@@ -321,6 +333,19 @@ exports.releaseStyles = {
|
|
|
321
333
|
const key = alias ? alias[written] : written;
|
|
322
334
|
return key === undefined ? undefined : { module, key };
|
|
323
335
|
};
|
|
336
|
+
const onlyNamesStyleKeys = (variable) => {
|
|
337
|
+
if (!variable || variable.references.length === 0)
|
|
338
|
+
return false;
|
|
339
|
+
return variable.references.every((reference) => {
|
|
340
|
+
if (reference.init)
|
|
341
|
+
return true;
|
|
342
|
+
const parent = reference.identifier.parent;
|
|
343
|
+
return (parent?.type === 'MemberExpression' &&
|
|
344
|
+
parent.computed &&
|
|
345
|
+
parent.property === reference.identifier &&
|
|
346
|
+
Boolean(moduleOf(parent)));
|
|
347
|
+
});
|
|
348
|
+
};
|
|
324
349
|
const accessOf = (module, key) => `${module.binding}${IDENTIFIER.test(key) ? `.${key}` : `['${key}']`}`;
|
|
325
350
|
const classText = (node) => {
|
|
326
351
|
const resolved = styleKey(node);
|
|
@@ -526,6 +551,19 @@ exports.releaseStyles = {
|
|
|
526
551
|
if (removeImportedBindings(node, importedStatics, 'staticImport', (specifier) => onlyUsedByReleasedStyles(specifier.local.name)))
|
|
527
552
|
return;
|
|
528
553
|
}
|
|
554
|
+
const foldedKeys = node.specifiers
|
|
555
|
+
.filter((specifier) => specifier.type === 'ImportSpecifier' &&
|
|
556
|
+
constants[specifier.local.name] !== undefined)
|
|
557
|
+
.map((specifier) => specifier.imported.name ?? specifier.imported.value);
|
|
558
|
+
if (active && foldedKeys.length > 0) {
|
|
559
|
+
if (removeImportedBindings(node, foldedKeys, 'folded', (specifier) => {
|
|
560
|
+
const variable = moduleVariable(specifier.local.name);
|
|
561
|
+
return Boolean(variable &&
|
|
562
|
+
(variable.references.length === 0 ||
|
|
563
|
+
onlyNamesStyleKeys(variable)));
|
|
564
|
+
}))
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
529
567
|
const importedModule = importedReleasedModule(String(node.source.value));
|
|
530
568
|
if (importedModule) {
|
|
531
569
|
const importedBinding = node.specifiers.find((specifier) => specifier.type === 'ImportSpecifier' &&
|
|
@@ -634,19 +672,7 @@ exports.releaseStyles = {
|
|
|
634
672
|
node.parent.parent?.type === 'ExportNamedDeclaration')
|
|
635
673
|
return;
|
|
636
674
|
const scope = sourceCode.getScope(node);
|
|
637
|
-
|
|
638
|
-
if (!variable || variable.references.length === 0)
|
|
639
|
-
return;
|
|
640
|
-
const onlyNamesKeys = variable.references.every((reference) => {
|
|
641
|
-
if (reference.init)
|
|
642
|
-
return true;
|
|
643
|
-
const parent = reference.identifier.parent;
|
|
644
|
-
return (parent?.type === 'MemberExpression' &&
|
|
645
|
-
parent.computed &&
|
|
646
|
-
parent.property === reference.identifier &&
|
|
647
|
-
Boolean(moduleOf(parent)));
|
|
648
|
-
});
|
|
649
|
-
if (!onlyNamesKeys)
|
|
675
|
+
if (!onlyNamesStyleKeys(scope.variables.find((candidate) => candidate.name === node.id.name)))
|
|
650
676
|
return;
|
|
651
677
|
const target = node.parent;
|
|
652
678
|
context.report({
|
|
@@ -894,8 +920,26 @@ exports.releaseStyles = {
|
|
|
894
920
|
takeProp();
|
|
895
921
|
return;
|
|
896
922
|
}
|
|
897
|
-
|
|
923
|
+
const text = expression ? resolveText(expression) : undefined;
|
|
924
|
+
if (!complete && expression && text === undefined)
|
|
898
925
|
return;
|
|
926
|
+
const foldsKey = expression?.type === 'MemberExpression' &&
|
|
927
|
+
expression.computed &&
|
|
928
|
+
expression.property.type === 'Identifier' &&
|
|
929
|
+
constants[expression.property.name] !== undefined;
|
|
930
|
+
if (foldsKey &&
|
|
931
|
+
text !== undefined &&
|
|
932
|
+
text !== sourceCode.getText(expression)) {
|
|
933
|
+
if (carried) {
|
|
934
|
+
takeProp(text);
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
context.report({
|
|
938
|
+
node: expression,
|
|
939
|
+
messageId: 'read',
|
|
940
|
+
fix: (fixer) => fixer.replaceText(expression, text),
|
|
941
|
+
});
|
|
942
|
+
}
|
|
899
943
|
takeProp();
|
|
900
944
|
},
|
|
901
945
|
};
|