@plumeria/codemod 18.2.21 → 18.2.22
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 +4 -2
- package/dist/release.js +53 -3
- package/dist/transforms/adopt-styles.js +28 -10
- package/dist/transforms/from-plumeria.d.ts +22 -2
- package/dist/transforms/from-plumeria.js +237 -46
- package/dist/transforms/release-styles.d.ts +1 -0
- package/dist/transforms/release-styles.js +13 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -194,8 +194,10 @@ under the same condition as the style that has to win:
|
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
Order is only forced where the two classes actually disagree, measured per
|
|
197
|
-
at-rule and per selector.
|
|
198
|
-
`
|
|
197
|
+
at-rule and per selector. Where one property covers another — `padding` against
|
|
198
|
+
`padding-top` — Plumeria settles it by specificity rather than order, so the
|
|
199
|
+
shorthand is written first and the array has no say. Anything left unsettled is
|
|
200
|
+
reported as `composition-order`.
|
|
199
201
|
|
|
200
202
|
A variant picked with a bracket is resolved to the class its key names, whether
|
|
201
203
|
the constant is declared in the same file or imported, and a constant left
|
package/dist/release.js
CHANGED
|
@@ -326,7 +326,11 @@ function planRelease(targets) {
|
|
|
326
326
|
const held = new Map();
|
|
327
327
|
const exported = new Map();
|
|
328
328
|
const duplicated = new Set();
|
|
329
|
+
const seen = new Set();
|
|
329
330
|
const read = (statement, isExport) => {
|
|
331
|
+
if (seen.has(statement))
|
|
332
|
+
return;
|
|
333
|
+
seen.add(statement);
|
|
330
334
|
if (statement?.type !== 'VariableDeclaration' ||
|
|
331
335
|
statement.kind !== 'const')
|
|
332
336
|
return;
|
|
@@ -359,6 +363,8 @@ function planRelease(targets) {
|
|
|
359
363
|
}
|
|
360
364
|
const resolvedConstants = new Map();
|
|
361
365
|
const compositions = new Map();
|
|
366
|
+
const foldings = new Map();
|
|
367
|
+
const reached = new Map();
|
|
362
368
|
for (const source of sources) {
|
|
363
369
|
let ast;
|
|
364
370
|
try {
|
|
@@ -449,14 +455,43 @@ function planRelease(targets) {
|
|
|
449
455
|
if (owners_.some((candidate) => !candidate))
|
|
450
456
|
return;
|
|
451
457
|
const owner = owners_[0].source;
|
|
452
|
-
if (owners_.some((candidate) => candidate.source !== owner))
|
|
453
|
-
return;
|
|
454
458
|
const named = slots.map((slot) => slot.map((part) => ({
|
|
455
459
|
binding: owners.get(part.object).binding,
|
|
456
460
|
key: part.key,
|
|
457
461
|
})));
|
|
458
462
|
const collapsible = expression.elements.every((element) => memberOf(element)) &&
|
|
459
463
|
named.every((slot) => slot.length === 1);
|
|
464
|
+
if (owners_.some((candidate) => candidate.source !== owner)) {
|
|
465
|
+
if (!collapsible)
|
|
466
|
+
return;
|
|
467
|
+
const parts = slots.map((slot, index) => {
|
|
468
|
+
const held = owners.get(slot[0].object);
|
|
469
|
+
return {
|
|
470
|
+
binding: held.binding,
|
|
471
|
+
key: slot[0].key,
|
|
472
|
+
...(held.source === owner ? {} : { source: held.source }),
|
|
473
|
+
target: (0, exports.releasedPath)(held.source),
|
|
474
|
+
at: index,
|
|
475
|
+
};
|
|
476
|
+
});
|
|
477
|
+
foldings.set(owner, [
|
|
478
|
+
...(foldings.get(owner) ?? []),
|
|
479
|
+
{
|
|
480
|
+
signature: parts
|
|
481
|
+
.map((part) => `${part.target}#${part.key}`)
|
|
482
|
+
.join('|'),
|
|
483
|
+
parts: parts.map(({ binding, key, source }) => ({
|
|
484
|
+
binding,
|
|
485
|
+
key,
|
|
486
|
+
...(source ? { source } : {}),
|
|
487
|
+
})),
|
|
488
|
+
},
|
|
489
|
+
]);
|
|
490
|
+
for (const part of parts)
|
|
491
|
+
if (part.source)
|
|
492
|
+
reached.set(owner, (reached.get(owner) ?? new Set()).add(part.source));
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
460
495
|
compositions.set(owner, [
|
|
461
496
|
...(compositions.get(owner) ?? []),
|
|
462
497
|
{
|
|
@@ -473,7 +508,22 @@ function planRelease(targets) {
|
|
|
473
508
|
for (const source of sources) {
|
|
474
509
|
let module;
|
|
475
510
|
try {
|
|
476
|
-
module = (0, from_plumeria_1.convertPlumeriaModule)(sourceText.get(source), importedPlumeriaValues(source), compositions.get(source) ?? []
|
|
511
|
+
module = (0, from_plumeria_1.convertPlumeriaModule)(sourceText.get(source), importedPlumeriaValues(source), compositions.get(source) ?? [], {
|
|
512
|
+
target: (0, exports.releasedPath)(source),
|
|
513
|
+
foldings: foldings.get(source) ?? [],
|
|
514
|
+
foreign: Object.fromEntries([...(reached.get(source) ?? [])].map((other) => [
|
|
515
|
+
other,
|
|
516
|
+
{
|
|
517
|
+
text: sourceText.get(other),
|
|
518
|
+
values: {
|
|
519
|
+
...importedPlumeriaValues(other),
|
|
520
|
+
...(staticValues.get(other) ?? {}),
|
|
521
|
+
...(themeValues.get(other) ?? {}),
|
|
522
|
+
...(animationValues.get(other) ?? {}),
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
])),
|
|
526
|
+
});
|
|
477
527
|
}
|
|
478
528
|
catch {
|
|
479
529
|
continue;
|
|
@@ -80,6 +80,13 @@ exports.adoptStyles = {
|
|
|
80
80
|
let coreLocal;
|
|
81
81
|
let readsValue = false;
|
|
82
82
|
let several = false;
|
|
83
|
+
const moduleFor = (specifier) => {
|
|
84
|
+
const resolved = (0, resolve_1.resolveSourcePath)(specifier, context.filename);
|
|
85
|
+
if (resolved && modules[resolved])
|
|
86
|
+
return modules[resolved];
|
|
87
|
+
const named = Object.entries(modules).filter(([key]) => key === specifier || path.basename(key) === path.basename(specifier));
|
|
88
|
+
return named.length === 1 ? named[0][1] : undefined;
|
|
89
|
+
};
|
|
83
90
|
const stylingPosition = (node) => {
|
|
84
91
|
let current = node;
|
|
85
92
|
while (current.parent &&
|
|
@@ -171,8 +178,7 @@ exports.adoptStyles = {
|
|
|
171
178
|
coreLocal = specifier.local.name;
|
|
172
179
|
continue;
|
|
173
180
|
}
|
|
174
|
-
|
|
175
|
-
if (!target || !modules[target])
|
|
181
|
+
if (!moduleFor(String(statement.source.value)))
|
|
176
182
|
continue;
|
|
177
183
|
for (const specifier of statement.specifiers)
|
|
178
184
|
if (specifier.type === 'ImportDefaultSpecifier') {
|
|
@@ -229,14 +235,12 @@ exports.adoptStyles = {
|
|
|
229
235
|
},
|
|
230
236
|
ImportDeclaration(node) {
|
|
231
237
|
const specifier = String(node.source.value);
|
|
232
|
-
const
|
|
233
|
-
const named = Object.entries(modules).filter(([key]) => key === specifier ||
|
|
234
|
-
path.basename(key) === path.basename(specifier));
|
|
235
|
-
const entry = (resolved ? modules[resolved] : undefined) ??
|
|
236
|
-
(named.length === 1 ? named[0][1] : undefined);
|
|
238
|
+
const entry = moduleFor(specifier);
|
|
237
239
|
if (!entry) {
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
+
const resolved = (0, resolve_1.resolveSourcePath)(specifier, context.filename);
|
|
241
|
+
const adopted = Object.values(modules).find((candidate) => candidate.target === resolved ||
|
|
242
|
+
candidate.source === specifier ||
|
|
243
|
+
path.basename(candidate.source) === path.basename(specifier));
|
|
240
244
|
if (!adopted)
|
|
241
245
|
return;
|
|
242
246
|
const already = node.specifiers.find((specifier) => specifier.type === 'ImportSpecifier' &&
|
|
@@ -402,7 +406,21 @@ exports.adoptStyles = {
|
|
|
402
406
|
if (joined) {
|
|
403
407
|
if (!joined.some((element) => bindings.has(styleRead(element)?.object?.name)))
|
|
404
408
|
return;
|
|
405
|
-
const
|
|
409
|
+
const styles_ = joined.filter((element) => bindings.has(styleRead(element)?.object?.name));
|
|
410
|
+
const carried = joined.filter((element) => !styles_.includes(element));
|
|
411
|
+
const list = styles_.map(renamedText).join(', ');
|
|
412
|
+
if (carried.length > 0) {
|
|
413
|
+
const kept = carried
|
|
414
|
+
.map((element) => context.sourceCode.getText(element))
|
|
415
|
+
.join(', ');
|
|
416
|
+
context.report({
|
|
417
|
+
node,
|
|
418
|
+
messageId: 'prop',
|
|
419
|
+
data: { prop: styleProp },
|
|
420
|
+
fix: (fixer) => fixer.replaceText(node, `className={[${kept}].filter(Boolean).join(' ')} ${styleProp}={[${list}]}`),
|
|
421
|
+
});
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
406
424
|
if (!intrinsic(node.parent) && coreLocal) {
|
|
407
425
|
context.report({
|
|
408
426
|
node: expression,
|
|
@@ -12,6 +12,7 @@ export interface PlumeriaModule {
|
|
|
12
12
|
functions: Record<string, {
|
|
13
13
|
params: string[];
|
|
14
14
|
variables: string[];
|
|
15
|
+
lengths: boolean[];
|
|
15
16
|
}>;
|
|
16
17
|
aliases: Record<string, Record<string, string>>;
|
|
17
18
|
merges: Record<string, string>;
|
|
@@ -32,7 +33,22 @@ export interface PlumeriaComposition {
|
|
|
32
33
|
line: number;
|
|
33
34
|
column: number;
|
|
34
35
|
}
|
|
35
|
-
export
|
|
36
|
+
export interface PlumeriaFolding {
|
|
37
|
+
signature: string;
|
|
38
|
+
parts: {
|
|
39
|
+
binding: string;
|
|
40
|
+
key: string;
|
|
41
|
+
source?: string;
|
|
42
|
+
}[];
|
|
43
|
+
}
|
|
44
|
+
export interface PlumeriaForeign {
|
|
45
|
+
text: string;
|
|
46
|
+
values: Record<string, unknown>;
|
|
47
|
+
}
|
|
48
|
+
export declare const compositionSignature: (parts: {
|
|
49
|
+
target: string;
|
|
50
|
+
className: string;
|
|
51
|
+
}[]) => string;
|
|
36
52
|
export interface ThemeExtraction {
|
|
37
53
|
bindings: Record<string, Record<string, string>>;
|
|
38
54
|
globalCss: string;
|
|
@@ -58,4 +74,8 @@ export declare const __private: {
|
|
|
58
74
|
export declare function extractPlumeriaThemes(source: string): ThemeExtraction;
|
|
59
75
|
export declare function extractPlumeriaStatics(source: string, externalValues?: Record<string, unknown>): StaticExtraction;
|
|
60
76
|
export declare function extractPlumeriaAnimations(source: string, externalValues?: Record<string, unknown>): AnimationExtraction;
|
|
61
|
-
export declare function convertPlumeriaModule(source: string, externalValues?: Record<string, unknown>, compositions?: PlumeriaComposition[]
|
|
77
|
+
export declare function convertPlumeriaModule(source: string, externalValues?: Record<string, unknown>, compositions?: PlumeriaComposition[], options?: {
|
|
78
|
+
target?: string;
|
|
79
|
+
foldings?: PlumeriaFolding[];
|
|
80
|
+
foreign?: Record<string, PlumeriaForeign>;
|
|
81
|
+
}): PlumeriaModule | null;
|
|
@@ -7,8 +7,87 @@ exports.extractPlumeriaAnimations = extractPlumeriaAnimations;
|
|
|
7
7
|
exports.convertPlumeriaModule = convertPlumeriaModule;
|
|
8
8
|
const parser_1 = require("@typescript-eslint/parser");
|
|
9
9
|
const zss_engine_1 = require("zss-engine");
|
|
10
|
-
const
|
|
10
|
+
const UNITLESS = new Set(zss_engine_1.exceptionCamelCase.map((property) => (0, zss_engine_1.camelToKebabCase)(property)));
|
|
11
|
+
const compositionSignature = (parts) => parts.map((part) => `${part.target}#${part.className}`).join('|');
|
|
11
12
|
exports.compositionSignature = compositionSignature;
|
|
13
|
+
const own = (target, classNames) => (0, exports.compositionSignature)(classNames.map((className) => ({ target, className })));
|
|
14
|
+
const DIRECT_SHORTHANDS = {};
|
|
15
|
+
for (const [shorthand, longhands] of Object.entries(zss_engine_1.DIRECT_LONGHANDS)) {
|
|
16
|
+
for (const longhand of longhands) {
|
|
17
|
+
if (!DIRECT_SHORTHANDS[longhand])
|
|
18
|
+
DIRECT_SHORTHANDS[longhand] = [];
|
|
19
|
+
DIRECT_SHORTHANDS[longhand].push(shorthand);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const depths = new Map();
|
|
23
|
+
const depthOf = (property) => {
|
|
24
|
+
const cached = depths.get(property);
|
|
25
|
+
if (cached !== undefined)
|
|
26
|
+
return cached;
|
|
27
|
+
depths.set(property, 0);
|
|
28
|
+
let depth = 0;
|
|
29
|
+
for (const shorthand of DIRECT_SHORTHANDS[property] || [])
|
|
30
|
+
depth = Math.max(depth, depthOf(shorthand) + 1);
|
|
31
|
+
depths.set(property, depth);
|
|
32
|
+
return depth;
|
|
33
|
+
};
|
|
34
|
+
const coverages = new Map();
|
|
35
|
+
const coverageOf = (property) => {
|
|
36
|
+
const cached = coverages.get(property);
|
|
37
|
+
if (cached)
|
|
38
|
+
return cached;
|
|
39
|
+
const longhands = zss_engine_1.DIRECT_LONGHANDS[property];
|
|
40
|
+
const coverage = longhands
|
|
41
|
+
? new Set(longhands.flatMap((longhand) => [...coverageOf(longhand)]))
|
|
42
|
+
: new Set([property]);
|
|
43
|
+
coverages.set(property, coverage);
|
|
44
|
+
return coverage;
|
|
45
|
+
};
|
|
46
|
+
const covers = (outer, inner) => {
|
|
47
|
+
if (outer === inner)
|
|
48
|
+
return false;
|
|
49
|
+
const wide = coverageOf(outer);
|
|
50
|
+
const narrow = coverageOf(inner);
|
|
51
|
+
return wide.size > narrow.size && [...narrow].every((leaf) => wide.has(leaf));
|
|
52
|
+
};
|
|
53
|
+
const overlaps = (first, second) => {
|
|
54
|
+
const left = coverageOf(first);
|
|
55
|
+
return [...coverageOf(second)].some((leaf) => left.has(leaf));
|
|
56
|
+
};
|
|
57
|
+
const byRank = (written) => {
|
|
58
|
+
const outranked = (later, earlier) => (later.held ?? []).some((mine) => (earlier.held ?? []).some((theirs) => later.suffix === earlier.suffix &&
|
|
59
|
+
overlaps(mine.property, theirs.property) &&
|
|
60
|
+
mine.rank > theirs.rank));
|
|
61
|
+
const ordered = [];
|
|
62
|
+
const pending = [...written];
|
|
63
|
+
while (pending.length > 0) {
|
|
64
|
+
const index = pending.findIndex((candidate, position) => !pending.some((other, held) => held !== position && outranked(candidate, other)));
|
|
65
|
+
ordered.push(...pending.splice(index === -1 ? 0 : index, 1));
|
|
66
|
+
}
|
|
67
|
+
return ordered;
|
|
68
|
+
};
|
|
69
|
+
const byImplication = (written) => {
|
|
70
|
+
const ordered = [];
|
|
71
|
+
const pending = [...written];
|
|
72
|
+
while (pending.length > 0) {
|
|
73
|
+
const index = pending.findIndex((candidate, position) => !pending.some((other, held) => held !== position &&
|
|
74
|
+
candidate.condition !== undefined &&
|
|
75
|
+
other.condition !== undefined &&
|
|
76
|
+
candidate.condition !== other.condition &&
|
|
77
|
+
(0, zss_engine_1.impliesCondition)(candidate.condition, other.condition)));
|
|
78
|
+
ordered.push(...pending.splice(index === -1 ? 0 : index, 1));
|
|
79
|
+
}
|
|
80
|
+
return ordered.map((entry) => entry.text);
|
|
81
|
+
};
|
|
82
|
+
const byContainment = (declared) => {
|
|
83
|
+
const ordered = [];
|
|
84
|
+
const pending = [...declared];
|
|
85
|
+
while (pending.length > 0) {
|
|
86
|
+
const index = pending.findIndex((candidate, position) => !pending.some((other, held) => held !== position && covers(other.property, candidate.property)));
|
|
87
|
+
ordered.push(...pending.splice(index === -1 ? 0 : index, 1));
|
|
88
|
+
}
|
|
89
|
+
return ordered.map((entry) => entry.text);
|
|
90
|
+
};
|
|
12
91
|
const toIdent = (value) => value.replace(/[^A-Za-z0-9-]/g, '');
|
|
13
92
|
const capitalize = (value) => `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
|
|
14
93
|
const camelJoin = (names) => names
|
|
@@ -366,7 +445,8 @@ function extractPlumeriaAnimations(source, externalValues = {}) {
|
|
|
366
445
|
reports,
|
|
367
446
|
};
|
|
368
447
|
}
|
|
369
|
-
function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
448
|
+
function convertPlumeriaModule(source, externalValues = {}, compositions = [], options = {}) {
|
|
449
|
+
const target = options.target ?? '';
|
|
370
450
|
const ast = (0, parser_1.parse)(source, {
|
|
371
451
|
sourceType: 'module',
|
|
372
452
|
ecmaFeatures: { jsx: true },
|
|
@@ -435,27 +515,37 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
435
515
|
const members = new Map();
|
|
436
516
|
const footprints = new Map();
|
|
437
517
|
const track = (className) => {
|
|
438
|
-
const
|
|
439
|
-
footprints.set(className,
|
|
440
|
-
return { root: `.${className}`,
|
|
518
|
+
const held = [];
|
|
519
|
+
footprints.set(className, held);
|
|
520
|
+
return { root: `.${className}`, held };
|
|
441
521
|
};
|
|
442
|
-
const
|
|
522
|
+
const relate = (first, second) => {
|
|
443
523
|
const left = footprints.get(first);
|
|
444
524
|
const right = footprints.get(second);
|
|
445
525
|
if (!left || !right)
|
|
446
|
-
return true;
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
526
|
+
return { crossing: true };
|
|
527
|
+
let ranked;
|
|
528
|
+
let crossing = false;
|
|
529
|
+
for (const mine of left) {
|
|
530
|
+
for (const theirs of right) {
|
|
531
|
+
if (mine.suffix !== theirs.suffix)
|
|
532
|
+
continue;
|
|
533
|
+
if (!overlaps(mine.property, theirs.property))
|
|
534
|
+
continue;
|
|
535
|
+
if (mine.rank > theirs.rank)
|
|
536
|
+
ranked ??= [second, first];
|
|
537
|
+
else if (theirs.rank > mine.rank)
|
|
538
|
+
ranked ??= [first, second];
|
|
539
|
+
else
|
|
540
|
+
crossing = true;
|
|
541
|
+
}
|
|
454
542
|
}
|
|
455
|
-
return
|
|
543
|
+
return { ranked, crossing };
|
|
456
544
|
};
|
|
545
|
+
const crosses = (first, second) => relate(first, second).crossing;
|
|
457
546
|
const taken = new Set();
|
|
458
547
|
const functions = {};
|
|
548
|
+
const variableUses = new Map();
|
|
459
549
|
const classNameFor = (owner, key) => {
|
|
460
550
|
if (!taken.has(key))
|
|
461
551
|
return key;
|
|
@@ -477,7 +567,7 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
477
567
|
return;
|
|
478
568
|
}
|
|
479
569
|
const restrictRoot = restrict ? selectorRoot(selector) : 0;
|
|
480
|
-
const
|
|
570
|
+
const declared = [];
|
|
481
571
|
const nested = [];
|
|
482
572
|
const markers = [];
|
|
483
573
|
for (const property of style.properties) {
|
|
@@ -519,15 +609,24 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
519
609
|
.get(bucketOf(wrappers, selector.slice(restrictRoot)))
|
|
520
610
|
?.has(cssProperty))
|
|
521
611
|
continue;
|
|
612
|
+
if (typeof value === 'string')
|
|
613
|
+
for (const [, variable] of value.matchAll(/var\((--[\w-]+)\)/g))
|
|
614
|
+
variableUses.set(variable, (variableUses.get(variable) ?? new Set()).add(cssProperty));
|
|
522
615
|
if (footprint) {
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
616
|
+
const suffix = selector.slice(footprint.root.length);
|
|
617
|
+
footprint.held.push({
|
|
618
|
+
bucket: bucketOf(wrappers, suffix),
|
|
619
|
+
suffix,
|
|
620
|
+
property: cssProperty,
|
|
621
|
+
rank: depthOf(cssProperty) + (wrappers.length > 0 ? 1 : 0),
|
|
622
|
+
});
|
|
527
623
|
}
|
|
528
|
-
|
|
624
|
+
declared.push({
|
|
625
|
+
property: cssProperty,
|
|
626
|
+
text: ` ${cssProperty}: ${(0, zss_engine_1.applyCssValue)(value, cssProperty)};`,
|
|
627
|
+
});
|
|
529
628
|
}
|
|
530
|
-
const pushRule = (target, lines) => {
|
|
629
|
+
const pushRule = (target, lines, held = []) => {
|
|
531
630
|
if (lines.length === 0)
|
|
532
631
|
return;
|
|
533
632
|
let rule = `${target} {\n${lines.join('\n')}\n}`;
|
|
@@ -537,10 +636,18 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
537
636
|
.map((line) => ` ${line}`)
|
|
538
637
|
.join('\n')}\n}`;
|
|
539
638
|
}
|
|
540
|
-
rules.push(
|
|
639
|
+
rules.push({
|
|
640
|
+
text: rule,
|
|
641
|
+
condition: wrappers.length === 1 ? wrappers[0] : undefined,
|
|
642
|
+
suffix: target.slice(target.indexOf('.') + 1).replace(/^[\w-]+/, ''),
|
|
643
|
+
held,
|
|
644
|
+
});
|
|
541
645
|
};
|
|
542
646
|
if (phase !== 'nested')
|
|
543
|
-
pushRule(selector,
|
|
647
|
+
pushRule(selector, byContainment(declared), declared.map((entry) => ({
|
|
648
|
+
property: entry.property,
|
|
649
|
+
rank: depthOf(entry.property) + (wrappers.length > 0 ? 1 : 0),
|
|
650
|
+
})));
|
|
544
651
|
if (phase === 'declarations')
|
|
545
652
|
return;
|
|
546
653
|
for (const marker of markers) {
|
|
@@ -600,10 +707,19 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
600
707
|
params.forEach((parameter, index) => {
|
|
601
708
|
scope.set(parameter, `var(${variables[index]})`);
|
|
602
709
|
});
|
|
603
|
-
functions[className] = { params, variables };
|
|
604
710
|
keys.push(className);
|
|
605
711
|
openGroup(className);
|
|
606
712
|
emit(body, `.${className}`, [], scope, 'both', track(className));
|
|
713
|
+
functions[className] = {
|
|
714
|
+
params,
|
|
715
|
+
variables,
|
|
716
|
+
lengths: variables.map((variable) => {
|
|
717
|
+
const used = variableUses.get(variable);
|
|
718
|
+
return (used !== undefined &&
|
|
719
|
+
used.size > 0 &&
|
|
720
|
+
[...used].every((property) => !UNITLESS.has(property) && !property.startsWith('--')));
|
|
721
|
+
}),
|
|
722
|
+
};
|
|
607
723
|
continue;
|
|
608
724
|
}
|
|
609
725
|
keys.push(className);
|
|
@@ -631,6 +747,7 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
631
747
|
const sort = () => {
|
|
632
748
|
const edges = new Map();
|
|
633
749
|
const blame = new Map();
|
|
750
|
+
const link = (before, after) => edges.set(before, (edges.get(before) ?? new Set()).add(after));
|
|
634
751
|
for (const entry of resolved) {
|
|
635
752
|
if (inlined.has(entry))
|
|
636
753
|
continue;
|
|
@@ -638,9 +755,14 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
638
755
|
for (let second = first + 1; second < entry.slots.length; second++) {
|
|
639
756
|
for (const before of entry.slots[first]) {
|
|
640
757
|
for (const after of entry.slots[second]) {
|
|
641
|
-
if (before === after
|
|
758
|
+
if (before === after)
|
|
759
|
+
continue;
|
|
760
|
+
const { ranked, crossing } = relate(before, after);
|
|
761
|
+
if (ranked)
|
|
762
|
+
link(ranked[0], ranked[1]);
|
|
763
|
+
if (!crossing)
|
|
642
764
|
continue;
|
|
643
|
-
|
|
765
|
+
link(before, after);
|
|
644
766
|
blame.set(`${before}|${after}`, entry.composition);
|
|
645
767
|
}
|
|
646
768
|
}
|
|
@@ -693,7 +815,7 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
693
815
|
if (entry.slots.some((slot) => slot.length !== 1))
|
|
694
816
|
continue;
|
|
695
817
|
const names = entry.slots.map((slot) => slot[0]);
|
|
696
|
-
const signature = (
|
|
818
|
+
const signature = own(target, names);
|
|
697
819
|
if (overrides[signature])
|
|
698
820
|
continue;
|
|
699
821
|
const perSlot = {};
|
|
@@ -701,7 +823,7 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
701
823
|
const losers = names
|
|
702
824
|
.slice(0, slot)
|
|
703
825
|
.filter((earlier) => earlier !== names[slot] &&
|
|
704
|
-
|
|
826
|
+
crosses(earlier, names[slot]) &&
|
|
705
827
|
order.get(earlier) > order.get(names[slot]));
|
|
706
828
|
if (losers.length === 0)
|
|
707
829
|
continue;
|
|
@@ -721,17 +843,15 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
721
843
|
const theirs = footprints.get(loser);
|
|
722
844
|
if (!mine || !theirs)
|
|
723
845
|
continue;
|
|
724
|
-
for (const
|
|
725
|
-
const
|
|
726
|
-
|
|
846
|
+
for (const entry of mine) {
|
|
847
|
+
const crossed = theirs.some((candidate) => candidate.suffix === entry.suffix &&
|
|
848
|
+
candidate.rank === entry.rank &&
|
|
849
|
+
overlaps(entry.property, candidate.property));
|
|
850
|
+
if (!crossed)
|
|
727
851
|
continue;
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
const held = disputed.get(bucket) ?? new Set();
|
|
732
|
-
held.add(property);
|
|
733
|
-
disputed.set(bucket, held);
|
|
734
|
-
}
|
|
852
|
+
const held = disputed.get(entry.bucket) ?? new Set();
|
|
853
|
+
held.add(entry.property);
|
|
854
|
+
disputed.set(entry.bucket, held);
|
|
735
855
|
}
|
|
736
856
|
}
|
|
737
857
|
if (disputed.size === 0)
|
|
@@ -747,7 +867,7 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
747
867
|
if (inlined.has(entry))
|
|
748
868
|
continue;
|
|
749
869
|
const signature = entry.slots.every((slot) => slot.length === 1) &&
|
|
750
|
-
(
|
|
870
|
+
own(target, entry.slots.map((slot) => slot[0]));
|
|
751
871
|
let unresolved;
|
|
752
872
|
for (let first = 0; first < entry.slots.length && !unresolved; first++) {
|
|
753
873
|
for (let second = first + 1; second < entry.slots.length; second++) {
|
|
@@ -755,7 +875,10 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
755
875
|
continue;
|
|
756
876
|
for (const before of entry.slots[first]) {
|
|
757
877
|
for (const after of entry.slots[second]) {
|
|
758
|
-
if (before === after
|
|
878
|
+
if (before === after)
|
|
879
|
+
continue;
|
|
880
|
+
const { ranked, crossing } = relate(before, after);
|
|
881
|
+
if (ranked || !crossing)
|
|
759
882
|
continue;
|
|
760
883
|
if (order.get(before) < order.get(after))
|
|
761
884
|
continue;
|
|
@@ -774,7 +897,7 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
774
897
|
});
|
|
775
898
|
}
|
|
776
899
|
for (const entry of collapsible) {
|
|
777
|
-
const signature = (
|
|
900
|
+
const signature = own(target, entry.parts.map((part) => part.className));
|
|
778
901
|
if (merges[signature])
|
|
779
902
|
continue;
|
|
780
903
|
const merged = classNameFor(binding, camelJoin(entry.parts.map((part) => part.className)));
|
|
@@ -787,13 +910,80 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
787
910
|
emit(part.value, `.${merged}`, [], values, 'nested');
|
|
788
911
|
}
|
|
789
912
|
else {
|
|
790
|
-
rules.push(
|
|
791
|
-
|
|
792
|
-
|
|
913
|
+
rules.push({
|
|
914
|
+
text: `.${merged} {\n composes: ${entry.parts
|
|
915
|
+
.map((part) => part.className)
|
|
916
|
+
.join(' ')};\n}`,
|
|
917
|
+
});
|
|
793
918
|
}
|
|
794
919
|
keys.push(merged);
|
|
795
920
|
merges[signature] = merged;
|
|
796
921
|
}
|
|
922
|
+
const parsed = new Map();
|
|
923
|
+
const styleOf = (part) => {
|
|
924
|
+
if (!part.source) {
|
|
925
|
+
const held = members.get(`${part.binding}.${part.key}`);
|
|
926
|
+
return held ? { value: held.value, scope: values } : undefined;
|
|
927
|
+
}
|
|
928
|
+
const held = options.foreign?.[part.source];
|
|
929
|
+
if (!held)
|
|
930
|
+
return undefined;
|
|
931
|
+
let tree = parsed.get(part.source);
|
|
932
|
+
if (!tree) {
|
|
933
|
+
try {
|
|
934
|
+
tree = (0, parser_1.parse)(held.text, {
|
|
935
|
+
sourceType: 'module',
|
|
936
|
+
ecmaFeatures: { jsx: true },
|
|
937
|
+
loc: true,
|
|
938
|
+
range: true,
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
catch {
|
|
942
|
+
return undefined;
|
|
943
|
+
}
|
|
944
|
+
parsed.set(part.source, tree);
|
|
945
|
+
}
|
|
946
|
+
for (const raw of tree.body) {
|
|
947
|
+
const statement = declarationOf(raw);
|
|
948
|
+
if (statement?.type !== 'VariableDeclaration')
|
|
949
|
+
continue;
|
|
950
|
+
for (const declaration of statement.declarations) {
|
|
951
|
+
if (declaration.id.name !== part.binding ||
|
|
952
|
+
!isCall(declaration.init, 'create'))
|
|
953
|
+
continue;
|
|
954
|
+
for (const property of declaration.init.arguments[0]?.properties ??
|
|
955
|
+
[]) {
|
|
956
|
+
if (property.type !== 'Property')
|
|
957
|
+
continue;
|
|
958
|
+
const name = property.computed
|
|
959
|
+
? undefined
|
|
960
|
+
: propertyKey(property.key);
|
|
961
|
+
if (name === part.key)
|
|
962
|
+
return {
|
|
963
|
+
value: property.value,
|
|
964
|
+
scope: new Map(Object.entries(held.values)),
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
return undefined;
|
|
970
|
+
};
|
|
971
|
+
for (const folding of options.foldings ?? []) {
|
|
972
|
+
if (merges[folding.signature])
|
|
973
|
+
continue;
|
|
974
|
+
const parts = folding.parts.map(styleOf);
|
|
975
|
+
if (parts.some((part) => !part))
|
|
976
|
+
continue;
|
|
977
|
+
const merged = classNameFor(binding, camelJoin(folding.parts.map((part) => part.key)));
|
|
978
|
+
taken.add(merged);
|
|
979
|
+
openGroup(merged);
|
|
980
|
+
for (const part of parts)
|
|
981
|
+
emit(part.value, `.${merged}`, [], part.scope, 'declarations');
|
|
982
|
+
for (const part of parts)
|
|
983
|
+
emit(part.value, `.${merged}`, [], part.scope, 'nested');
|
|
984
|
+
keys.push(merged);
|
|
985
|
+
merges[folding.signature] = merged;
|
|
986
|
+
}
|
|
797
987
|
const emitted = [
|
|
798
988
|
...sorted.ordered,
|
|
799
989
|
...[...sorted.stuck],
|
|
@@ -804,7 +994,8 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
|
804
994
|
if (seen.has(name))
|
|
805
995
|
return [];
|
|
806
996
|
seen.add(name);
|
|
807
|
-
|
|
997
|
+
const group = groups.find((held) => held.name === name);
|
|
998
|
+
return group ? byImplication(byRank(group.rules)) : [];
|
|
808
999
|
});
|
|
809
1000
|
return {
|
|
810
1001
|
binding,
|
|
@@ -387,7 +387,10 @@ exports.releaseStyles = {
|
|
|
387
387
|
className: accessOf(resolved.module, resolved.key),
|
|
388
388
|
declarations: func.variables.map((variable, index) => {
|
|
389
389
|
const argument = sourceCode.getText(value.arguments[index]);
|
|
390
|
-
|
|
390
|
+
const written = func.lengths?.[index]
|
|
391
|
+
? `typeof ${argument} === 'number' ? \`\${${argument}}px\` : ${argument}`
|
|
392
|
+
: argument;
|
|
393
|
+
return `['${variable}' as string]: ${written}`;
|
|
391
394
|
}),
|
|
392
395
|
};
|
|
393
396
|
};
|
|
@@ -627,7 +630,8 @@ exports.releaseStyles = {
|
|
|
627
630
|
node.id.type !== 'Identifier' ||
|
|
628
631
|
node.init?.type !== 'Literal' ||
|
|
629
632
|
constants[node.id.name] !== node.init.value ||
|
|
630
|
-
node.parent.declarations.length !== 1
|
|
633
|
+
node.parent.declarations.length !== 1 ||
|
|
634
|
+
node.parent.parent?.type === 'ExportNamedDeclaration')
|
|
631
635
|
return;
|
|
632
636
|
const scope = sourceCode.getScope(node);
|
|
633
637
|
const variable = scope.variables.find((candidate) => candidate.name === node.id.name);
|
|
@@ -777,13 +781,14 @@ exports.releaseStyles = {
|
|
|
777
781
|
if (elements.length < 2)
|
|
778
782
|
return undefined;
|
|
779
783
|
const resolved = elements.map(styleKey);
|
|
780
|
-
if (resolved.some((part) => !part))
|
|
784
|
+
if (resolved.some((part) => !part?.module.target))
|
|
781
785
|
return undefined;
|
|
782
|
-
const
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
786
|
+
const host = resolved[0].module;
|
|
787
|
+
const signature = resolved
|
|
788
|
+
.map((part) => `${part.module.target}#${part.key}`)
|
|
789
|
+
.join('|');
|
|
790
|
+
const name = host.merges?.[signature];
|
|
791
|
+
return name ? accessOf(host, name) : undefined;
|
|
787
792
|
};
|
|
788
793
|
const replaceWithFunctionStyle = (className, declarations) => {
|
|
789
794
|
const styleAttribute = node.parent.attributes.find((attribute) => attribute.type === 'JSXAttribute' &&
|