@plumeria/codemod 18.2.18 → 18.2.19
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 +6 -3
- package/dist/release.d.ts +1 -0
- package/dist/release.js +46 -21
- package/dist/resolve.d.ts +2 -0
- package/dist/resolve.js +104 -0
- package/dist/transforms/from-plumeria.d.ts +8 -0
- package/dist/transforms/from-plumeria.js +166 -47
- package/dist/transforms/release-styles.d.ts +4 -0
- package/dist/transforms/release-styles.js +144 -58
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -331,7 +331,7 @@ async function migrateFromCssModules(options) {
|
|
|
331
331
|
console.log(line);
|
|
332
332
|
return 1;
|
|
333
333
|
}
|
|
334
|
-
function releaseConfig(modules, themes, animations) {
|
|
334
|
+
function releaseConfig(modules, themes, animations, statics) {
|
|
335
335
|
return [
|
|
336
336
|
{ ignores: IGNORES },
|
|
337
337
|
{
|
|
@@ -345,7 +345,10 @@ function releaseConfig(modules, themes, animations) {
|
|
|
345
345
|
},
|
|
346
346
|
plugins: { codemod: { rules: { 'release-styles': release_styles_1.releaseStyles } } },
|
|
347
347
|
rules: {
|
|
348
|
-
'codemod/release-styles': [
|
|
348
|
+
'codemod/release-styles': [
|
|
349
|
+
'error',
|
|
350
|
+
{ modules, themes, animations, statics },
|
|
351
|
+
],
|
|
349
352
|
},
|
|
350
353
|
},
|
|
351
354
|
];
|
|
@@ -369,7 +372,7 @@ async function migrateFromPlumeria(options) {
|
|
|
369
372
|
}
|
|
370
373
|
const config = {
|
|
371
374
|
overrideConfigFile: true,
|
|
372
|
-
overrideConfig: releaseConfig(planned.modules, planned.themes, planned.animations),
|
|
375
|
+
overrideConfig: releaseConfig(planned.modules, planned.themes, planned.animations, planned.statics),
|
|
373
376
|
errorOnUnmatchedPattern: false,
|
|
374
377
|
};
|
|
375
378
|
const preview = await new eslint_1.ESLint(config).lintFiles(targets);
|
package/dist/release.d.ts
CHANGED
package/dist/release.js
CHANGED
|
@@ -40,6 +40,7 @@ exports.formatReleaseReports = formatReleaseReports;
|
|
|
40
40
|
const fs = __importStar(require("node:fs"));
|
|
41
41
|
const path = __importStar(require("node:path"));
|
|
42
42
|
const parser_1 = require("@typescript-eslint/parser");
|
|
43
|
+
const resolve_1 = require("./resolve");
|
|
43
44
|
const from_plumeria_1 = require("./transforms/from-plumeria");
|
|
44
45
|
const EXTENSION = /\.(?:[cm]?[jt]sx?)$/;
|
|
45
46
|
const IGNORED = new Set([
|
|
@@ -74,7 +75,7 @@ const findSources = (targets) => {
|
|
|
74
75
|
}
|
|
75
76
|
return found.sort();
|
|
76
77
|
};
|
|
77
|
-
const releasedPath = (source) => source.replace(
|
|
78
|
+
const releasedPath = (source) => source.replace(/(?:\.styles)?\.[^.]+$/, '.module.css');
|
|
78
79
|
exports.releasedPath = releasedPath;
|
|
79
80
|
const globalPath = (targets) => {
|
|
80
81
|
const first = path.resolve(targets[0]);
|
|
@@ -93,11 +94,22 @@ function planRelease(targets) {
|
|
|
93
94
|
const modules = {};
|
|
94
95
|
const themes = {};
|
|
95
96
|
const animations = {};
|
|
97
|
+
const statics = {};
|
|
96
98
|
const globalRules = [];
|
|
97
99
|
const sources = findSources(targets);
|
|
98
100
|
const sourceText = new Map(sources.map((source) => [source, fs.readFileSync(source, 'utf8')]));
|
|
99
101
|
const themeValues = new Map();
|
|
100
102
|
const animationValues = new Map();
|
|
103
|
+
const staticValues = new Map();
|
|
104
|
+
for (const source of sources) {
|
|
105
|
+
try {
|
|
106
|
+
const extracted = (0, from_plumeria_1.extractPlumeriaStatics)(sourceText.get(source));
|
|
107
|
+
if (Object.keys(extracted.bindings).length > 0)
|
|
108
|
+
staticValues.set(source, extracted.bindings);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
}
|
|
112
|
+
}
|
|
101
113
|
for (const source of sources) {
|
|
102
114
|
try {
|
|
103
115
|
const extracted = (0, from_plumeria_1.extractPlumeriaThemes)(sourceText.get(source));
|
|
@@ -121,11 +133,6 @@ function planRelease(targets) {
|
|
|
121
133
|
catch {
|
|
122
134
|
}
|
|
123
135
|
}
|
|
124
|
-
const sourceForImport = (owner, specifier, candidates) => {
|
|
125
|
-
const resolved = path.resolve(path.dirname(owner), specifier);
|
|
126
|
-
const stem = resolved.replace(/\.[^.]+$/, '');
|
|
127
|
-
return [...candidates].find((candidate) => candidate.replace(/\.[^.]+$/, '') === stem);
|
|
128
|
-
};
|
|
129
136
|
const importedValues = (source, availableBySource) => {
|
|
130
137
|
const result = {};
|
|
131
138
|
const ast = (0, parser_1.parse)(sourceText.get(source), {
|
|
@@ -134,13 +141,14 @@ function planRelease(targets) {
|
|
|
134
141
|
});
|
|
135
142
|
for (const node of ast.body) {
|
|
136
143
|
if (node.type !== 'ImportDeclaration' ||
|
|
137
|
-
typeof node.source.value !== 'string'
|
|
138
|
-
!node.source.value.startsWith('.'))
|
|
144
|
+
typeof node.source.value !== 'string')
|
|
139
145
|
continue;
|
|
140
|
-
const target =
|
|
146
|
+
const target = (0, resolve_1.resolveSourcePath)(node.source.value, source);
|
|
141
147
|
if (!target)
|
|
142
148
|
continue;
|
|
143
149
|
const available = availableBySource.get(target);
|
|
150
|
+
if (!available)
|
|
151
|
+
continue;
|
|
144
152
|
for (const specifier of node.specifiers) {
|
|
145
153
|
if (specifier.type === 'ImportSpecifier') {
|
|
146
154
|
const imported = specifier.imported.name ?? specifier.imported.value;
|
|
@@ -154,22 +162,33 @@ function planRelease(targets) {
|
|
|
154
162
|
}
|
|
155
163
|
return result;
|
|
156
164
|
};
|
|
157
|
-
const importedThemes = (source) => importedValues(source, themeValues);
|
|
158
|
-
const importedAnimations = (source) => importedValues(source, animationValues);
|
|
159
165
|
const importedPlumeriaValues = (source) => {
|
|
160
|
-
const merged = {
|
|
161
|
-
for (const [
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
166
|
+
const merged = {};
|
|
167
|
+
for (const table of [staticValues, themeValues, animationValues]) {
|
|
168
|
+
for (const [key, value] of Object.entries(importedValues(source, table))) {
|
|
169
|
+
const previous = merged[key];
|
|
170
|
+
merged[key] =
|
|
171
|
+
previous &&
|
|
172
|
+
typeof previous === 'object' &&
|
|
173
|
+
value &&
|
|
174
|
+
typeof value === 'object'
|
|
175
|
+
? { ...previous, ...value }
|
|
176
|
+
: value;
|
|
177
|
+
}
|
|
170
178
|
}
|
|
171
179
|
return merged;
|
|
172
180
|
};
|
|
181
|
+
for (const source of sources) {
|
|
182
|
+
try {
|
|
183
|
+
const extracted = (0, from_plumeria_1.extractPlumeriaStatics)(sourceText.get(source), importedPlumeriaValues(source));
|
|
184
|
+
if (Object.keys(extracted.bindings).length === 0)
|
|
185
|
+
continue;
|
|
186
|
+
staticValues.set(source, extracted.bindings);
|
|
187
|
+
statics[source] = Object.keys(extracted.bindings);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
}
|
|
191
|
+
}
|
|
173
192
|
for (const source of sources) {
|
|
174
193
|
try {
|
|
175
194
|
const extracted = (0, from_plumeria_1.extractPlumeriaAnimations)(sourceText.get(source), {
|
|
@@ -218,7 +237,12 @@ function planRelease(targets) {
|
|
|
218
237
|
if (reports.length === 0) {
|
|
219
238
|
modules[source] = {
|
|
220
239
|
source: specifier,
|
|
240
|
+
target,
|
|
221
241
|
binding: converted.binding,
|
|
242
|
+
...(converted.definitionOnly ? { definitionOnly: true } : {}),
|
|
243
|
+
...(Object.keys(converted.aliases).length > 0
|
|
244
|
+
? { aliases: converted.aliases }
|
|
245
|
+
: {}),
|
|
222
246
|
...(Object.keys(converted.functions).length > 0
|
|
223
247
|
? { functions: converted.functions }
|
|
224
248
|
: {}),
|
|
@@ -230,6 +254,7 @@ function planRelease(targets) {
|
|
|
230
254
|
modules,
|
|
231
255
|
themes,
|
|
232
256
|
animations,
|
|
257
|
+
statics,
|
|
233
258
|
global: globalRules.length > 0
|
|
234
259
|
? {
|
|
235
260
|
target: globalPath(targets),
|
package/dist/resolve.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.resetTsconfigCache = void 0;
|
|
37
|
+
exports.resolveSourcePath = resolveSourcePath;
|
|
38
|
+
const fs = __importStar(require("node:fs"));
|
|
39
|
+
const path = __importStar(require("node:path"));
|
|
40
|
+
const get_tsconfig_1 = require("get-tsconfig");
|
|
41
|
+
const EXTENSIONS = [
|
|
42
|
+
'.ts',
|
|
43
|
+
'.tsx',
|
|
44
|
+
'.mts',
|
|
45
|
+
'.cts',
|
|
46
|
+
'.js',
|
|
47
|
+
'.jsx',
|
|
48
|
+
'.mjs',
|
|
49
|
+
'.cjs',
|
|
50
|
+
];
|
|
51
|
+
const CANDIDATES = [
|
|
52
|
+
...EXTENSIONS,
|
|
53
|
+
...EXTENSIONS.map((extension) => `${path.sep}index${extension}`),
|
|
54
|
+
];
|
|
55
|
+
const REWRITTEN = {
|
|
56
|
+
'.js': ['.ts', '.tsx'],
|
|
57
|
+
'.jsx': ['.tsx'],
|
|
58
|
+
'.mjs': ['.mts'],
|
|
59
|
+
'.cjs': ['.cts'],
|
|
60
|
+
};
|
|
61
|
+
let cachedMatcher;
|
|
62
|
+
const pathsMatcher = () => {
|
|
63
|
+
if (cachedMatcher === undefined) {
|
|
64
|
+
const tsconfig = (0, get_tsconfig_1.getTsconfig)(process.cwd());
|
|
65
|
+
cachedMatcher = tsconfig ? (0, get_tsconfig_1.createPathsMatcher)(tsconfig) : null;
|
|
66
|
+
}
|
|
67
|
+
return cachedMatcher;
|
|
68
|
+
};
|
|
69
|
+
const resetTsconfigCache = () => {
|
|
70
|
+
cachedMatcher = undefined;
|
|
71
|
+
};
|
|
72
|
+
exports.resetTsconfigCache = resetTsconfigCache;
|
|
73
|
+
const isFile = (candidate) => fs.statSync(candidate, { throwIfNoEntry: false })?.isFile() ?? false;
|
|
74
|
+
const resolveFile = (base) => {
|
|
75
|
+
if (isFile(base))
|
|
76
|
+
return base;
|
|
77
|
+
const extension = path.extname(base);
|
|
78
|
+
for (const rewritten of REWRITTEN[extension] ?? []) {
|
|
79
|
+
const candidate = `${base.slice(0, -extension.length)}${rewritten}`;
|
|
80
|
+
if (isFile(candidate))
|
|
81
|
+
return candidate;
|
|
82
|
+
}
|
|
83
|
+
for (const candidate of CANDIDATES) {
|
|
84
|
+
if (isFile(base + candidate))
|
|
85
|
+
return base + candidate;
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
};
|
|
89
|
+
function resolveSourcePath(specifier, importer) {
|
|
90
|
+
if (specifier === '@plumeria/core')
|
|
91
|
+
return null;
|
|
92
|
+
if (specifier.startsWith('.')) {
|
|
93
|
+
return resolveFile(path.resolve(path.dirname(importer), specifier));
|
|
94
|
+
}
|
|
95
|
+
const matcher = pathsMatcher();
|
|
96
|
+
if (!matcher)
|
|
97
|
+
return null;
|
|
98
|
+
for (const candidate of matcher(specifier)) {
|
|
99
|
+
const resolved = resolveFile(candidate);
|
|
100
|
+
if (resolved)
|
|
101
|
+
return resolved;
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
@@ -13,6 +13,8 @@ export interface PlumeriaModule {
|
|
|
13
13
|
params: string[];
|
|
14
14
|
variables: string[];
|
|
15
15
|
}>;
|
|
16
|
+
aliases: Record<string, Record<string, string>>;
|
|
17
|
+
definitionOnly: boolean;
|
|
16
18
|
reports: PlumeriaReport[];
|
|
17
19
|
}
|
|
18
20
|
export interface ThemeExtraction {
|
|
@@ -20,6 +22,10 @@ export interface ThemeExtraction {
|
|
|
20
22
|
globalCss: string;
|
|
21
23
|
reports: PlumeriaReport[];
|
|
22
24
|
}
|
|
25
|
+
export interface StaticExtraction {
|
|
26
|
+
bindings: Record<string, Record<string, unknown>>;
|
|
27
|
+
reports: PlumeriaReport[];
|
|
28
|
+
}
|
|
23
29
|
export interface AnimationExtraction {
|
|
24
30
|
bindings: Record<string, string>;
|
|
25
31
|
keyframes: string[];
|
|
@@ -31,7 +37,9 @@ export declare const __private: {
|
|
|
31
37
|
propertyKey: (node: any) => string | undefined;
|
|
32
38
|
memberPath: (node: any) => string[] | undefined;
|
|
33
39
|
evaluate: (node: any, values: Map<string, unknown>) => unknown;
|
|
40
|
+
bindsElsewhere: (ast: any, name: string, declared: any) => boolean;
|
|
34
41
|
};
|
|
35
42
|
export declare function extractPlumeriaThemes(source: string): ThemeExtraction;
|
|
43
|
+
export declare function extractPlumeriaStatics(source: string, externalValues?: Record<string, unknown>): StaticExtraction;
|
|
36
44
|
export declare function extractPlumeriaAnimations(source: string, externalValues?: Record<string, unknown>): AnimationExtraction;
|
|
37
45
|
export declare function convertPlumeriaModule(source: string, externalValues?: Record<string, unknown>): PlumeriaModule | null;
|
|
@@ -2,10 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.__private = void 0;
|
|
4
4
|
exports.extractPlumeriaThemes = extractPlumeriaThemes;
|
|
5
|
+
exports.extractPlumeriaStatics = extractPlumeriaStatics;
|
|
5
6
|
exports.extractPlumeriaAnimations = extractPlumeriaAnimations;
|
|
6
7
|
exports.convertPlumeriaModule = convertPlumeriaModule;
|
|
7
8
|
const parser_1 = require("@typescript-eslint/parser");
|
|
8
9
|
const zss_engine_1 = require("zss-engine");
|
|
10
|
+
const toIdent = (value) => value.replace(/[^A-Za-z0-9-]/g, '');
|
|
11
|
+
const markerVariable = (id, pseudo) => {
|
|
12
|
+
const hash = (0, zss_engine_1.genBase36Hash)({ [id]: pseudo }, 1, 8);
|
|
13
|
+
return `--${hash}-${toIdent(id)}-${toIdent(pseudo.split('(')[0])}`;
|
|
14
|
+
};
|
|
9
15
|
const propertyKey = (node) => {
|
|
10
16
|
if (!node)
|
|
11
17
|
return undefined;
|
|
@@ -35,9 +41,27 @@ const lookup = (path, values) => {
|
|
|
35
41
|
}
|
|
36
42
|
return value;
|
|
37
43
|
};
|
|
44
|
+
const markerArguments = (node, method, values) => {
|
|
45
|
+
if (node?.type !== 'CallExpression')
|
|
46
|
+
return undefined;
|
|
47
|
+
const path = memberPath(node.callee);
|
|
48
|
+
if (path?.length !== 2 || path[1] !== method)
|
|
49
|
+
return undefined;
|
|
50
|
+
const id = evaluate(node.arguments[0], values);
|
|
51
|
+
const pseudo = evaluate(node.arguments[1], values);
|
|
52
|
+
return typeof id === 'string' && typeof pseudo === 'string'
|
|
53
|
+
? { id, pseudo }
|
|
54
|
+
: undefined;
|
|
55
|
+
};
|
|
38
56
|
const evaluate = (node, values) => {
|
|
39
57
|
if (!node)
|
|
40
58
|
return undefined;
|
|
59
|
+
if (node.type === 'CallExpression') {
|
|
60
|
+
const extended = markerArguments(node, 'extended', values);
|
|
61
|
+
return extended
|
|
62
|
+
? `@container style(${markerVariable(extended.id, extended.pseudo)}: 1)`
|
|
63
|
+
: undefined;
|
|
64
|
+
}
|
|
41
65
|
if (node.type === 'Literal')
|
|
42
66
|
return node.value;
|
|
43
67
|
if (node.type === 'Identifier')
|
|
@@ -78,7 +102,29 @@ const evaluate = (node, values) => {
|
|
|
78
102
|
}
|
|
79
103
|
return undefined;
|
|
80
104
|
};
|
|
81
|
-
|
|
105
|
+
const bindsElsewhere = (ast, name, declared) => {
|
|
106
|
+
const skipped = new Set(['loc', 'range', 'parent']);
|
|
107
|
+
const visit = (node, parent) => {
|
|
108
|
+
if (!node || typeof node !== 'object' || node === declared)
|
|
109
|
+
return false;
|
|
110
|
+
if (Array.isArray(node))
|
|
111
|
+
return node.some((child) => visit(child, parent));
|
|
112
|
+
if (typeof node.type !== 'string')
|
|
113
|
+
return false;
|
|
114
|
+
if (node.type === 'Identifier' && node.name === name) {
|
|
115
|
+
if (parent?.type === 'ExportSpecifier')
|
|
116
|
+
return false;
|
|
117
|
+
if (parent?.type === 'MemberExpression' && parent.property === node)
|
|
118
|
+
return !!parent.computed;
|
|
119
|
+
if (parent?.type === 'Property' && parent.key === node)
|
|
120
|
+
return !!parent.computed;
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
return Object.keys(node).some((key) => !skipped.has(key) && visit(node[key], node));
|
|
124
|
+
};
|
|
125
|
+
return visit(ast, undefined);
|
|
126
|
+
};
|
|
127
|
+
exports.__private = { propertyKey, memberPath, evaluate, bindsElsewhere };
|
|
82
128
|
const isCall = (node, method) => {
|
|
83
129
|
if (node?.type !== 'CallExpression')
|
|
84
130
|
return false;
|
|
@@ -192,6 +238,41 @@ const emitGlobalObject = (selector, object) => {
|
|
|
192
238
|
const content = [...declarations, ...children].join('\n');
|
|
193
239
|
return `${selector} {\n${content}\n}`;
|
|
194
240
|
};
|
|
241
|
+
function extractPlumeriaStatics(source, externalValues = {}) {
|
|
242
|
+
const ast = (0, parser_1.parse)(source, {
|
|
243
|
+
sourceType: 'module',
|
|
244
|
+
ecmaFeatures: { jsx: true },
|
|
245
|
+
loc: true,
|
|
246
|
+
range: true,
|
|
247
|
+
});
|
|
248
|
+
const values = new Map(Object.entries(externalValues));
|
|
249
|
+
const bindings = {};
|
|
250
|
+
const reports = [];
|
|
251
|
+
for (const raw of ast.body) {
|
|
252
|
+
const statement = declarationOf(raw);
|
|
253
|
+
if (statement?.type !== 'VariableDeclaration')
|
|
254
|
+
continue;
|
|
255
|
+
for (const declaration of statement.declarations) {
|
|
256
|
+
if (declaration.id.type !== 'Identifier' ||
|
|
257
|
+
!isCall(declaration.init, 'createStatic'))
|
|
258
|
+
continue;
|
|
259
|
+
const value = evaluate(declaration.init.arguments[0], values);
|
|
260
|
+
if (value && typeof value === 'object') {
|
|
261
|
+
values.set(declaration.id.name, value);
|
|
262
|
+
bindings[declaration.id.name] = value;
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
reports.push({
|
|
266
|
+
line: declaration.init.loc.start.line,
|
|
267
|
+
column: declaration.init.loc.start.column + 1,
|
|
268
|
+
kind: 'dynamic-create-static',
|
|
269
|
+
hint: '`createStatic` must contain values that can be written into CSS.',
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return { bindings, reports };
|
|
275
|
+
}
|
|
195
276
|
function extractPlumeriaAnimations(source, externalValues = {}) {
|
|
196
277
|
const ast = (0, parser_1.parse)(source, {
|
|
197
278
|
sourceType: 'module',
|
|
@@ -311,7 +392,7 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
311
392
|
if (isCall(declaration.init, 'create')) {
|
|
312
393
|
const object = declaration.init.arguments[0];
|
|
313
394
|
if (object?.type === 'ObjectExpression') {
|
|
314
|
-
creates.push({ binding: declaration.id.name, object });
|
|
395
|
+
creates.push({ binding: declaration.id.name, object, declaration });
|
|
315
396
|
}
|
|
316
397
|
else {
|
|
317
398
|
report(declaration.init, 'dynamic-create', '`css.create` must receive an object literal.');
|
|
@@ -321,13 +402,22 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
321
402
|
}
|
|
322
403
|
if (creates.length === 0)
|
|
323
404
|
return null;
|
|
324
|
-
|
|
325
|
-
report(creates[1].object, 'multiple-create', 'Move each `css.create` call to its own module before exporting to CSS Modules.');
|
|
326
|
-
}
|
|
327
|
-
const { binding, object } = creates[0];
|
|
405
|
+
const { binding } = creates[0];
|
|
328
406
|
const rules = [];
|
|
329
407
|
const keys = [];
|
|
408
|
+
const aliases = {};
|
|
409
|
+
const taken = new Set();
|
|
330
410
|
const functions = {};
|
|
411
|
+
const classNameFor = (owner, key) => {
|
|
412
|
+
if (!taken.has(key))
|
|
413
|
+
return key;
|
|
414
|
+
const namespaced = `${owner}-${key}`;
|
|
415
|
+
let candidate = namespaced;
|
|
416
|
+
let suffix = 2;
|
|
417
|
+
while (taken.has(candidate))
|
|
418
|
+
candidate = `${namespaced}-${suffix++}`;
|
|
419
|
+
return candidate;
|
|
420
|
+
};
|
|
331
421
|
const emit = (style, selector, wrappers = [], scope = values) => {
|
|
332
422
|
if (style?.type !== 'ObjectExpression') {
|
|
333
423
|
report(style, 'dynamic-style', 'Only static style objects are exported.');
|
|
@@ -335,7 +425,18 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
335
425
|
}
|
|
336
426
|
const declarations = [];
|
|
337
427
|
const nested = [];
|
|
428
|
+
const markers = [];
|
|
338
429
|
for (const property of style.properties) {
|
|
430
|
+
if (property.type === 'SpreadElement') {
|
|
431
|
+
const marker = markerArguments(property.argument, 'marker', scope);
|
|
432
|
+
if (marker) {
|
|
433
|
+
markers.push({
|
|
434
|
+
pseudo: marker.pseudo,
|
|
435
|
+
variable: markerVariable(marker.id, marker.pseudo),
|
|
436
|
+
});
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
339
440
|
if (property.type !== 'Property' || property.kind !== 'init') {
|
|
340
441
|
report(property, 'spread-style', 'Spread styles must be expanded before exporting to CSS Modules.');
|
|
341
442
|
continue;
|
|
@@ -361,8 +462,10 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
361
462
|
const cssProperty = (0, zss_engine_1.camelToKebabCase)(rawKey);
|
|
362
463
|
declarations.push(` ${cssProperty}: ${(0, zss_engine_1.applyCssValue)(value, cssProperty)};`);
|
|
363
464
|
}
|
|
364
|
-
|
|
365
|
-
|
|
465
|
+
const pushRule = (target, lines) => {
|
|
466
|
+
if (lines.length === 0)
|
|
467
|
+
return;
|
|
468
|
+
let rule = `${target} {\n${lines.join('\n')}\n}`;
|
|
366
469
|
for (const wrapper of [...wrappers].reverse()) {
|
|
367
470
|
rule = `${wrapper} {\n${rule
|
|
368
471
|
.split('\n')
|
|
@@ -370,6 +473,10 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
370
473
|
.join('\n')}\n}`;
|
|
371
474
|
}
|
|
372
475
|
rules.push(rule);
|
|
476
|
+
};
|
|
477
|
+
pushRule(selector, declarations);
|
|
478
|
+
for (const marker of markers) {
|
|
479
|
+
pushRule(`${selector}${marker.pseudo}`, [` ${marker.variable}: 1;`]);
|
|
373
480
|
}
|
|
374
481
|
for (const child of nested) {
|
|
375
482
|
if (child.key.startsWith('@')) {
|
|
@@ -380,50 +487,60 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
380
487
|
}
|
|
381
488
|
}
|
|
382
489
|
};
|
|
383
|
-
for (const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
: propertyKey(property.key);
|
|
391
|
-
if (typeof key !== 'string') {
|
|
392
|
-
report(property.key, 'dynamic-style-key', 'Style names must be statically known.');
|
|
393
|
-
continue;
|
|
394
|
-
}
|
|
395
|
-
if (property.value.type === 'ArrowFunctionExpression' ||
|
|
396
|
-
property.value.type === 'FunctionExpression') {
|
|
397
|
-
const params = [];
|
|
398
|
-
let valid = true;
|
|
399
|
-
for (const parameter of property.value.params) {
|
|
400
|
-
if (parameter.type !== 'Identifier') {
|
|
401
|
-
valid = false;
|
|
402
|
-
break;
|
|
403
|
-
}
|
|
404
|
-
params.push(parameter.name);
|
|
490
|
+
for (const create of creates) {
|
|
491
|
+
const owner = create.binding;
|
|
492
|
+
const alias = {};
|
|
493
|
+
for (const property of create.object.properties) {
|
|
494
|
+
if (property.type !== 'Property' || property.kind !== 'init') {
|
|
495
|
+
report(property, 'spread-create', 'Top-level spreads cannot name a CSS Module class.');
|
|
496
|
+
continue;
|
|
405
497
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
498
|
+
const key = property.computed
|
|
499
|
+
? evaluate(property.key, values)
|
|
500
|
+
: propertyKey(property.key);
|
|
501
|
+
if (typeof key !== 'string') {
|
|
502
|
+
report(property.key, 'dynamic-style-key', 'Style names must be statically known.');
|
|
503
|
+
continue;
|
|
410
504
|
}
|
|
411
|
-
|
|
412
|
-
|
|
505
|
+
const className = classNameFor(owner, key);
|
|
506
|
+
taken.add(className);
|
|
507
|
+
if (owner !== binding)
|
|
508
|
+
alias[key] = className;
|
|
509
|
+
if (property.value.type === 'ArrowFunctionExpression' ||
|
|
510
|
+
property.value.type === 'FunctionExpression') {
|
|
511
|
+
const params = [];
|
|
512
|
+
let valid = true;
|
|
513
|
+
for (const parameter of property.value.params) {
|
|
514
|
+
if (parameter.type !== 'Identifier') {
|
|
515
|
+
valid = false;
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
params.push(parameter.name);
|
|
519
|
+
}
|
|
520
|
+
let body = property.value.body;
|
|
521
|
+
if (body.type === 'BlockStatement') {
|
|
522
|
+
const returned = body.body.find((statement) => statement.type === 'ReturnStatement');
|
|
523
|
+
body = returned?.argument;
|
|
524
|
+
}
|
|
525
|
+
if (!valid || body?.type !== 'ObjectExpression') {
|
|
526
|
+
report(property.value, 'function-style', 'Function style keys need identifier parameters and an object return value.');
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
const variables = params.map((parameter) => `--${(0, zss_engine_1.camelToKebabCase)(owner)}-${(0, zss_engine_1.camelToKebabCase)(key)}-${(0, zss_engine_1.camelToKebabCase)(parameter)}`);
|
|
530
|
+
const scope = new Map(values);
|
|
531
|
+
params.forEach((parameter, index) => {
|
|
532
|
+
scope.set(parameter, `var(${variables[index]})`);
|
|
533
|
+
});
|
|
534
|
+
functions[className] = { params, variables };
|
|
535
|
+
keys.push(className);
|
|
536
|
+
emit(body, `.${className}`, [], scope);
|
|
413
537
|
continue;
|
|
414
538
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
params.forEach((parameter, index) => {
|
|
418
|
-
scope.set(parameter, `var(${variables[index]})`);
|
|
419
|
-
});
|
|
420
|
-
functions[key] = { params, variables };
|
|
421
|
-
keys.push(key);
|
|
422
|
-
emit(body, `.${key}`, [], scope);
|
|
423
|
-
continue;
|
|
539
|
+
keys.push(className);
|
|
540
|
+
emit(property.value, `.${className}`);
|
|
424
541
|
}
|
|
425
|
-
|
|
426
|
-
|
|
542
|
+
if (owner !== binding)
|
|
543
|
+
aliases[owner] = alias;
|
|
427
544
|
}
|
|
428
545
|
return {
|
|
429
546
|
binding,
|
|
@@ -431,6 +548,8 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
431
548
|
globalCss: `${themes.globalCss}${animations.globalCss}`,
|
|
432
549
|
keys,
|
|
433
550
|
functions,
|
|
551
|
+
aliases,
|
|
552
|
+
definitionOnly: creates.every((create) => !bindsElsewhere(ast, create.binding, create.declaration.id)),
|
|
434
553
|
reports,
|
|
435
554
|
};
|
|
436
555
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { Rule } from 'eslint';
|
|
2
2
|
export interface ReleaseModule {
|
|
3
3
|
source: string;
|
|
4
|
+
target?: string;
|
|
4
5
|
binding: string;
|
|
6
|
+
definitionOnly?: boolean;
|
|
7
|
+
aliases?: Record<string, Record<string, string>>;
|
|
5
8
|
functions?: Record<string, {
|
|
6
9
|
params: string[];
|
|
7
10
|
variables: string[];
|
|
@@ -11,6 +14,7 @@ export interface ReleaseStylesOptions {
|
|
|
11
14
|
modules: Record<string, ReleaseModule>;
|
|
12
15
|
themes?: Record<string, string[]>;
|
|
13
16
|
animations?: Record<string, string[]>;
|
|
17
|
+
statics?: Record<string, string[]>;
|
|
14
18
|
styleProp?: string;
|
|
15
19
|
active?: boolean;
|
|
16
20
|
}
|
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.releaseStyles = exports.__private = void 0;
|
|
37
37
|
const path = __importStar(require("node:path"));
|
|
38
|
+
const resolve_1 = require("../resolve");
|
|
38
39
|
const memberPath = (node) => {
|
|
39
40
|
if (node?.type === 'Identifier')
|
|
40
41
|
return [node.name];
|
|
@@ -54,6 +55,7 @@ const isCreate = (node) => {
|
|
|
54
55
|
const path = memberPath(node.callee);
|
|
55
56
|
return path?.length === 2 && path[1] === 'create';
|
|
56
57
|
};
|
|
58
|
+
const IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
57
59
|
const needsFilter = (node) => node.type === 'LogicalExpression' ||
|
|
58
60
|
node.type === 'ConditionalExpression' ||
|
|
59
61
|
node.type === 'SpreadElement';
|
|
@@ -72,6 +74,8 @@ exports.releaseStyles = {
|
|
|
72
74
|
themeImport: 'Remove the createTheme import after inlining its CSS variables.',
|
|
73
75
|
animation: '`keyframes` or `viewTransition` was written globally.',
|
|
74
76
|
animationImport: 'Remove the animation import after replacing it with its generated name.',
|
|
77
|
+
staticImport: 'Remove the createStatic import after inlining its values into the CSS.',
|
|
78
|
+
alias: 'Read this style from the CSS Module the whole file was exported to.',
|
|
75
79
|
core: 'Remove the Plumeria import after exporting the styles.',
|
|
76
80
|
prop: 'Use className after exporting this style to CSS Modules.',
|
|
77
81
|
composition: 'Join the composed CSS Module class names.',
|
|
@@ -85,6 +89,7 @@ exports.releaseStyles = {
|
|
|
85
89
|
modules: { type: 'object' },
|
|
86
90
|
themes: { type: 'object' },
|
|
87
91
|
animations: { type: 'object' },
|
|
92
|
+
statics: { type: 'object' },
|
|
88
93
|
styleProp: { type: 'string' },
|
|
89
94
|
active: { type: 'boolean' },
|
|
90
95
|
},
|
|
@@ -94,43 +99,80 @@ exports.releaseStyles = {
|
|
|
94
99
|
},
|
|
95
100
|
create(context) {
|
|
96
101
|
const raw = (context.options[0] ?? {});
|
|
97
|
-
const
|
|
102
|
+
const declared = raw.modules?.[context.filename];
|
|
103
|
+
const entry = declared?.definitionOnly ? undefined : declared;
|
|
98
104
|
const themeBindings = raw.themes?.[context.filename] ?? [];
|
|
99
105
|
const animationBindings = raw.animations?.[context.filename] ?? [];
|
|
100
106
|
const styleProp = raw.styleProp ?? 'classStyle';
|
|
101
107
|
const sourceCode = context.sourceCode;
|
|
102
108
|
let active = Boolean(entry || raw.active);
|
|
103
109
|
let activeModule = entry;
|
|
104
|
-
const
|
|
105
|
-
if (!
|
|
106
|
-
return
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
const specifierFor = (module) => {
|
|
111
|
+
if (!module.target)
|
|
112
|
+
return module.source;
|
|
113
|
+
const relative = path
|
|
114
|
+
.relative(path.dirname(context.filename), module.target)
|
|
115
|
+
.split(path.sep)
|
|
116
|
+
.join('/');
|
|
117
|
+
return relative.startsWith('.') ? relative : `./${relative}`;
|
|
111
118
|
};
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
+
const importOf = (specifiers, source) => {
|
|
120
|
+
const defaultImport = specifiers.find((specifier) => specifier.type === 'ImportDefaultSpecifier');
|
|
121
|
+
const namedImports = specifiers.filter((specifier) => specifier.type === 'ImportSpecifier');
|
|
122
|
+
const clauses = [];
|
|
123
|
+
if (defaultImport)
|
|
124
|
+
clauses.push(sourceCode.getText(defaultImport));
|
|
125
|
+
if (namedImports.length > 0) {
|
|
126
|
+
clauses.push(`{ ${namedImports
|
|
127
|
+
.map((specifier) => sourceCode.getText(specifier))
|
|
128
|
+
.join(', ')} }`);
|
|
129
|
+
}
|
|
130
|
+
return `import ${clauses.join(', ')} from ${sourceCode.getText(source)};`;
|
|
119
131
|
};
|
|
120
|
-
const
|
|
121
|
-
|
|
132
|
+
const importedFrom = (specifier, table) => {
|
|
133
|
+
const resolved = (0, resolve_1.resolveSourcePath)(specifier, context.filename);
|
|
134
|
+
return resolved ? table?.[resolved] : undefined;
|
|
135
|
+
};
|
|
136
|
+
const importedReleasedModule = (specifier) => importedFrom(specifier, raw.modules);
|
|
137
|
+
const importedThemeBindings = (specifier) => importedFrom(specifier, raw.themes);
|
|
138
|
+
const importedAnimationBindings = (specifier) => importedFrom(specifier, raw.animations);
|
|
139
|
+
const importedStaticBindings = (specifier) => importedFrom(specifier, raw.statics);
|
|
140
|
+
const releasedRange = () => {
|
|
141
|
+
if (!entry)
|
|
122
142
|
return undefined;
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
143
|
+
for (const statement of sourceCode.ast.body) {
|
|
144
|
+
const declaration = statement.type === 'ExportNamedDeclaration'
|
|
145
|
+
? statement.declaration
|
|
146
|
+
: statement;
|
|
147
|
+
if (declaration?.type !== 'VariableDeclaration')
|
|
148
|
+
continue;
|
|
149
|
+
for (const candidate of declaration.declarations) {
|
|
150
|
+
if (candidate.id.type === 'Identifier' &&
|
|
151
|
+
candidate.id.name === entry.binding &&
|
|
152
|
+
isCreate(candidate.init))
|
|
153
|
+
return statement.range;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return undefined;
|
|
127
157
|
};
|
|
128
|
-
const
|
|
158
|
+
const onlyUsedByReleasedStyles = (local) => {
|
|
159
|
+
const range = releasedRange();
|
|
160
|
+
if (!range)
|
|
161
|
+
return false;
|
|
162
|
+
const scope = sourceCode.getScope(sourceCode.ast);
|
|
163
|
+
const moduleScope = scope.type === 'global' ? (scope.childScopes[0] ?? scope) : scope;
|
|
164
|
+
const variable = moduleScope.variables.find((candidate) => candidate.name === local);
|
|
165
|
+
if (!variable || variable.references.length === 0)
|
|
166
|
+
return false;
|
|
167
|
+
return variable.references.every((reference) => reference.identifier.range[0] >= range[0] &&
|
|
168
|
+
reference.identifier.range[1] <= range[1]);
|
|
169
|
+
};
|
|
170
|
+
const removeImportedBindings = (node, bindings, messageId, dead) => {
|
|
129
171
|
const removed = node.specifiers.filter((specifier) => {
|
|
130
172
|
if (specifier.type !== 'ImportSpecifier')
|
|
131
173
|
return false;
|
|
132
174
|
const name = specifier.imported.name ?? specifier.imported.value;
|
|
133
|
-
return bindings.includes(name);
|
|
175
|
+
return bindings.includes(name) && (!dead || dead(specifier));
|
|
134
176
|
});
|
|
135
177
|
if (removed.length === 0)
|
|
136
178
|
return false;
|
|
@@ -138,21 +180,9 @@ exports.releaseStyles = {
|
|
|
138
180
|
context.report({
|
|
139
181
|
node,
|
|
140
182
|
messageId,
|
|
141
|
-
fix: (fixer) =>
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const defaultImport = retained.find((specifier) => specifier.type === 'ImportDefaultSpecifier');
|
|
145
|
-
const namedImports = retained.filter((specifier) => specifier.type === 'ImportSpecifier');
|
|
146
|
-
const clauses = [];
|
|
147
|
-
if (defaultImport)
|
|
148
|
-
clauses.push(sourceCode.getText(defaultImport));
|
|
149
|
-
if (namedImports.length > 0) {
|
|
150
|
-
clauses.push(`{ ${namedImports
|
|
151
|
-
.map((specifier) => sourceCode.getText(specifier))
|
|
152
|
-
.join(', ')} }`);
|
|
153
|
-
}
|
|
154
|
-
return fixer.replaceText(node, `import ${clauses.join(', ')} from ${sourceCode.getText(node.source)};`);
|
|
155
|
-
},
|
|
183
|
+
fix: (fixer) => retained.length === 0
|
|
184
|
+
? fixer.remove(node)
|
|
185
|
+
: fixer.replaceText(node, importOf(retained, node.source)),
|
|
156
186
|
});
|
|
157
187
|
return true;
|
|
158
188
|
};
|
|
@@ -177,16 +207,30 @@ exports.releaseStyles = {
|
|
|
177
207
|
if (removeImportedBindings(node, importedThemes, 'themeImport'))
|
|
178
208
|
return;
|
|
179
209
|
}
|
|
210
|
+
const importedStatics = importedStaticBindings(String(node.source.value));
|
|
211
|
+
if (importedStatics) {
|
|
212
|
+
if (removeImportedBindings(node, importedStatics, 'staticImport', (specifier) => onlyUsedByReleasedStyles(specifier.local.name)))
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
180
215
|
const importedModule = importedReleasedModule(String(node.source.value));
|
|
181
216
|
if (importedModule) {
|
|
182
217
|
active = true;
|
|
183
218
|
const importedBinding = node.specifiers.find((specifier) => specifier.type === 'ImportSpecifier' &&
|
|
184
219
|
(specifier.imported.name ?? specifier.imported.value) ===
|
|
185
220
|
importedModule.binding);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
221
|
+
const local = importedBinding?.local.name ?? importedModule.binding;
|
|
222
|
+
activeModule = { ...importedModule, binding: local };
|
|
223
|
+
if (importedModule.definitionOnly && importedBinding) {
|
|
224
|
+
const retained = node.specifiers.filter((specifier) => specifier !== importedBinding);
|
|
225
|
+
const released = `import ${local} from '${specifierFor(importedModule)}';`;
|
|
226
|
+
context.report({
|
|
227
|
+
node,
|
|
228
|
+
messageId: 'definition',
|
|
229
|
+
fix: (fixer) => fixer.replaceText(node, retained.length === 0
|
|
230
|
+
? released
|
|
231
|
+
: `${released}\n${importOf(retained, node.source)}`),
|
|
232
|
+
});
|
|
233
|
+
}
|
|
190
234
|
}
|
|
191
235
|
},
|
|
192
236
|
VariableDeclaration(node) {
|
|
@@ -218,8 +262,18 @@ exports.releaseStyles = {
|
|
|
218
262
|
}
|
|
219
263
|
if (!entry)
|
|
220
264
|
return;
|
|
221
|
-
if (declaration.
|
|
222
|
-
|
|
265
|
+
if (!isCreate(declaration.init))
|
|
266
|
+
return;
|
|
267
|
+
if (entry.aliases?.[declaration.id.name]) {
|
|
268
|
+
const aliased = node.parent?.type === 'ExportNamedDeclaration' ? node.parent : node;
|
|
269
|
+
context.report({
|
|
270
|
+
node: aliased,
|
|
271
|
+
messageId: 'definition',
|
|
272
|
+
fix: (fixer) => fixer.remove(aliased),
|
|
273
|
+
});
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (declaration.id.name !== entry.binding)
|
|
223
277
|
return;
|
|
224
278
|
const exported = node.parent?.type === 'ExportNamedDeclaration';
|
|
225
279
|
const target = exported ? node.parent : node;
|
|
@@ -233,6 +287,29 @@ exports.releaseStyles = {
|
|
|
233
287
|
fix: (fixer) => fixer.replaceText(target, replacement),
|
|
234
288
|
});
|
|
235
289
|
},
|
|
290
|
+
MemberExpression(node) {
|
|
291
|
+
if (!entry?.aliases || node.object.type !== 'Identifier')
|
|
292
|
+
return;
|
|
293
|
+
const alias = entry.aliases[node.object.name];
|
|
294
|
+
if (!alias)
|
|
295
|
+
return;
|
|
296
|
+
const key = node.computed
|
|
297
|
+
? node.property.type === 'Literal'
|
|
298
|
+
? String(node.property.value)
|
|
299
|
+
: undefined
|
|
300
|
+
: node.property.name;
|
|
301
|
+
const className = key ? alias[key] : undefined;
|
|
302
|
+
if (!className)
|
|
303
|
+
return;
|
|
304
|
+
const access = IDENTIFIER.test(className)
|
|
305
|
+
? `.${className}`
|
|
306
|
+
: `['${className}']`;
|
|
307
|
+
context.report({
|
|
308
|
+
node,
|
|
309
|
+
messageId: 'alias',
|
|
310
|
+
fix: (fixer) => fixer.replaceText(node, `${entry.binding}${access}`),
|
|
311
|
+
});
|
|
312
|
+
},
|
|
236
313
|
JSXAttribute(node) {
|
|
237
314
|
if (!active)
|
|
238
315
|
return;
|
|
@@ -242,23 +319,34 @@ exports.releaseStyles = {
|
|
|
242
319
|
? node.value.expression
|
|
243
320
|
: undefined;
|
|
244
321
|
const moduleInfo = activeModule;
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
322
|
+
const readsModule = (callee) => Boolean(moduleInfo &&
|
|
323
|
+
callee?.type === 'MemberExpression' &&
|
|
324
|
+
callee.object.type === 'Identifier' &&
|
|
325
|
+
(callee.object.name === moduleInfo.binding ||
|
|
326
|
+
moduleInfo.aliases?.[callee.object.name]));
|
|
327
|
+
const styleKey = (callee) => {
|
|
328
|
+
if (!readsModule(callee))
|
|
251
329
|
return undefined;
|
|
252
|
-
const
|
|
253
|
-
?
|
|
254
|
-
? String(
|
|
330
|
+
const raw = callee.computed
|
|
331
|
+
? callee.property.type === 'Literal'
|
|
332
|
+
? String(callee.property.value)
|
|
255
333
|
: undefined
|
|
256
|
-
:
|
|
334
|
+
: callee.property.name;
|
|
335
|
+
if (raw === undefined)
|
|
336
|
+
return undefined;
|
|
337
|
+
const alias = moduleInfo.aliases?.[callee.object.name];
|
|
338
|
+
return alias ? alias[raw] : raw;
|
|
339
|
+
};
|
|
340
|
+
const functionStyle = (value) => {
|
|
341
|
+
if (!moduleInfo || value?.type !== 'CallExpression')
|
|
342
|
+
return undefined;
|
|
343
|
+
const key = styleKey(value.callee);
|
|
257
344
|
const func = key ? moduleInfo.functions?.[key] : undefined;
|
|
258
345
|
if (!key || !func || func.variables.length !== value.arguments.length)
|
|
259
346
|
return undefined;
|
|
347
|
+
const access = IDENTIFIER.test(key) ? `.${key}` : `['${key}']`;
|
|
260
348
|
return {
|
|
261
|
-
className: `${moduleInfo.binding}
|
|
349
|
+
className: `${moduleInfo.binding}${access}`,
|
|
262
350
|
declarations: func.variables.map((variable, index) => {
|
|
263
351
|
const argument = sourceCode.getText(value.arguments[index]);
|
|
264
352
|
return `'${variable}': ${argument}`;
|
|
@@ -299,9 +387,7 @@ exports.releaseStyles = {
|
|
|
299
387
|
return;
|
|
300
388
|
}
|
|
301
389
|
if (expression?.type === 'CallExpression' &&
|
|
302
|
-
expression.callee
|
|
303
|
-
expression.callee.object.type === 'Identifier' &&
|
|
304
|
-
expression.callee.object.name === moduleInfo?.binding) {
|
|
390
|
+
readsModule(expression.callee)) {
|
|
305
391
|
context.report({
|
|
306
392
|
node,
|
|
307
393
|
messageId: 'unsupportedFunctionStyle',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plumeria/codemod",
|
|
3
|
-
"version": "18.2.
|
|
3
|
+
"version": "18.2.19",
|
|
4
4
|
"description": "Codemods for migrating Plumeria APIs",
|
|
5
5
|
"author": "Refirst 11",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@typescript-eslint/parser": "^8.60.0",
|
|
36
36
|
"eslint": "^10.4.1",
|
|
37
|
+
"get-tsconfig": "^4.14.2",
|
|
37
38
|
"postcss": "8.5.25",
|
|
38
39
|
"postcss-selector-parser": "^7.1.1",
|
|
39
40
|
"zss-engine": "2.6.0"
|