@plumeria/codemod 18.2.18 → 18.2.20
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 +48 -9
- package/dist/cli.js +8 -3
- package/dist/migrate.js +3 -1
- package/dist/release.d.ts +3 -0
- package/dist/release.js +335 -53
- package/dist/resolve.d.ts +2 -0
- package/dist/resolve.js +104 -0
- package/dist/transforms/adopt-styles.d.ts +2 -0
- package/dist/transforms/adopt-styles.js +303 -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 +25 -1
- package/dist/transforms/from-plumeria.js +436 -51
- package/dist/transforms/release-styles.d.ts +8 -0
- package/dist/transforms/release-styles.js +582 -96
- package/package.json +2 -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,41 @@ 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
|
-
so the falsy branch drops out at runtime:
|
|
165
|
+
An array with no condition in it collapses into one class that `composes` its
|
|
166
|
+
members, so nothing is duplicated:
|
|
168
167
|
|
|
169
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:
|
|
175
|
+
|
|
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
|
+
|
|
174
200
|
### Global styles
|
|
175
201
|
|
|
176
202
|
`css.createTheme`, `css.keyframes`, and `css.viewTransition` produce rules that
|
|
@@ -195,12 +221,25 @@ src/Card.tsx
|
|
|
195
221
|
```
|
|
196
222
|
|
|
197
223
|
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
|
|
224
|
+
runtime function calls, and object spreads — are reported and left in place,
|
|
225
|
+
together with every file whose definitions they still need. A target
|
|
226
|
+
`*.module.css` that already exists is reported as `target-exists` and will not be
|
|
227
|
+
overwritten.
|
|
228
|
+
|
|
229
|
+
Several `css.create` calls in one file are not reported: they are written to the
|
|
230
|
+
same stylesheet, and a key an earlier call claimed is renamed.
|
|
201
231
|
|
|
202
232
|
The exit code is 1 while anything remains, so the command composes with a script.
|
|
203
233
|
|
|
234
|
+
The migration is idempotent — the first run converts, every run after it is a
|
|
235
|
+
fixed point:
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
f(f(x)) = f(x)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Clear a report and run again, and only the newly resolvable files move.
|
|
242
|
+
|
|
204
243
|
Start with `-d, --dry-run` to preview the export without writing files:
|
|
205
244
|
|
|
206
245
|
```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) {
|
|
334
|
+
function releaseConfig(modules, themes, animations, statics, values, complete) {
|
|
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, values, complete },
|
|
351
|
+
],
|
|
349
352
|
},
|
|
350
353
|
},
|
|
351
354
|
];
|
|
@@ -362,6 +365,8 @@ async function migrateFromPlumeria(options) {
|
|
|
362
365
|
return 0;
|
|
363
366
|
}
|
|
364
367
|
for (const sheet of planned.stylesheets) {
|
|
368
|
+
if (sheet.reports.length > 0)
|
|
369
|
+
continue;
|
|
365
370
|
console.log(` ${relative(sheet.source)} -> ${relative(sheet.target)}`);
|
|
366
371
|
}
|
|
367
372
|
if (planned.global) {
|
|
@@ -369,7 +374,7 @@ async function migrateFromPlumeria(options) {
|
|
|
369
374
|
}
|
|
370
375
|
const config = {
|
|
371
376
|
overrideConfigFile: true,
|
|
372
|
-
overrideConfig: releaseConfig(planned.modules, planned.themes, planned.animations),
|
|
377
|
+
overrideConfig: releaseConfig(planned.modules, planned.themes, planned.animations, planned.statics, planned.values, planned.complete),
|
|
373
378
|
errorOnUnmatchedPattern: false,
|
|
374
379
|
};
|
|
375
380
|
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
|
@@ -11,6 +11,9 @@ export interface ReleasePlan {
|
|
|
11
11
|
modules: Record<string, ReleaseModule>;
|
|
12
12
|
themes: Record<string, string[]>;
|
|
13
13
|
animations: Record<string, string[]>;
|
|
14
|
+
statics: Record<string, string[]>;
|
|
15
|
+
values: Record<string, Record<string, unknown>>;
|
|
16
|
+
complete: boolean;
|
|
14
17
|
global?: {
|
|
15
18
|
target: string;
|
|
16
19
|
css: string;
|
package/dist/release.js
CHANGED
|
@@ -40,7 +40,10 @@ 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");
|
|
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 */';
|
|
44
47
|
const EXTENSION = /\.(?:[cm]?[jt]sx?)$/;
|
|
45
48
|
const IGNORED = new Set([
|
|
46
49
|
'node_modules',
|
|
@@ -74,7 +77,45 @@ const findSources = (targets) => {
|
|
|
74
77
|
}
|
|
75
78
|
return found.sort();
|
|
76
79
|
};
|
|
77
|
-
const
|
|
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
|
+
};
|
|
118
|
+
const releasedPath = (source) => source.replace(/(?:\.styles)?\.[^.]+$/, '.module.css');
|
|
78
119
|
exports.releasedPath = releasedPath;
|
|
79
120
|
const globalPath = (targets) => {
|
|
80
121
|
const first = path.resolve(targets[0]);
|
|
@@ -93,19 +134,50 @@ function planRelease(targets) {
|
|
|
93
134
|
const modules = {};
|
|
94
135
|
const themes = {};
|
|
95
136
|
const animations = {};
|
|
96
|
-
const
|
|
137
|
+
const statics = {};
|
|
97
138
|
const sources = findSources(targets);
|
|
98
139
|
const sourceText = new Map(sources.map((source) => [source, fs.readFileSync(source, 'utf8')]));
|
|
99
140
|
const themeValues = new Map();
|
|
100
141
|
const animationValues = new Map();
|
|
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
|
+
};
|
|
166
|
+
for (const source of sources) {
|
|
167
|
+
try {
|
|
168
|
+
const extracted = (0, from_plumeria_1.extractPlumeriaStatics)(sourceText.get(source));
|
|
169
|
+
if (Object.keys(extracted.bindings).length > 0)
|
|
170
|
+
staticValues.set(source, extracted.bindings);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
}
|
|
174
|
+
}
|
|
101
175
|
for (const source of sources) {
|
|
102
176
|
try {
|
|
103
177
|
const extracted = (0, from_plumeria_1.extractPlumeriaThemes)(sourceText.get(source));
|
|
104
178
|
if (Object.keys(extracted.bindings).length === 0)
|
|
105
179
|
continue;
|
|
106
180
|
themeValues.set(source, extracted.bindings);
|
|
107
|
-
themes[source] = Object.keys(extracted.bindings);
|
|
108
|
-
globalRules.push(extracted.globalCss.trim());
|
|
109
181
|
}
|
|
110
182
|
catch {
|
|
111
183
|
}
|
|
@@ -121,11 +193,6 @@ function planRelease(targets) {
|
|
|
121
193
|
catch {
|
|
122
194
|
}
|
|
123
195
|
}
|
|
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
196
|
const importedValues = (source, availableBySource) => {
|
|
130
197
|
const result = {};
|
|
131
198
|
const ast = (0, parser_1.parse)(sourceText.get(source), {
|
|
@@ -134,106 +201,314 @@ function planRelease(targets) {
|
|
|
134
201
|
});
|
|
135
202
|
for (const node of ast.body) {
|
|
136
203
|
if (node.type !== 'ImportDeclaration' ||
|
|
137
|
-
typeof node.source.value !== 'string'
|
|
138
|
-
!node.source.value.startsWith('.'))
|
|
204
|
+
typeof node.source.value !== 'string')
|
|
139
205
|
continue;
|
|
140
|
-
const target =
|
|
206
|
+
const target = (0, resolve_1.resolveSourcePath)(node.source.value, source);
|
|
141
207
|
if (!target)
|
|
142
208
|
continue;
|
|
143
209
|
const available = availableBySource.get(target);
|
|
210
|
+
if (!available)
|
|
211
|
+
continue;
|
|
144
212
|
for (const specifier of node.specifiers) {
|
|
145
213
|
if (specifier.type === 'ImportSpecifier') {
|
|
146
214
|
const imported = specifier.imported.name ?? specifier.imported.value;
|
|
147
|
-
if (available[imported])
|
|
215
|
+
if (available[imported]) {
|
|
148
216
|
result[specifier.local.name] = available[imported];
|
|
217
|
+
dependencies.set(source, (dependencies.get(source) ?? new Set()).add(target));
|
|
218
|
+
}
|
|
149
219
|
}
|
|
150
220
|
else if (specifier.type === 'ImportNamespaceSpecifier') {
|
|
151
221
|
result[specifier.local.name] = available;
|
|
222
|
+
dependencies.set(source, (dependencies.get(source) ?? new Set()).add(target));
|
|
152
223
|
}
|
|
153
224
|
}
|
|
154
225
|
}
|
|
155
226
|
return result;
|
|
156
227
|
};
|
|
157
|
-
const importedThemes = (source) => importedValues(source, themeValues);
|
|
158
|
-
const importedAnimations = (source) => importedValues(source, animationValues);
|
|
159
228
|
const importedPlumeriaValues = (source) => {
|
|
160
|
-
const merged = {
|
|
161
|
-
for (const [
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
229
|
+
const merged = {};
|
|
230
|
+
for (const table of [staticValues, themeValues, animationValues]) {
|
|
231
|
+
for (const [key, value] of Object.entries(importedValues(source, table))) {
|
|
232
|
+
const previous = merged[key];
|
|
233
|
+
merged[key] =
|
|
234
|
+
previous &&
|
|
235
|
+
typeof previous === 'object' &&
|
|
236
|
+
value &&
|
|
237
|
+
typeof value === 'object'
|
|
238
|
+
? { ...previous, ...value }
|
|
239
|
+
: value;
|
|
240
|
+
}
|
|
170
241
|
}
|
|
171
242
|
return merged;
|
|
172
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
|
+
}
|
|
256
|
+
for (const source of sources) {
|
|
257
|
+
try {
|
|
258
|
+
const extracted = (0, from_plumeria_1.extractPlumeriaStatics)(sourceText.get(source), importedPlumeriaValues(source));
|
|
259
|
+
note(source, extracted.reports);
|
|
260
|
+
if (Object.keys(extracted.bindings).length === 0)
|
|
261
|
+
continue;
|
|
262
|
+
staticValues.set(source, extracted.bindings);
|
|
263
|
+
staticKeys.set(source, Object.keys(extracted.bindings));
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
}
|
|
267
|
+
}
|
|
173
268
|
for (const source of sources) {
|
|
174
269
|
try {
|
|
175
270
|
const extracted = (0, from_plumeria_1.extractPlumeriaAnimations)(sourceText.get(source), {
|
|
176
271
|
...importedPlumeriaValues(source),
|
|
177
272
|
...(themeValues.get(source) ?? {}),
|
|
178
273
|
});
|
|
274
|
+
note(source, extracted.reports);
|
|
179
275
|
if (Object.keys(extracted.bindings).length === 0)
|
|
180
276
|
continue;
|
|
181
277
|
animationValues.set(source, extracted.bindings);
|
|
182
|
-
|
|
278
|
+
animationKeys.set(source, [
|
|
183
279
|
...extracted.keyframes,
|
|
184
280
|
...extracted.viewTransitions,
|
|
185
|
-
];
|
|
186
|
-
|
|
281
|
+
]);
|
|
282
|
+
addGlobal(source, extracted.globalCss);
|
|
187
283
|
}
|
|
188
284
|
catch {
|
|
189
285
|
}
|
|
190
286
|
}
|
|
287
|
+
const createBindings = new Map();
|
|
191
288
|
for (const source of sources) {
|
|
192
|
-
let converted;
|
|
193
289
|
try {
|
|
194
|
-
|
|
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 compositions = new Map();
|
|
314
|
+
for (const source of sources) {
|
|
315
|
+
let ast;
|
|
316
|
+
try {
|
|
317
|
+
ast = (0, parser_1.parse)(sourceText.get(source), {
|
|
318
|
+
sourceType: 'module',
|
|
319
|
+
ecmaFeatures: { jsx: true },
|
|
320
|
+
loc: true,
|
|
321
|
+
range: true,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
const owners = new Map();
|
|
328
|
+
for (const binding of createBindings.get(source) ?? [])
|
|
329
|
+
owners.set(binding, { source, binding });
|
|
330
|
+
for (const node of ast.body) {
|
|
331
|
+
if (node.type !== 'ImportDeclaration')
|
|
332
|
+
continue;
|
|
333
|
+
const target = (0, resolve_1.resolveSourcePath)(String(node.source.value), source);
|
|
334
|
+
const declared = target ? createBindings.get(target) : undefined;
|
|
335
|
+
if (!target || !declared)
|
|
336
|
+
continue;
|
|
337
|
+
for (const specifier of node.specifiers) {
|
|
338
|
+
if (specifier.type !== 'ImportSpecifier')
|
|
339
|
+
continue;
|
|
340
|
+
const imported = specifier.imported.name ?? specifier.imported.value;
|
|
341
|
+
if (declared.has(imported))
|
|
342
|
+
owners.set(specifier.local.name, {
|
|
343
|
+
source: target,
|
|
344
|
+
binding: imported,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
walk(ast.body, (node) => {
|
|
349
|
+
if (node.type !== 'MemberExpression' ||
|
|
350
|
+
!node.computed ||
|
|
351
|
+
node.property.type === 'Literal' ||
|
|
352
|
+
node.object.type !== 'Identifier')
|
|
353
|
+
return;
|
|
354
|
+
const owner = owners.get(node.object.name);
|
|
355
|
+
if (!owner)
|
|
356
|
+
return;
|
|
357
|
+
note(owner.source, [
|
|
358
|
+
{
|
|
359
|
+
line: node.loc.start.line,
|
|
360
|
+
column: node.loc.start.column + 1,
|
|
361
|
+
kind: 'dynamic-style-access',
|
|
362
|
+
hint: `\`${node.object.name}\` is read with a computed key, which has no class name until it runs.`,
|
|
363
|
+
},
|
|
364
|
+
]);
|
|
365
|
+
});
|
|
366
|
+
walk(ast.body, (node) => {
|
|
367
|
+
if (node.type !== 'JSXAttribute' ||
|
|
368
|
+
node.name?.name !== STYLE_PROP ||
|
|
369
|
+
node.value?.type !== 'JSXExpressionContainer')
|
|
370
|
+
return;
|
|
371
|
+
const expression = node.value.expression;
|
|
372
|
+
if (expression?.type !== 'ArrayExpression' ||
|
|
373
|
+
expression.elements.length < 2)
|
|
374
|
+
return;
|
|
375
|
+
const slots = expression.elements.map(slotOf);
|
|
376
|
+
if (slots.some((slot) => slot.length === 0))
|
|
377
|
+
return;
|
|
378
|
+
const owners_ = slots.flat().map((part) => owners.get(part.object));
|
|
379
|
+
if (owners_.some((candidate) => !candidate))
|
|
380
|
+
return;
|
|
381
|
+
const owner = owners_[0].source;
|
|
382
|
+
if (owners_.some((candidate) => candidate.source !== owner))
|
|
383
|
+
return;
|
|
384
|
+
const named = slots.map((slot) => slot.map((part) => ({
|
|
385
|
+
binding: owners.get(part.object).binding,
|
|
386
|
+
key: part.key,
|
|
387
|
+
})));
|
|
388
|
+
const collapsible = expression.elements.every((element) => memberOf(element)) &&
|
|
389
|
+
named.every((slot) => slot.length === 1);
|
|
390
|
+
compositions.set(owner, [
|
|
391
|
+
...(compositions.get(owner) ?? []),
|
|
392
|
+
{
|
|
393
|
+
parts: collapsible ? named.map((slot) => slot[0]) : [],
|
|
394
|
+
slots: named,
|
|
395
|
+
file: source,
|
|
396
|
+
line: node.loc.start.line,
|
|
397
|
+
column: node.loc.start.column + 1,
|
|
398
|
+
},
|
|
399
|
+
]);
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
const converted = new Map();
|
|
403
|
+
for (const source of sources) {
|
|
404
|
+
let module;
|
|
405
|
+
try {
|
|
406
|
+
module = (0, from_plumeria_1.convertPlumeriaModule)(sourceText.get(source), importedPlumeriaValues(source), compositions.get(source) ?? []);
|
|
195
407
|
}
|
|
196
408
|
catch {
|
|
197
409
|
continue;
|
|
198
410
|
}
|
|
199
|
-
if (!
|
|
411
|
+
if (!module)
|
|
200
412
|
continue;
|
|
201
413
|
const target = (0, exports.releasedPath)(source);
|
|
202
|
-
|
|
203
|
-
|
|
414
|
+
converted.set(source, module);
|
|
415
|
+
note(source, module.reports);
|
|
204
416
|
if (fs.existsSync(target)) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
417
|
+
note(source, [
|
|
418
|
+
{
|
|
419
|
+
line: 0,
|
|
420
|
+
column: 0,
|
|
421
|
+
kind: 'target-exists',
|
|
422
|
+
hint: `${path.basename(target)} already exists and was not overwritten.`,
|
|
423
|
+
},
|
|
424
|
+
]);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
const blocked = new Set(reportsBySource.keys());
|
|
428
|
+
for (let changed = true; changed;) {
|
|
429
|
+
changed = false;
|
|
430
|
+
for (const source of blocked) {
|
|
431
|
+
for (const dependency of dependencies.get(source) ?? []) {
|
|
432
|
+
if (blocked.has(dependency))
|
|
433
|
+
continue;
|
|
434
|
+
blocked.add(dependency);
|
|
435
|
+
note(dependency, [
|
|
436
|
+
{
|
|
437
|
+
line: 0,
|
|
438
|
+
column: 0,
|
|
439
|
+
kind: 'blocked-dependency',
|
|
440
|
+
hint: `${path.relative(path.dirname(dependency), source)} still needs these definitions.`,
|
|
441
|
+
},
|
|
442
|
+
]);
|
|
443
|
+
changed = true;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const globalRules = [];
|
|
448
|
+
const values = {};
|
|
449
|
+
for (const source of sources) {
|
|
450
|
+
const reports = reportsBySource.get(source) ?? [];
|
|
451
|
+
const module = converted.get(source);
|
|
452
|
+
if (module) {
|
|
453
|
+
stylesheets.push({
|
|
454
|
+
source,
|
|
455
|
+
target: (0, exports.releasedPath)(source),
|
|
456
|
+
css: module.css,
|
|
457
|
+
reports,
|
|
210
458
|
});
|
|
211
459
|
}
|
|
212
|
-
|
|
213
|
-
source,
|
|
214
|
-
target,
|
|
215
|
-
css: converted.css,
|
|
216
|
-
reports,
|
|
217
|
-
});
|
|
218
|
-
if (reports.length === 0) {
|
|
219
|
-
modules[source] = {
|
|
220
|
-
source: specifier,
|
|
221
|
-
binding: converted.binding,
|
|
222
|
-
...(Object.keys(converted.functions).length > 0
|
|
223
|
-
? { functions: converted.functions }
|
|
224
|
-
: {}),
|
|
225
|
-
};
|
|
460
|
+
else if (reports.length > 0) {
|
|
461
|
+
stylesheets.push({ source, target: '', css: '', reports });
|
|
226
462
|
}
|
|
463
|
+
if (blocked.has(source))
|
|
464
|
+
continue;
|
|
465
|
+
const resolved = {
|
|
466
|
+
...(staticValues.get(source) ?? {}),
|
|
467
|
+
...(themeValues.get(source) ?? {}),
|
|
468
|
+
...(animationValues.get(source) ?? {}),
|
|
469
|
+
};
|
|
470
|
+
if (Object.keys(resolved).length > 0)
|
|
471
|
+
values[source] = resolved;
|
|
472
|
+
globalRules.push(...(globalBySource.get(source) ?? []));
|
|
473
|
+
if (themeKeys.has(source))
|
|
474
|
+
themes[source] = themeKeys.get(source);
|
|
475
|
+
if (animationKeys.has(source))
|
|
476
|
+
animations[source] = animationKeys.get(source);
|
|
477
|
+
if (staticKeys.has(source))
|
|
478
|
+
statics[source] = staticKeys.get(source);
|
|
479
|
+
if (!module)
|
|
480
|
+
continue;
|
|
481
|
+
modules[source] = {
|
|
482
|
+
source: `./${path.basename((0, exports.releasedPath)(source))}`,
|
|
483
|
+
target: (0, exports.releasedPath)(source),
|
|
484
|
+
binding: module.binding,
|
|
485
|
+
...(module.definitionOnly ? { definitionOnly: true } : {}),
|
|
486
|
+
...(Object.keys(module.aliases).length > 0
|
|
487
|
+
? { aliases: module.aliases }
|
|
488
|
+
: {}),
|
|
489
|
+
...(Object.keys(module.functions).length > 0
|
|
490
|
+
? { functions: module.functions }
|
|
491
|
+
: {}),
|
|
492
|
+
...(Object.keys(module.merges).length > 0
|
|
493
|
+
? { merges: module.merges }
|
|
494
|
+
: {}),
|
|
495
|
+
...(Object.keys(module.overrides).length > 0
|
|
496
|
+
? { overrides: module.overrides }
|
|
497
|
+
: {}),
|
|
498
|
+
};
|
|
227
499
|
}
|
|
228
500
|
return {
|
|
229
501
|
stylesheets,
|
|
230
502
|
modules,
|
|
231
503
|
themes,
|
|
232
504
|
animations,
|
|
505
|
+
statics,
|
|
506
|
+
values,
|
|
507
|
+
complete: stylesheets.every((sheet) => sheet.reports.length === 0),
|
|
233
508
|
global: globalRules.length > 0
|
|
234
509
|
? {
|
|
235
510
|
target: globalPath(targets),
|
|
236
|
-
css:
|
|
511
|
+
css: `${GLOBAL_OPEN}\n${globalRules.join('\n\n')}\n${GLOBAL_CLOSE}\n`,
|
|
237
512
|
}
|
|
238
513
|
: undefined,
|
|
239
514
|
};
|
|
@@ -249,6 +524,13 @@ function writeRelease(plan) {
|
|
|
249
524
|
const previous = fs.existsSync(plan.global.target)
|
|
250
525
|
? fs.readFileSync(plan.global.target, 'utf8')
|
|
251
526
|
: '';
|
|
527
|
+
const opening = previous.indexOf(GLOBAL_OPEN);
|
|
528
|
+
const closing = previous.lastIndexOf(GLOBAL_CLOSE);
|
|
529
|
+
if (opening !== -1 && closing > opening) {
|
|
530
|
+
const kept = `${previous.slice(0, opening)}${previous.slice(closing + GLOBAL_CLOSE.length)}`;
|
|
531
|
+
fs.writeFileSync(plan.global.target, `${kept.replace(/\n+$/, '\n')}${plan.global.css}`, 'utf8');
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
252
534
|
const separator = previous.length > 0 && !previous.endsWith('\n') ? '\n\n' : '';
|
|
253
535
|
fs.writeFileSync(plan.global.target, `${previous}${separator}${plan.global.css}`, 'utf8');
|
|
254
536
|
}
|