@plumeria/codemod 20.0.0 → 21.1.0
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/README.md
CHANGED
|
@@ -7,6 +7,7 @@ No install needed — run it with `npx`.
|
|
|
7
7
|
npx @plumeria/codemod migrate --from css-modules
|
|
8
8
|
npx @plumeria/codemod migrate --from plumeria
|
|
9
9
|
npx @plumeria/codemod rename-prop classStyle sx
|
|
10
|
+
npx @plumeria/codemod rename-api viewTransition viewTransitionClass
|
|
10
11
|
```
|
|
11
12
|
|
|
12
13
|
Commit before running, so the rewrite can be reverted with `git checkout`.
|
|
@@ -219,7 +220,7 @@ reported as `dynamic-style-access` and its definitions stay in Plumeria.
|
|
|
219
220
|
|
|
220
221
|
### Global styles
|
|
221
222
|
|
|
222
|
-
`css.createTheme` and `css.
|
|
223
|
+
`css.createTheme` and `css.viewTransitionClass` produce rules that belong in a global
|
|
223
224
|
stylesheet, not in a CSS Module. The codemod extracts them and appends them to
|
|
224
225
|
the stylesheet the project already loads — `app/global.css` or
|
|
225
226
|
`styles/global.css`, under `src` or not, spelled `global` or `globals` — and
|
|
@@ -227,7 +228,7 @@ writes `src/styles/global.css` (or `styles/global.css` when `src` does not
|
|
|
227
228
|
exist) when it finds none. Theme tokens become CSS custom properties under
|
|
228
229
|
`:where(:root)` and the theme selector; view transitions become
|
|
229
230
|
`::view-transition-*` pseudo-element rules. References in style objects are
|
|
230
|
-
replaced by their generated names. A `keyframes` or `
|
|
231
|
+
replaced by their generated names. A `keyframes` or `viewTransitionClass` nothing the
|
|
231
232
|
migration exports reaches is left where it is.
|
|
232
233
|
|
|
233
234
|
A CSS Module renames the value of `animation-name` the way it renames a class,
|
|
@@ -361,3 +362,40 @@ withPlumeria(nextConfig, { styleProp: 'sx' });
|
|
|
361
362
|
// eslint.config.js
|
|
362
363
|
settings: { plumeria: { styleProp: 'sx' } }
|
|
363
364
|
```
|
|
365
|
+
|
|
366
|
+
## `rename-api`
|
|
367
|
+
|
|
368
|
+
Renames a Plumeria API after it has changed its name in a release:
|
|
369
|
+
|
|
370
|
+
```sh
|
|
371
|
+
npx @plumeria/codemod rename-api <from> <to> [paths...]
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
| Option | Effect |
|
|
375
|
+
| --------------- | ---------------------------------------- |
|
|
376
|
+
| `-d, --dry-run` | report what would change without writing |
|
|
377
|
+
|
|
378
|
+
`<to>` has to be an API `@plumeria/core` exports, so a typo is reported instead
|
|
379
|
+
of written. Paths default to the current directory, with the same directories
|
|
380
|
+
skipped as `rename-prop`.
|
|
381
|
+
|
|
382
|
+
### What it rewrites
|
|
383
|
+
|
|
384
|
+
Calls through the namespace or default import of `@plumeria/core`:
|
|
385
|
+
|
|
386
|
+
```diff
|
|
387
|
+
import * as css from '@plumeria/core';
|
|
388
|
+
- const fade = css.viewTransition({ old: { opacity: 0 } });
|
|
389
|
+
+ const fade = css.viewTransitionClass({ old: { opacity: 0 } });
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
And named imports, together with every reference to them. An alias keeps its
|
|
393
|
+
local name, so only the import changes:
|
|
394
|
+
|
|
395
|
+
```diff
|
|
396
|
+
- import { viewTransition, viewTransition as vt } from '@plumeria/core';
|
|
397
|
+
- const fade = viewTransition({ old: { opacity: 0 } });
|
|
398
|
+
+ import { viewTransitionClass, viewTransitionClass as vt } from '@plumeria/core';
|
|
399
|
+
+ const fade = viewTransitionClass({ old: { opacity: 0 } });
|
|
400
|
+
const slide = vt({ new: { opacity: 1 } });
|
|
401
|
+
```
|
package/dist/cli.js
CHANGED
|
@@ -42,12 +42,25 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
42
42
|
const eslint_1 = require("eslint");
|
|
43
43
|
const tsParser = __importStar(require("@typescript-eslint/parser"));
|
|
44
44
|
const rename_prop_1 = require("./transforms/rename-prop");
|
|
45
|
+
const rename_api_1 = require("./transforms/rename-api");
|
|
45
46
|
const adopt_styles_1 = require("./transforms/adopt-styles");
|
|
46
47
|
const release_styles_1 = require("./transforms/release-styles");
|
|
47
48
|
const migrate_1 = require("./migrate");
|
|
48
49
|
const release_1 = require("./release");
|
|
49
50
|
const { version } = require('../package.json');
|
|
50
|
-
const TRANSFORMS = ['rename-prop', 'migrate'];
|
|
51
|
+
const TRANSFORMS = ['rename-prop', 'rename-api', 'migrate'];
|
|
52
|
+
const APIS = [
|
|
53
|
+
'create',
|
|
54
|
+
'createTheme',
|
|
55
|
+
'createStatic',
|
|
56
|
+
'keyframes',
|
|
57
|
+
'viewTransitionClass',
|
|
58
|
+
'positionTry',
|
|
59
|
+
'varType',
|
|
60
|
+
'marker',
|
|
61
|
+
'extended',
|
|
62
|
+
'use',
|
|
63
|
+
];
|
|
51
64
|
const SOURCES = ['css-modules', 'plumeria'];
|
|
52
65
|
const SOURCE_FILES = '**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}';
|
|
53
66
|
const IGNORES = [
|
|
@@ -63,6 +76,7 @@ const USAGE = `plumeria-codemod — codemods for migrating Plumeria APIs
|
|
|
63
76
|
|
|
64
77
|
Usage
|
|
65
78
|
npx @plumeria/codemod rename-prop <from> <to> [paths...]
|
|
79
|
+
npx @plumeria/codemod rename-api <from> <to> [paths...]
|
|
66
80
|
npx @plumeria/codemod migrate --from css-modules [paths...]
|
|
67
81
|
npx @plumeria/codemod migrate --from plumeria [paths...]
|
|
68
82
|
|
|
@@ -76,6 +90,7 @@ Options
|
|
|
76
90
|
Examples
|
|
77
91
|
npx @plumeria/codemod rename-prop classStyle sx
|
|
78
92
|
npx @plumeria/codemod rename-prop classStyle sx src app --dry-run
|
|
93
|
+
npx @plumeria/codemod rename-api viewTransition viewTransitionClass
|
|
79
94
|
npx @plumeria/codemod migrate --from css-modules
|
|
80
95
|
npx @plumeria/codemod migrate --from plumeria --dry-run
|
|
81
96
|
|
|
@@ -139,14 +154,27 @@ function parseArgs(argv) {
|
|
|
139
154
|
};
|
|
140
155
|
}
|
|
141
156
|
if (!from || !to) {
|
|
142
|
-
throw new UsageError(
|
|
143
|
-
}
|
|
144
|
-
if (!IDENTIFIER.test(to)) {
|
|
145
|
-
throw new UsageError(`"${to}" is not a valid identifier`);
|
|
157
|
+
throw new UsageError(`${transform} needs both <from> and <to>`);
|
|
146
158
|
}
|
|
147
159
|
if (from === to) {
|
|
148
160
|
throw new UsageError(`<from> and <to> are both "${from}"`);
|
|
149
161
|
}
|
|
162
|
+
if (transform === 'rename-api') {
|
|
163
|
+
if (!APIS.includes(to)) {
|
|
164
|
+
throw new UsageError(`"${to}" is not a Plumeria API. Available: ${APIS.join(', ')}`);
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
transform: 'rename-api',
|
|
168
|
+
from,
|
|
169
|
+
to,
|
|
170
|
+
targets: targets.length > 0 ? targets : ['.'],
|
|
171
|
+
dryRun,
|
|
172
|
+
includeTypes,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (!IDENTIFIER.test(to)) {
|
|
176
|
+
throw new UsageError(`"${to}" is not a valid identifier`);
|
|
177
|
+
}
|
|
150
178
|
if (RESERVED.has(to)) {
|
|
151
179
|
throw new UsageError(`"${to}" is already used by React`);
|
|
152
180
|
}
|
|
@@ -160,7 +188,18 @@ function parseArgs(argv) {
|
|
|
160
188
|
};
|
|
161
189
|
}
|
|
162
190
|
function buildConfig(options) {
|
|
163
|
-
const { from, to, includeTypes } = options;
|
|
191
|
+
const { transform, from, to, includeTypes } = options;
|
|
192
|
+
const rule = transform === 'rename-api'
|
|
193
|
+
? {
|
|
194
|
+
plugins: { codemod: { rules: { 'rename-api': rename_api_1.renameApi } } },
|
|
195
|
+
rules: { 'codemod/rename-api': ['error', { from, to }] },
|
|
196
|
+
}
|
|
197
|
+
: {
|
|
198
|
+
plugins: { codemod: { rules: { 'rename-prop': rename_prop_1.renameProp } } },
|
|
199
|
+
rules: {
|
|
200
|
+
'codemod/rename-prop': ['error', { from, to, includeTypes }],
|
|
201
|
+
},
|
|
202
|
+
};
|
|
164
203
|
return [
|
|
165
204
|
{ ignores: IGNORES },
|
|
166
205
|
{
|
|
@@ -172,8 +211,7 @@ function buildConfig(options) {
|
|
|
172
211
|
ecmaFeatures: { jsx: true },
|
|
173
212
|
},
|
|
174
213
|
},
|
|
175
|
-
|
|
176
|
-
rules: { 'codemod/rename-prop': ['error', { from, to, includeTypes }] },
|
|
214
|
+
...rule,
|
|
177
215
|
},
|
|
178
216
|
];
|
|
179
217
|
}
|
package/dist/release.js
CHANGED
|
@@ -42,7 +42,7 @@ const path = __importStar(require("node:path"));
|
|
|
42
42
|
const parser_1 = require("@typescript-eslint/parser");
|
|
43
43
|
const resolve_1 = require("./resolve");
|
|
44
44
|
const from_plumeria_1 = require("./transforms/from-plumeria");
|
|
45
|
-
const GLOBAL_OPEN = '/* Generated from css.createTheme, css.keyframes, and css.
|
|
45
|
+
const GLOBAL_OPEN = '/* Generated from css.createTheme, css.keyframes, and css.viewTransitionClass by @plumeria/codemod */';
|
|
46
46
|
const GLOBAL_CLOSE = '/* End of @plumeria/codemod generated styles */';
|
|
47
47
|
const EXTENSION = /\.(?:[cm]?[jt]sx?)$/;
|
|
48
48
|
const IGNORED = new Set([
|
|
@@ -389,11 +389,11 @@ function extractPlumeriaAnimations(source, externalValues = {}) {
|
|
|
389
389
|
}
|
|
390
390
|
for (const declaration of declarations) {
|
|
391
391
|
if (declaration.id.type !== 'Identifier' ||
|
|
392
|
-
!isCall(declaration.init, '
|
|
392
|
+
!isCall(declaration.init, 'viewTransitionClass'))
|
|
393
393
|
continue;
|
|
394
394
|
const object = evaluate(declaration.init.arguments[0], values);
|
|
395
395
|
if (!object || typeof object !== 'object') {
|
|
396
|
-
report(declaration.init, 'dynamic-view-transition', '`
|
|
396
|
+
report(declaration.init, 'dynamic-view-transition', '`viewTransitionClass` needs a statically resolvable object.');
|
|
397
397
|
continue;
|
|
398
398
|
}
|
|
399
399
|
const hash = (0, zss_engine_1.genBase36Hash)(object, 1, 8);
|
|
@@ -406,7 +406,7 @@ function extractPlumeriaAnimations(source, externalValues = {}) {
|
|
|
406
406
|
const style = object[part];
|
|
407
407
|
if (style && typeof style === 'object') {
|
|
408
408
|
const pseudoPart = part === 'imagePair' ? 'image-pair' : part;
|
|
409
|
-
parts.push(emitGlobalObject(`::view-transition-${pseudoPart}(
|
|
409
|
+
parts.push(emitGlobalObject(`::view-transition-${pseudoPart}(*.${name})`, style));
|
|
410
410
|
}
|
|
411
411
|
}
|
|
412
412
|
if (parts.length > 0)
|
|
@@ -96,7 +96,7 @@ exports.releaseStyles = {
|
|
|
96
96
|
static: '`createStatic` values were inlined into the generated CSS.',
|
|
97
97
|
theme: '`createTheme` was written to the global stylesheet.',
|
|
98
98
|
themeImport: 'Remove the createTheme import after inlining its CSS variables.',
|
|
99
|
-
animation: '`keyframes` or `
|
|
99
|
+
animation: '`keyframes` or `viewTransitionClass` was written globally.',
|
|
100
100
|
animationImport: 'Remove the animation import after replacing it with its generated name.',
|
|
101
101
|
staticImport: 'Remove the createStatic import after inlining its values into the CSS.',
|
|
102
102
|
alias: 'Read this style from the CSS Module the whole file was exported to.',
|
|
@@ -170,7 +170,7 @@ exports.releaseStyles = {
|
|
|
170
170
|
return Boolean(entry);
|
|
171
171
|
if (called[1] === 'createTheme')
|
|
172
172
|
return themeBindings.includes(only.id.name);
|
|
173
|
-
if (called[1] === 'keyframes' || called[1] === '
|
|
173
|
+
if (called[1] === 'keyframes' || called[1] === 'viewTransitionClass')
|
|
174
174
|
return animationBindings.includes(only.id.name);
|
|
175
175
|
if (called[1] === 'create')
|
|
176
176
|
return Boolean(entry &&
|
|
@@ -367,7 +367,7 @@ exports.releaseStyles = {
|
|
|
367
367
|
? Boolean(entry)
|
|
368
368
|
: method === 'createTheme'
|
|
369
369
|
? themeBindings.length > 0
|
|
370
|
-
: method === 'keyframes' || method === '
|
|
370
|
+
: method === 'keyframes' || method === 'viewTransitionClass'
|
|
371
371
|
? animationBindings.length > 0
|
|
372
372
|
: false;
|
|
373
373
|
const survivesCore = (program) => {
|
|
@@ -604,14 +604,14 @@ exports.releaseStyles = {
|
|
|
604
604
|
((entry && path[1] === 'createStatic') ||
|
|
605
605
|
(path[1] === 'createTheme' &&
|
|
606
606
|
themeBindings.includes(declaration.id.name)) ||
|
|
607
|
-
((path[1] === 'keyframes' || path[1] === '
|
|
607
|
+
((path[1] === 'keyframes' || path[1] === 'viewTransitionClass') &&
|
|
608
608
|
animationBindings.includes(declaration.id.name)))) {
|
|
609
609
|
const target = node.parent?.type === 'ExportNamedDeclaration' ? node.parent : node;
|
|
610
610
|
context.report({
|
|
611
611
|
node: target,
|
|
612
612
|
messageId: path[1] === 'createTheme'
|
|
613
613
|
? 'theme'
|
|
614
|
-
: path[1] === 'keyframes' || path[1] === '
|
|
614
|
+
: path[1] === 'keyframes' || path[1] === 'viewTransitionClass'
|
|
615
615
|
? 'animation'
|
|
616
616
|
: 'static',
|
|
617
617
|
fix: (fixer) => removeStatement(fixer, target),
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renameApi = void 0;
|
|
4
|
+
const CORE = '@plumeria/core';
|
|
5
|
+
exports.renameApi = {
|
|
6
|
+
meta: {
|
|
7
|
+
type: 'suggestion',
|
|
8
|
+
docs: {
|
|
9
|
+
description: 'Rename a Plumeria API to a new name',
|
|
10
|
+
},
|
|
11
|
+
fixable: 'code',
|
|
12
|
+
messages: {
|
|
13
|
+
rename: 'Rename "{{from}}" to "{{to}}".',
|
|
14
|
+
},
|
|
15
|
+
schema: [
|
|
16
|
+
{
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
from: { type: 'string' },
|
|
20
|
+
to: { type: 'string' },
|
|
21
|
+
},
|
|
22
|
+
required: ['from', 'to'],
|
|
23
|
+
additionalProperties: false,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
create(context) {
|
|
28
|
+
const { from, to } = context.options[0];
|
|
29
|
+
const namespaces = new Set();
|
|
30
|
+
const rename = (node) => context.report({
|
|
31
|
+
node,
|
|
32
|
+
messageId: 'rename',
|
|
33
|
+
data: { from, to },
|
|
34
|
+
fix: (fixer) => fixer.replaceText(node, to),
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
ImportDeclaration(node) {
|
|
38
|
+
if (node.source.value !== CORE)
|
|
39
|
+
return;
|
|
40
|
+
for (const specifier of node.specifiers) {
|
|
41
|
+
if (specifier.type !== 'ImportSpecifier') {
|
|
42
|
+
namespaces.add(specifier.local.name);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (specifier.imported.type !== 'Identifier')
|
|
46
|
+
continue;
|
|
47
|
+
if (specifier.imported.name !== from)
|
|
48
|
+
continue;
|
|
49
|
+
rename(specifier.imported);
|
|
50
|
+
if (specifier.local.name !== from)
|
|
51
|
+
continue;
|
|
52
|
+
const [variable] = context.sourceCode.getDeclaredVariables(specifier);
|
|
53
|
+
for (const reference of variable?.references ?? []) {
|
|
54
|
+
rename(reference.identifier);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
MemberExpression(node) {
|
|
59
|
+
if (node.computed)
|
|
60
|
+
return;
|
|
61
|
+
if (node.object.type !== 'Identifier')
|
|
62
|
+
return;
|
|
63
|
+
if (!namespaces.has(node.object.name))
|
|
64
|
+
return;
|
|
65
|
+
if (node.property.type !== 'Identifier')
|
|
66
|
+
return;
|
|
67
|
+
if (node.property.name !== from)
|
|
68
|
+
return;
|
|
69
|
+
rename(node.property);
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plumeria/codemod",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Codemods for Plumeria: migrate a CSS Modules codebase in, export it back out, and rename the styling prop",
|
|
3
|
+
"version": "21.1.0",
|
|
4
|
+
"description": "Codemods for Plumeria: migrate a CSS Modules codebase in, export it back out, and rename the styling prop or an API",
|
|
5
5
|
"author": "Refirst 11",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://plumeria.dev",
|