@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.viewTransition` produce rules that belong in a global
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 `viewTransition` nothing the
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('rename-prop needs both <from> and <to>');
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
- plugins: { codemod: { rules: { 'rename-prop': rename_prop_1.renameProp } } },
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.viewTransition by @plumeria/codemod */';
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, 'viewTransition'))
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', '`viewTransition` needs a statically resolvable object.');
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}(${name})`, style));
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 `viewTransition` was written globally.',
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] === 'viewTransition')
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 === 'viewTransition'
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] === 'viewTransition') &&
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] === 'viewTransition'
614
+ : path[1] === 'keyframes' || path[1] === 'viewTransitionClass'
615
615
  ? 'animation'
616
616
  : 'static',
617
617
  fix: (fixer) => removeStatement(fixer, target),
@@ -0,0 +1,6 @@
1
+ import type { Rule } from 'eslint';
2
+ export interface RenameApiOptions {
3
+ from: string;
4
+ to: string;
5
+ }
6
+ export declare const renameApi: Rule.RuleModule;
@@ -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": "20.0.0",
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",