@plumeria/codemod 18.2.19 → 18.2.21
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 +61 -9
- package/dist/cli.js +13 -3
- package/dist/migrate.js +3 -1
- package/dist/release.d.ts +3 -0
- package/dist/release.js +369 -38
- package/dist/transforms/adopt-styles.d.ts +2 -0
- package/dist/transforms/adopt-styles.js +320 -10
- package/dist/transforms/css-modules.d.ts +5 -1
- package/dist/transforms/css-modules.js +59 -5
- package/dist/transforms/from-plumeria.d.ts +17 -1
- package/dist/transforms/from-plumeria.js +276 -10
- package/dist/transforms/release-styles.d.ts +5 -0
- package/dist/transforms/release-styles.js +535 -67
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -134,6 +134,7 @@ export function Card({ width }: { width: number }) {
|
|
|
134
134
|
/* Card.tsx — after */
|
|
135
135
|
- import * as css from '@plumeria/core';
|
|
136
136
|
+ import styles from './Card.module.css';
|
|
137
|
+
+ export { styles };
|
|
137
138
|
|
|
138
139
|
- export const styles = css.create({
|
|
139
140
|
- base: { fontSize: 12 },
|
|
@@ -143,14 +144,13 @@ export function Card({ width }: { width: number }) {
|
|
|
143
144
|
- },
|
|
144
145
|
- size: (width: number) => ({ width }),
|
|
145
146
|
- });
|
|
146
|
-
+ export { styles };
|
|
147
147
|
|
|
148
148
|
export function Card({ width }: { width: number }) {
|
|
149
149
|
- return <div classStyle={[styles.base, styles.card, styles.size(width)]} />;
|
|
150
150
|
+ return (
|
|
151
151
|
+ <div
|
|
152
|
-
+ className={
|
|
153
|
-
+ style={{ '--styles-size-width': width }}
|
|
152
|
+
+ className={[styles.base, styles.card, styles.size].join(' ')}
|
|
153
|
+
+ style={{ ['--styles-size-width' as string]: width }}
|
|
154
154
|
+ />
|
|
155
155
|
+ );
|
|
156
156
|
}
|
|
@@ -162,15 +162,54 @@ equivalent, so each parameter becomes a CSS custom property named
|
|
|
162
162
|
values move to the inline `style` prop. If a `style` prop already exists, the
|
|
163
163
|
custom properties are merged into it.
|
|
164
164
|
|
|
165
|
-
An array
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
An array with no condition in it collapses into one class that `composes` its
|
|
166
|
+
members, so nothing is duplicated:
|
|
167
|
+
|
|
168
|
+
```diff
|
|
169
|
+
- <div classStyle={[styles.base, styles.card]} />
|
|
170
|
+
+ <div className={styles.baseCard} />
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Otherwise the array is joined as it stands. `filter` is added only when a
|
|
174
|
+
condition can make a member falsy, so that branch drops out at runtime:
|
|
168
175
|
|
|
169
176
|
```diff
|
|
177
|
+
- <div classStyle={[styles.base, styles.card, styles.size(width)]} />
|
|
178
|
+
+ <div className={[styles.base, styles.card, styles.size].join(' ')} style={{ … }} />
|
|
179
|
+
|
|
170
180
|
- <div classStyle={[styles.base, active && styles.active]} />
|
|
171
181
|
+ <div className={[styles.base, active && styles.active].filter(Boolean).join(' ')} />
|
|
172
182
|
```
|
|
173
183
|
|
|
184
|
+
`classStyle={[b, a]}` gives `a` the last word, while a stylesheet gives it to
|
|
185
|
+
whichever rule is written later. The export reproduces the array order by
|
|
186
|
+
emitting the classes in an order that satisfies every call site at once. Where
|
|
187
|
+
two call sites compose the same pair in opposite orders, the one that cannot be
|
|
188
|
+
satisfied carries an extra class holding only the disputed declarations, applied
|
|
189
|
+
under the same condition as the style that has to win:
|
|
190
|
+
|
|
191
|
+
```diff
|
|
192
|
+
- <div classStyle={[styles.raised, flat && styles.surface]} />
|
|
193
|
+
+ <div className={[styles.raised, flat && styles.surface, flat && styles.surfaceOverRaised].filter(Boolean).join(' ')} />
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Order is only forced where the two classes actually disagree, measured per
|
|
197
|
+
at-rule and per selector. Anything left unsettled is reported as
|
|
198
|
+
`composition-order`.
|
|
199
|
+
|
|
200
|
+
A variant picked with a bracket is resolved to the class its key names, whether
|
|
201
|
+
the constant is declared in the same file or imported, and a constant left
|
|
202
|
+
naming nothing is removed with it:
|
|
203
|
+
|
|
204
|
+
```diff
|
|
205
|
+
- const size = 'small';
|
|
206
|
+
- <div classStyle={styles[size]} />
|
|
207
|
+
+ <div className={styles.small} />
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Only a `const` holding a string literal is followed. A key computed at runtime is
|
|
211
|
+
reported as `dynamic-style-access` and its definitions stay in Plumeria.
|
|
212
|
+
|
|
174
213
|
### Global styles
|
|
175
214
|
|
|
176
215
|
`css.createTheme`, `css.keyframes`, and `css.viewTransition` produce rules that
|
|
@@ -195,12 +234,25 @@ src/Card.tsx
|
|
|
195
234
|
```
|
|
196
235
|
|
|
197
236
|
Constructs that cannot be resolved statically — dynamic values, computed keys,
|
|
198
|
-
runtime function calls, object spreads
|
|
199
|
-
|
|
200
|
-
exists is reported as `target-exists` and will not be
|
|
237
|
+
runtime function calls, and object spreads — are reported and left in place,
|
|
238
|
+
together with every file whose definitions they still need. A target
|
|
239
|
+
`*.module.css` that already exists is reported as `target-exists` and will not be
|
|
240
|
+
overwritten.
|
|
241
|
+
|
|
242
|
+
Several `css.create` calls in one file are not reported: they are written to the
|
|
243
|
+
same stylesheet, and a key an earlier call claimed is renamed.
|
|
201
244
|
|
|
202
245
|
The exit code is 1 while anything remains, so the command composes with a script.
|
|
203
246
|
|
|
247
|
+
The migration is idempotent — the first run converts, every run after it is a
|
|
248
|
+
fixed point:
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
f(f(x)) = f(x)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Clear a report and run again, and only the newly resolvable files move.
|
|
255
|
+
|
|
204
256
|
Start with `-d, --dry-run` to preview the export without writing files:
|
|
205
257
|
|
|
206
258
|
```sh
|
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, statics) {
|
|
334
|
+
function releaseConfig(modules, themes, animations, statics, values, complete, constants) {
|
|
335
335
|
return [
|
|
336
336
|
{ ignores: IGNORES },
|
|
337
337
|
{
|
|
@@ -347,7 +347,15 @@ function releaseConfig(modules, themes, animations, statics) {
|
|
|
347
347
|
rules: {
|
|
348
348
|
'codemod/release-styles': [
|
|
349
349
|
'error',
|
|
350
|
-
{
|
|
350
|
+
{
|
|
351
|
+
modules,
|
|
352
|
+
themes,
|
|
353
|
+
animations,
|
|
354
|
+
statics,
|
|
355
|
+
values,
|
|
356
|
+
complete,
|
|
357
|
+
constants,
|
|
358
|
+
},
|
|
351
359
|
],
|
|
352
360
|
},
|
|
353
361
|
},
|
|
@@ -365,6 +373,8 @@ async function migrateFromPlumeria(options) {
|
|
|
365
373
|
return 0;
|
|
366
374
|
}
|
|
367
375
|
for (const sheet of planned.stylesheets) {
|
|
376
|
+
if (sheet.reports.length > 0)
|
|
377
|
+
continue;
|
|
368
378
|
console.log(` ${relative(sheet.source)} -> ${relative(sheet.target)}`);
|
|
369
379
|
}
|
|
370
380
|
if (planned.global) {
|
|
@@ -372,7 +382,7 @@ async function migrateFromPlumeria(options) {
|
|
|
372
382
|
}
|
|
373
383
|
const config = {
|
|
374
384
|
overrideConfigFile: true,
|
|
375
|
-
overrideConfig: releaseConfig(planned.modules, planned.themes, planned.animations, planned.statics),
|
|
385
|
+
overrideConfig: releaseConfig(planned.modules, planned.themes, planned.animations, planned.statics, planned.values, planned.complete, planned.constants),
|
|
376
386
|
errorOnUnmatchedPattern: false,
|
|
377
387
|
};
|
|
378
388
|
const preview = await new eslint_1.ESLint(config).lintFiles(targets);
|
package/dist/migrate.js
CHANGED
|
@@ -83,10 +83,12 @@ function plan(targets) {
|
|
|
83
83
|
const converted = (0, css_modules_1.convertStylesheet)(fs.readFileSync(source, 'utf-8'));
|
|
84
84
|
const target = (0, exports.targetPath)(source);
|
|
85
85
|
stylesheets.push({ source, target, reports: converted.reports });
|
|
86
|
-
modules[
|
|
86
|
+
modules[source] = {
|
|
87
87
|
source: `./${path.basename(target, '.ts')}`,
|
|
88
|
+
target,
|
|
88
89
|
names: converted.names,
|
|
89
90
|
composes: converted.composes,
|
|
91
|
+
functions: converted.functions,
|
|
90
92
|
};
|
|
91
93
|
}
|
|
92
94
|
return { stylesheets, modules };
|
package/dist/release.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ export interface ReleasePlan {
|
|
|
12
12
|
themes: Record<string, string[]>;
|
|
13
13
|
animations: Record<string, string[]>;
|
|
14
14
|
statics: Record<string, string[]>;
|
|
15
|
+
values: Record<string, Record<string, unknown>>;
|
|
16
|
+
complete: boolean;
|
|
17
|
+
constants: Record<string, Record<string, string>>;
|
|
15
18
|
global?: {
|
|
16
19
|
target: string;
|
|
17
20
|
css: string;
|
package/dist/release.js
CHANGED
|
@@ -42,6 +42,8 @@ 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 */';
|
|
46
|
+
const GLOBAL_CLOSE = '/* End of @plumeria/codemod generated styles */';
|
|
45
47
|
const EXTENSION = /\.(?:[cm]?[jt]sx?)$/;
|
|
46
48
|
const IGNORED = new Set([
|
|
47
49
|
'node_modules',
|
|
@@ -75,6 +77,44 @@ const findSources = (targets) => {
|
|
|
75
77
|
}
|
|
76
78
|
return found.sort();
|
|
77
79
|
};
|
|
80
|
+
const STYLE_PROP = 'classStyle';
|
|
81
|
+
const memberOf = (node) => {
|
|
82
|
+
if (node?.type !== 'MemberExpression' || node.object.type !== 'Identifier')
|
|
83
|
+
return undefined;
|
|
84
|
+
const key = node.computed
|
|
85
|
+
? node.property.type === 'Literal'
|
|
86
|
+
? String(node.property.value)
|
|
87
|
+
: undefined
|
|
88
|
+
: node.property.name;
|
|
89
|
+
return key === undefined ? undefined : { object: node.object.name, key };
|
|
90
|
+
};
|
|
91
|
+
const slotOf = (node) => {
|
|
92
|
+
const member = memberOf(node);
|
|
93
|
+
if (member)
|
|
94
|
+
return [member];
|
|
95
|
+
if (node?.type === 'LogicalExpression')
|
|
96
|
+
return slotOf(node.right);
|
|
97
|
+
if (node?.type === 'ConditionalExpression')
|
|
98
|
+
return [...slotOf(node.consequent), ...slotOf(node.alternate)];
|
|
99
|
+
return [];
|
|
100
|
+
};
|
|
101
|
+
const walk = (node, visit) => {
|
|
102
|
+
if (!node || typeof node !== 'object')
|
|
103
|
+
return;
|
|
104
|
+
if (Array.isArray(node)) {
|
|
105
|
+
for (const child of node)
|
|
106
|
+
walk(child, visit);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (typeof node.type !== 'string')
|
|
110
|
+
return;
|
|
111
|
+
visit(node);
|
|
112
|
+
for (const key of Object.keys(node)) {
|
|
113
|
+
if (key === 'parent' || key === 'loc' || key === 'range')
|
|
114
|
+
continue;
|
|
115
|
+
walk(node[key], visit);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
78
118
|
const releasedPath = (source) => source.replace(/(?:\.styles)?\.[^.]+$/, '.module.css');
|
|
79
119
|
exports.releasedPath = releasedPath;
|
|
80
120
|
const globalPath = (targets) => {
|
|
@@ -95,12 +135,34 @@ function planRelease(targets) {
|
|
|
95
135
|
const themes = {};
|
|
96
136
|
const animations = {};
|
|
97
137
|
const statics = {};
|
|
98
|
-
const globalRules = [];
|
|
99
138
|
const sources = findSources(targets);
|
|
100
139
|
const sourceText = new Map(sources.map((source) => [source, fs.readFileSync(source, 'utf8')]));
|
|
101
140
|
const themeValues = new Map();
|
|
102
141
|
const animationValues = new Map();
|
|
103
142
|
const staticValues = new Map();
|
|
143
|
+
const reportsBySource = new Map();
|
|
144
|
+
const globalBySource = new Map();
|
|
145
|
+
const themeKeys = new Map();
|
|
146
|
+
const animationKeys = new Map();
|
|
147
|
+
const staticKeys = new Map();
|
|
148
|
+
const dependencies = new Map();
|
|
149
|
+
const note = (source, reports) => {
|
|
150
|
+
if (reports.length === 0)
|
|
151
|
+
return;
|
|
152
|
+
reportsBySource.set(source, [
|
|
153
|
+
...(reportsBySource.get(source) ?? []),
|
|
154
|
+
...reports,
|
|
155
|
+
]);
|
|
156
|
+
};
|
|
157
|
+
const addGlobal = (source, css) => {
|
|
158
|
+
const trimmed = css.trim();
|
|
159
|
+
if (trimmed.length === 0)
|
|
160
|
+
return;
|
|
161
|
+
globalBySource.set(source, [
|
|
162
|
+
...(globalBySource.get(source) ?? []),
|
|
163
|
+
trimmed,
|
|
164
|
+
]);
|
|
165
|
+
};
|
|
104
166
|
for (const source of sources) {
|
|
105
167
|
try {
|
|
106
168
|
const extracted = (0, from_plumeria_1.extractPlumeriaStatics)(sourceText.get(source));
|
|
@@ -116,8 +178,6 @@ function planRelease(targets) {
|
|
|
116
178
|
if (Object.keys(extracted.bindings).length === 0)
|
|
117
179
|
continue;
|
|
118
180
|
themeValues.set(source, extracted.bindings);
|
|
119
|
-
themes[source] = Object.keys(extracted.bindings);
|
|
120
|
-
globalRules.push(extracted.globalCss.trim());
|
|
121
181
|
}
|
|
122
182
|
catch {
|
|
123
183
|
}
|
|
@@ -152,11 +212,14 @@ function planRelease(targets) {
|
|
|
152
212
|
for (const specifier of node.specifiers) {
|
|
153
213
|
if (specifier.type === 'ImportSpecifier') {
|
|
154
214
|
const imported = specifier.imported.name ?? specifier.imported.value;
|
|
155
|
-
if (available[imported])
|
|
215
|
+
if (available[imported]) {
|
|
156
216
|
result[specifier.local.name] = available[imported];
|
|
217
|
+
dependencies.set(source, (dependencies.get(source) ?? new Set()).add(target));
|
|
218
|
+
}
|
|
157
219
|
}
|
|
158
220
|
else if (specifier.type === 'ImportNamespaceSpecifier') {
|
|
159
221
|
result[specifier.local.name] = available;
|
|
222
|
+
dependencies.set(source, (dependencies.get(source) ?? new Set()).add(target));
|
|
160
223
|
}
|
|
161
224
|
}
|
|
162
225
|
}
|
|
@@ -178,13 +241,26 @@ function planRelease(targets) {
|
|
|
178
241
|
}
|
|
179
242
|
return merged;
|
|
180
243
|
};
|
|
244
|
+
for (const source of sources) {
|
|
245
|
+
try {
|
|
246
|
+
const extracted = (0, from_plumeria_1.extractPlumeriaThemes)(sourceText.get(source));
|
|
247
|
+
note(source, extracted.reports);
|
|
248
|
+
if (Object.keys(extracted.bindings).length === 0)
|
|
249
|
+
continue;
|
|
250
|
+
themeKeys.set(source, Object.keys(extracted.bindings));
|
|
251
|
+
addGlobal(source, extracted.globalCss);
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
}
|
|
255
|
+
}
|
|
181
256
|
for (const source of sources) {
|
|
182
257
|
try {
|
|
183
258
|
const extracted = (0, from_plumeria_1.extractPlumeriaStatics)(sourceText.get(source), importedPlumeriaValues(source));
|
|
259
|
+
note(source, extracted.reports);
|
|
184
260
|
if (Object.keys(extracted.bindings).length === 0)
|
|
185
261
|
continue;
|
|
186
262
|
staticValues.set(source, extracted.bindings);
|
|
187
|
-
|
|
263
|
+
staticKeys.set(source, Object.keys(extracted.bindings));
|
|
188
264
|
}
|
|
189
265
|
catch {
|
|
190
266
|
}
|
|
@@ -195,59 +271,301 @@ function planRelease(targets) {
|
|
|
195
271
|
...importedPlumeriaValues(source),
|
|
196
272
|
...(themeValues.get(source) ?? {}),
|
|
197
273
|
});
|
|
274
|
+
note(source, extracted.reports);
|
|
198
275
|
if (Object.keys(extracted.bindings).length === 0)
|
|
199
276
|
continue;
|
|
200
277
|
animationValues.set(source, extracted.bindings);
|
|
201
|
-
|
|
278
|
+
animationKeys.set(source, [
|
|
202
279
|
...extracted.keyframes,
|
|
203
280
|
...extracted.viewTransitions,
|
|
204
|
-
];
|
|
205
|
-
|
|
281
|
+
]);
|
|
282
|
+
addGlobal(source, extracted.globalCss);
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const createBindings = new Map();
|
|
288
|
+
for (const source of sources) {
|
|
289
|
+
try {
|
|
290
|
+
const ast = (0, parser_1.parse)(sourceText.get(source), {
|
|
291
|
+
sourceType: 'module',
|
|
292
|
+
ecmaFeatures: { jsx: true },
|
|
293
|
+
});
|
|
294
|
+
const declared = new Set();
|
|
295
|
+
for (const raw of ast.body) {
|
|
296
|
+
const statement = raw.type === 'ExportNamedDeclaration' ? raw.declaration : raw;
|
|
297
|
+
if (statement?.type !== 'VariableDeclaration')
|
|
298
|
+
continue;
|
|
299
|
+
for (const declaration of statement.declarations) {
|
|
300
|
+
if (declaration.id.type === 'Identifier' &&
|
|
301
|
+
declaration.init?.type === 'CallExpression' &&
|
|
302
|
+
declaration.init.callee.type === 'MemberExpression' &&
|
|
303
|
+
declaration.init.callee.property?.name === 'create')
|
|
304
|
+
declared.add(declaration.id.name);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (declared.size > 0)
|
|
308
|
+
createBindings.set(source, declared);
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
const stringConstants = new Map();
|
|
314
|
+
const exportedConstants = new Map();
|
|
315
|
+
for (const source of sources) {
|
|
316
|
+
let ast;
|
|
317
|
+
try {
|
|
318
|
+
ast = (0, parser_1.parse)(sourceText.get(source), {
|
|
319
|
+
sourceType: 'module',
|
|
320
|
+
ecmaFeatures: { jsx: true },
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
const held = new Map();
|
|
327
|
+
const exported = new Map();
|
|
328
|
+
const duplicated = new Set();
|
|
329
|
+
const read = (statement, isExport) => {
|
|
330
|
+
if (statement?.type !== 'VariableDeclaration' ||
|
|
331
|
+
statement.kind !== 'const')
|
|
332
|
+
return;
|
|
333
|
+
for (const declaration of statement.declarations) {
|
|
334
|
+
if (declaration.id.type !== 'Identifier' ||
|
|
335
|
+
declaration.init?.type !== 'Literal' ||
|
|
336
|
+
typeof declaration.init.value !== 'string')
|
|
337
|
+
continue;
|
|
338
|
+
if (held.has(declaration.id.name))
|
|
339
|
+
duplicated.add(declaration.id.name);
|
|
340
|
+
held.set(declaration.id.name, declaration.init.value);
|
|
341
|
+
if (isExport)
|
|
342
|
+
exported.set(declaration.id.name, declaration.init.value);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
walk(ast.body, (node) => {
|
|
346
|
+
if (node.type === 'ExportNamedDeclaration')
|
|
347
|
+
read(node.declaration, true);
|
|
348
|
+
else
|
|
349
|
+
read(node, false);
|
|
350
|
+
});
|
|
351
|
+
for (const name of duplicated) {
|
|
352
|
+
held.delete(name);
|
|
353
|
+
exported.delete(name);
|
|
354
|
+
}
|
|
355
|
+
if (held.size > 0)
|
|
356
|
+
stringConstants.set(source, held);
|
|
357
|
+
if (exported.size > 0)
|
|
358
|
+
exportedConstants.set(source, exported);
|
|
359
|
+
}
|
|
360
|
+
const resolvedConstants = new Map();
|
|
361
|
+
const compositions = new Map();
|
|
362
|
+
for (const source of sources) {
|
|
363
|
+
let ast;
|
|
364
|
+
try {
|
|
365
|
+
ast = (0, parser_1.parse)(sourceText.get(source), {
|
|
366
|
+
sourceType: 'module',
|
|
367
|
+
ecmaFeatures: { jsx: true },
|
|
368
|
+
loc: true,
|
|
369
|
+
range: true,
|
|
370
|
+
});
|
|
206
371
|
}
|
|
207
372
|
catch {
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
const constants = new Map(stringConstants.get(source) ?? []);
|
|
376
|
+
for (const node of ast.body) {
|
|
377
|
+
if (node.type !== 'ImportDeclaration')
|
|
378
|
+
continue;
|
|
379
|
+
const target = (0, resolve_1.resolveSourcePath)(String(node.source.value), source);
|
|
380
|
+
const available = target ? exportedConstants.get(target) : undefined;
|
|
381
|
+
if (!available)
|
|
382
|
+
continue;
|
|
383
|
+
for (const specifier of node.specifiers) {
|
|
384
|
+
if (specifier.type !== 'ImportSpecifier')
|
|
385
|
+
continue;
|
|
386
|
+
const imported = specifier.imported.name ?? specifier.imported.value;
|
|
387
|
+
const value = available.get(imported);
|
|
388
|
+
if (value !== undefined)
|
|
389
|
+
constants.set(specifier.local.name, value);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (constants.size > 0)
|
|
393
|
+
resolvedConstants.set(source, constants);
|
|
394
|
+
const owners = new Map();
|
|
395
|
+
for (const binding of createBindings.get(source) ?? [])
|
|
396
|
+
owners.set(binding, { source, binding });
|
|
397
|
+
for (const node of ast.body) {
|
|
398
|
+
if (node.type !== 'ImportDeclaration')
|
|
399
|
+
continue;
|
|
400
|
+
const target = (0, resolve_1.resolveSourcePath)(String(node.source.value), source);
|
|
401
|
+
const declared = target ? createBindings.get(target) : undefined;
|
|
402
|
+
if (!target || !declared)
|
|
403
|
+
continue;
|
|
404
|
+
for (const specifier of node.specifiers) {
|
|
405
|
+
if (specifier.type !== 'ImportSpecifier')
|
|
406
|
+
continue;
|
|
407
|
+
const imported = specifier.imported.name ?? specifier.imported.value;
|
|
408
|
+
if (declared.has(imported))
|
|
409
|
+
owners.set(specifier.local.name, {
|
|
410
|
+
source: target,
|
|
411
|
+
binding: imported,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
208
414
|
}
|
|
415
|
+
walk(ast.body, (node) => {
|
|
416
|
+
if (node.type !== 'MemberExpression' ||
|
|
417
|
+
!node.computed ||
|
|
418
|
+
node.property.type === 'Literal' ||
|
|
419
|
+
node.object.type !== 'Identifier')
|
|
420
|
+
return;
|
|
421
|
+
const owner = owners.get(node.object.name);
|
|
422
|
+
if (!owner)
|
|
423
|
+
return;
|
|
424
|
+
if (node.property.type === 'Identifier' &&
|
|
425
|
+
constants.has(node.property.name))
|
|
426
|
+
return;
|
|
427
|
+
note(owner.source, [
|
|
428
|
+
{
|
|
429
|
+
line: node.loc.start.line,
|
|
430
|
+
column: node.loc.start.column + 1,
|
|
431
|
+
kind: 'dynamic-style-access',
|
|
432
|
+
hint: `\`${node.object.name}\` is read with a computed key, which has no class name until it runs.`,
|
|
433
|
+
},
|
|
434
|
+
]);
|
|
435
|
+
});
|
|
436
|
+
walk(ast.body, (node) => {
|
|
437
|
+
if (node.type !== 'JSXAttribute' ||
|
|
438
|
+
node.name?.name !== STYLE_PROP ||
|
|
439
|
+
node.value?.type !== 'JSXExpressionContainer')
|
|
440
|
+
return;
|
|
441
|
+
const expression = node.value.expression;
|
|
442
|
+
if (expression?.type !== 'ArrayExpression' ||
|
|
443
|
+
expression.elements.length < 2)
|
|
444
|
+
return;
|
|
445
|
+
const slots = expression.elements.map(slotOf);
|
|
446
|
+
if (slots.some((slot) => slot.length === 0))
|
|
447
|
+
return;
|
|
448
|
+
const owners_ = slots.flat().map((part) => owners.get(part.object));
|
|
449
|
+
if (owners_.some((candidate) => !candidate))
|
|
450
|
+
return;
|
|
451
|
+
const owner = owners_[0].source;
|
|
452
|
+
if (owners_.some((candidate) => candidate.source !== owner))
|
|
453
|
+
return;
|
|
454
|
+
const named = slots.map((slot) => slot.map((part) => ({
|
|
455
|
+
binding: owners.get(part.object).binding,
|
|
456
|
+
key: part.key,
|
|
457
|
+
})));
|
|
458
|
+
const collapsible = expression.elements.every((element) => memberOf(element)) &&
|
|
459
|
+
named.every((slot) => slot.length === 1);
|
|
460
|
+
compositions.set(owner, [
|
|
461
|
+
...(compositions.get(owner) ?? []),
|
|
462
|
+
{
|
|
463
|
+
parts: collapsible ? named.map((slot) => slot[0]) : [],
|
|
464
|
+
slots: named,
|
|
465
|
+
file: source,
|
|
466
|
+
line: node.loc.start.line,
|
|
467
|
+
column: node.loc.start.column + 1,
|
|
468
|
+
},
|
|
469
|
+
]);
|
|
470
|
+
});
|
|
209
471
|
}
|
|
472
|
+
const converted = new Map();
|
|
210
473
|
for (const source of sources) {
|
|
211
|
-
let
|
|
474
|
+
let module;
|
|
212
475
|
try {
|
|
213
|
-
|
|
476
|
+
module = (0, from_plumeria_1.convertPlumeriaModule)(sourceText.get(source), importedPlumeriaValues(source), compositions.get(source) ?? []);
|
|
214
477
|
}
|
|
215
478
|
catch {
|
|
216
479
|
continue;
|
|
217
480
|
}
|
|
218
|
-
if (!
|
|
481
|
+
if (!module)
|
|
219
482
|
continue;
|
|
220
483
|
const target = (0, exports.releasedPath)(source);
|
|
221
|
-
|
|
222
|
-
|
|
484
|
+
converted.set(source, module);
|
|
485
|
+
note(source, module.reports);
|
|
223
486
|
if (fs.existsSync(target)) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
487
|
+
note(source, [
|
|
488
|
+
{
|
|
489
|
+
line: 0,
|
|
490
|
+
column: 0,
|
|
491
|
+
kind: 'target-exists',
|
|
492
|
+
hint: `${path.basename(target)} already exists and was not overwritten.`,
|
|
493
|
+
},
|
|
494
|
+
]);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
const blocked = new Set(reportsBySource.keys());
|
|
498
|
+
for (let changed = true; changed;) {
|
|
499
|
+
changed = false;
|
|
500
|
+
for (const source of blocked) {
|
|
501
|
+
for (const dependency of dependencies.get(source) ?? []) {
|
|
502
|
+
if (blocked.has(dependency))
|
|
503
|
+
continue;
|
|
504
|
+
blocked.add(dependency);
|
|
505
|
+
note(dependency, [
|
|
506
|
+
{
|
|
507
|
+
line: 0,
|
|
508
|
+
column: 0,
|
|
509
|
+
kind: 'blocked-dependency',
|
|
510
|
+
hint: `${path.relative(path.dirname(dependency), source)} still needs these definitions.`,
|
|
511
|
+
},
|
|
512
|
+
]);
|
|
513
|
+
changed = true;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
const globalRules = [];
|
|
518
|
+
const values = {};
|
|
519
|
+
for (const source of sources) {
|
|
520
|
+
const reports = reportsBySource.get(source) ?? [];
|
|
521
|
+
const module = converted.get(source);
|
|
522
|
+
if (module) {
|
|
523
|
+
stylesheets.push({
|
|
524
|
+
source,
|
|
525
|
+
target: (0, exports.releasedPath)(source),
|
|
526
|
+
css: module.css,
|
|
527
|
+
reports,
|
|
229
528
|
});
|
|
230
529
|
}
|
|
231
|
-
|
|
232
|
-
source,
|
|
233
|
-
target,
|
|
234
|
-
css: converted.css,
|
|
235
|
-
reports,
|
|
236
|
-
});
|
|
237
|
-
if (reports.length === 0) {
|
|
238
|
-
modules[source] = {
|
|
239
|
-
source: specifier,
|
|
240
|
-
target,
|
|
241
|
-
binding: converted.binding,
|
|
242
|
-
...(converted.definitionOnly ? { definitionOnly: true } : {}),
|
|
243
|
-
...(Object.keys(converted.aliases).length > 0
|
|
244
|
-
? { aliases: converted.aliases }
|
|
245
|
-
: {}),
|
|
246
|
-
...(Object.keys(converted.functions).length > 0
|
|
247
|
-
? { functions: converted.functions }
|
|
248
|
-
: {}),
|
|
249
|
-
};
|
|
530
|
+
else if (reports.length > 0) {
|
|
531
|
+
stylesheets.push({ source, target: '', css: '', reports });
|
|
250
532
|
}
|
|
533
|
+
if (blocked.has(source))
|
|
534
|
+
continue;
|
|
535
|
+
const resolved = {
|
|
536
|
+
...(staticValues.get(source) ?? {}),
|
|
537
|
+
...(themeValues.get(source) ?? {}),
|
|
538
|
+
...(animationValues.get(source) ?? {}),
|
|
539
|
+
};
|
|
540
|
+
if (Object.keys(resolved).length > 0)
|
|
541
|
+
values[source] = resolved;
|
|
542
|
+
globalRules.push(...(globalBySource.get(source) ?? []));
|
|
543
|
+
if (themeKeys.has(source))
|
|
544
|
+
themes[source] = themeKeys.get(source);
|
|
545
|
+
if (animationKeys.has(source))
|
|
546
|
+
animations[source] = animationKeys.get(source);
|
|
547
|
+
if (staticKeys.has(source))
|
|
548
|
+
statics[source] = staticKeys.get(source);
|
|
549
|
+
if (!module)
|
|
550
|
+
continue;
|
|
551
|
+
modules[source] = {
|
|
552
|
+
source: `./${path.basename((0, exports.releasedPath)(source))}`,
|
|
553
|
+
target: (0, exports.releasedPath)(source),
|
|
554
|
+
binding: module.binding,
|
|
555
|
+
...(module.definitionOnly ? { definitionOnly: true } : {}),
|
|
556
|
+
...(Object.keys(module.aliases).length > 0
|
|
557
|
+
? { aliases: module.aliases }
|
|
558
|
+
: {}),
|
|
559
|
+
...(Object.keys(module.functions).length > 0
|
|
560
|
+
? { functions: module.functions }
|
|
561
|
+
: {}),
|
|
562
|
+
...(Object.keys(module.merges).length > 0
|
|
563
|
+
? { merges: module.merges }
|
|
564
|
+
: {}),
|
|
565
|
+
...(Object.keys(module.overrides).length > 0
|
|
566
|
+
? { overrides: module.overrides }
|
|
567
|
+
: {}),
|
|
568
|
+
};
|
|
251
569
|
}
|
|
252
570
|
return {
|
|
253
571
|
stylesheets,
|
|
@@ -255,10 +573,16 @@ function planRelease(targets) {
|
|
|
255
573
|
themes,
|
|
256
574
|
animations,
|
|
257
575
|
statics,
|
|
576
|
+
values,
|
|
577
|
+
complete: stylesheets.every((sheet) => sheet.reports.length === 0),
|
|
578
|
+
constants: Object.fromEntries([...resolvedConstants].map(([source, held]) => [
|
|
579
|
+
source,
|
|
580
|
+
Object.fromEntries(held),
|
|
581
|
+
])),
|
|
258
582
|
global: globalRules.length > 0
|
|
259
583
|
? {
|
|
260
584
|
target: globalPath(targets),
|
|
261
|
-
css:
|
|
585
|
+
css: `${GLOBAL_OPEN}\n${globalRules.join('\n\n')}\n${GLOBAL_CLOSE}\n`,
|
|
262
586
|
}
|
|
263
587
|
: undefined,
|
|
264
588
|
};
|
|
@@ -274,6 +598,13 @@ function writeRelease(plan) {
|
|
|
274
598
|
const previous = fs.existsSync(plan.global.target)
|
|
275
599
|
? fs.readFileSync(plan.global.target, 'utf8')
|
|
276
600
|
: '';
|
|
601
|
+
const opening = previous.indexOf(GLOBAL_OPEN);
|
|
602
|
+
const closing = previous.lastIndexOf(GLOBAL_CLOSE);
|
|
603
|
+
if (opening !== -1 && closing > opening) {
|
|
604
|
+
const kept = `${previous.slice(0, opening)}${previous.slice(closing + GLOBAL_CLOSE.length)}`;
|
|
605
|
+
fs.writeFileSync(plan.global.target, `${kept.replace(/\n+$/, '\n')}${plan.global.css}`, 'utf8');
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
277
608
|
const separator = previous.length > 0 && !previous.endsWith('\n') ? '\n\n' : '';
|
|
278
609
|
fs.writeFileSync(plan.global.target, `${previous}${separator}${plan.global.css}`, 'utf8');
|
|
279
610
|
}
|