@plumeria/codemod 18.2.19 → 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 +5 -3
- package/dist/migrate.js +3 -1
- package/dist/release.d.ts +2 -0
- package/dist/release.js +295 -38
- 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 +17 -1
- package/dist/transforms/from-plumeria.js +276 -10
- package/dist/transforms/release-styles.d.ts +4 -0
- package/dist/transforms/release-styles.js +466 -66
- package/package.json +1 -1
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.__private = void 0;
|
|
3
|
+
exports.__private = exports.compositionSignature = void 0;
|
|
4
4
|
exports.extractPlumeriaThemes = extractPlumeriaThemes;
|
|
5
5
|
exports.extractPlumeriaStatics = extractPlumeriaStatics;
|
|
6
6
|
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 compositionSignature = (classNames) => classNames.join('|');
|
|
11
|
+
exports.compositionSignature = compositionSignature;
|
|
10
12
|
const toIdent = (value) => value.replace(/[^A-Za-z0-9-]/g, '');
|
|
13
|
+
const capitalize = (value) => `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
|
|
14
|
+
const camelJoin = (names) => names
|
|
15
|
+
.map((name, index) => {
|
|
16
|
+
const camel = name
|
|
17
|
+
.split('-')
|
|
18
|
+
.map((part, position) => (position === 0 ? part : capitalize(part)))
|
|
19
|
+
.join('');
|
|
20
|
+
return index === 0 ? camel : capitalize(camel);
|
|
21
|
+
})
|
|
22
|
+
.join('');
|
|
11
23
|
const markerVariable = (id, pseudo) => {
|
|
12
24
|
const hash = (0, zss_engine_1.genBase36Hash)({ [id]: pseudo }, 1, 8);
|
|
13
25
|
return `--${hash}-${toIdent(id)}-${toIdent(pseudo.split('(')[0])}`;
|
|
@@ -64,6 +76,14 @@ const evaluate = (node, values) => {
|
|
|
64
76
|
}
|
|
65
77
|
if (node.type === 'Literal')
|
|
66
78
|
return node.value;
|
|
79
|
+
if (node.type === 'UnaryExpression') {
|
|
80
|
+
if (node.operator !== '-' && node.operator !== '+')
|
|
81
|
+
return undefined;
|
|
82
|
+
const operand = evaluate(node.argument, values);
|
|
83
|
+
if (typeof operand !== 'number')
|
|
84
|
+
return undefined;
|
|
85
|
+
return node.operator === '-' ? -operand : operand;
|
|
86
|
+
}
|
|
67
87
|
if (node.type === 'Identifier')
|
|
68
88
|
return values.get(node.name);
|
|
69
89
|
if (node.type === 'MemberExpression') {
|
|
@@ -346,7 +366,7 @@ function extractPlumeriaAnimations(source, externalValues = {}) {
|
|
|
346
366
|
reports,
|
|
347
367
|
};
|
|
348
368
|
}
|
|
349
|
-
function convertPlumeriaModule(source, externalValues = {}) {
|
|
369
|
+
function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
350
370
|
const ast = (0, parser_1.parse)(source, {
|
|
351
371
|
sourceType: 'module',
|
|
352
372
|
ecmaFeatures: { jsx: true },
|
|
@@ -403,9 +423,37 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
403
423
|
if (creates.length === 0)
|
|
404
424
|
return null;
|
|
405
425
|
const { binding } = creates[0];
|
|
406
|
-
const
|
|
426
|
+
const groups = [];
|
|
427
|
+
let rules = [];
|
|
428
|
+
const openGroup = (name) => {
|
|
429
|
+
rules = [];
|
|
430
|
+
groups.push({ name, rules });
|
|
431
|
+
};
|
|
407
432
|
const keys = [];
|
|
408
433
|
const aliases = {};
|
|
434
|
+
const merges = {};
|
|
435
|
+
const members = new Map();
|
|
436
|
+
const footprints = new Map();
|
|
437
|
+
const track = (className) => {
|
|
438
|
+
const map = new Map();
|
|
439
|
+
footprints.set(className, map);
|
|
440
|
+
return { root: `.${className}`, map };
|
|
441
|
+
};
|
|
442
|
+
const conflicts = (first, second) => {
|
|
443
|
+
const left = footprints.get(first);
|
|
444
|
+
const right = footprints.get(second);
|
|
445
|
+
if (!left || !right)
|
|
446
|
+
return true;
|
|
447
|
+
for (const [bucket, properties] of left) {
|
|
448
|
+
const other = right.get(bucket);
|
|
449
|
+
if (!other)
|
|
450
|
+
continue;
|
|
451
|
+
for (const property of properties)
|
|
452
|
+
if (other.has(property))
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
return false;
|
|
456
|
+
};
|
|
409
457
|
const taken = new Set();
|
|
410
458
|
const functions = {};
|
|
411
459
|
const classNameFor = (owner, key) => {
|
|
@@ -418,11 +466,17 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
418
466
|
candidate = `${namespaced}-${suffix++}`;
|
|
419
467
|
return candidate;
|
|
420
468
|
};
|
|
421
|
-
const
|
|
469
|
+
const bucketOf = (wrappers, suffix) => `${wrappers.join('\u0000')}\u0001${suffix}`;
|
|
470
|
+
const selectorRoot = (selector) => {
|
|
471
|
+
const match = /^\.[A-Za-z0-9_-]+(?::not\(#\\#\))?/.exec(selector);
|
|
472
|
+
return match ? match[0].length : 0;
|
|
473
|
+
};
|
|
474
|
+
const emit = (style, selector, wrappers = [], scope = values, phase = 'both', footprint, restrict) => {
|
|
422
475
|
if (style?.type !== 'ObjectExpression') {
|
|
423
476
|
report(style, 'dynamic-style', 'Only static style objects are exported.');
|
|
424
477
|
return;
|
|
425
478
|
}
|
|
479
|
+
const restrictRoot = restrict ? selectorRoot(selector) : 0;
|
|
426
480
|
const declarations = [];
|
|
427
481
|
const nested = [];
|
|
428
482
|
const markers = [];
|
|
@@ -460,6 +514,17 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
460
514
|
continue;
|
|
461
515
|
}
|
|
462
516
|
const cssProperty = (0, zss_engine_1.camelToKebabCase)(rawKey);
|
|
517
|
+
if (restrict &&
|
|
518
|
+
!restrict
|
|
519
|
+
.get(bucketOf(wrappers, selector.slice(restrictRoot)))
|
|
520
|
+
?.has(cssProperty))
|
|
521
|
+
continue;
|
|
522
|
+
if (footprint) {
|
|
523
|
+
const bucket = bucketOf(wrappers, selector.slice(footprint.root.length));
|
|
524
|
+
const held = footprint.map.get(bucket) ?? new Set();
|
|
525
|
+
held.add(cssProperty);
|
|
526
|
+
footprint.map.set(bucket, held);
|
|
527
|
+
}
|
|
463
528
|
declarations.push(` ${cssProperty}: ${(0, zss_engine_1.applyCssValue)(value, cssProperty)};`);
|
|
464
529
|
}
|
|
465
530
|
const pushRule = (target, lines) => {
|
|
@@ -474,16 +539,19 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
474
539
|
}
|
|
475
540
|
rules.push(rule);
|
|
476
541
|
};
|
|
477
|
-
|
|
542
|
+
if (phase !== 'nested')
|
|
543
|
+
pushRule(selector, declarations);
|
|
544
|
+
if (phase === 'declarations')
|
|
545
|
+
return;
|
|
478
546
|
for (const marker of markers) {
|
|
479
547
|
pushRule(`${selector}${marker.pseudo}`, [` ${marker.variable}: 1;`]);
|
|
480
548
|
}
|
|
481
549
|
for (const child of nested) {
|
|
482
550
|
if (child.key.startsWith('@')) {
|
|
483
|
-
emit(child.value, selector, [...wrappers, child.key], scope);
|
|
551
|
+
emit(child.value, selector, [...wrappers, child.key], scope, 'both', footprint, restrict);
|
|
484
552
|
}
|
|
485
553
|
else {
|
|
486
|
-
emit(child.value, `${selector}${child.key}`, wrappers, scope);
|
|
554
|
+
emit(child.value, `${selector}${child.key}`, wrappers, scope, 'both', footprint, restrict);
|
|
487
555
|
}
|
|
488
556
|
}
|
|
489
557
|
};
|
|
@@ -506,6 +574,7 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
506
574
|
taken.add(className);
|
|
507
575
|
if (owner !== binding)
|
|
508
576
|
alias[key] = className;
|
|
577
|
+
members.set(`${owner}.${key}`, { className, value: property.value });
|
|
509
578
|
if (property.value.type === 'ArrowFunctionExpression' ||
|
|
510
579
|
property.value.type === 'FunctionExpression') {
|
|
511
580
|
const params = [];
|
|
@@ -533,22 +602,219 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
533
602
|
});
|
|
534
603
|
functions[className] = { params, variables };
|
|
535
604
|
keys.push(className);
|
|
536
|
-
|
|
605
|
+
openGroup(className);
|
|
606
|
+
emit(body, `.${className}`, [], scope, 'both', track(className));
|
|
537
607
|
continue;
|
|
538
608
|
}
|
|
539
609
|
keys.push(className);
|
|
540
|
-
|
|
610
|
+
openGroup(className);
|
|
611
|
+
emit(property.value, `.${className}`, [], values, 'both', track(className));
|
|
541
612
|
}
|
|
542
613
|
if (owner !== binding)
|
|
543
614
|
aliases[owner] = alias;
|
|
544
615
|
}
|
|
616
|
+
const resolved = compositions.map((composition) => ({
|
|
617
|
+
composition,
|
|
618
|
+
parts: composition.parts.flatMap((part) => {
|
|
619
|
+
const member = members.get(`${part.binding}.${part.key}`);
|
|
620
|
+
return member && !functions[member.className] ? [member] : [];
|
|
621
|
+
}),
|
|
622
|
+
slots: composition.slots.map((slot) => slot.flatMap((part) => {
|
|
623
|
+
const member = members.get(`${part.binding}.${part.key}`);
|
|
624
|
+
return member ? [member.className] : [];
|
|
625
|
+
})),
|
|
626
|
+
}));
|
|
627
|
+
const collapsible = new Set(resolved.filter((entry) => entry.composition.parts.length > 1 &&
|
|
628
|
+
entry.parts.length === entry.composition.parts.length));
|
|
629
|
+
const position = new Map(groups.map((group, index) => [group.name, index]));
|
|
630
|
+
const inlined = new Set();
|
|
631
|
+
const sort = () => {
|
|
632
|
+
const edges = new Map();
|
|
633
|
+
const blame = new Map();
|
|
634
|
+
for (const entry of resolved) {
|
|
635
|
+
if (inlined.has(entry))
|
|
636
|
+
continue;
|
|
637
|
+
for (let first = 0; first < entry.slots.length; first++) {
|
|
638
|
+
for (let second = first + 1; second < entry.slots.length; second++) {
|
|
639
|
+
for (const before of entry.slots[first]) {
|
|
640
|
+
for (const after of entry.slots[second]) {
|
|
641
|
+
if (before === after || !conflicts(before, after))
|
|
642
|
+
continue;
|
|
643
|
+
edges.set(before, (edges.get(before) ?? new Set()).add(after));
|
|
644
|
+
blame.set(`${before}|${after}`, entry.composition);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const incoming = new Map(groups.map((group) => [group.name, 0]));
|
|
651
|
+
for (const [, targets] of edges)
|
|
652
|
+
for (const target of targets)
|
|
653
|
+
if (incoming.has(target))
|
|
654
|
+
incoming.set(target, incoming.get(target) + 1);
|
|
655
|
+
const ready = groups
|
|
656
|
+
.filter((group) => incoming.get(group.name) === 0)
|
|
657
|
+
.map((group) => group.name);
|
|
658
|
+
const ordered = [];
|
|
659
|
+
while (ready.length > 0) {
|
|
660
|
+
ready.sort((a, b) => position.get(a) - position.get(b));
|
|
661
|
+
const name = ready.shift();
|
|
662
|
+
ordered.push(name);
|
|
663
|
+
for (const target of edges.get(name) ?? []) {
|
|
664
|
+
if (!incoming.has(target))
|
|
665
|
+
continue;
|
|
666
|
+
const remaining = incoming.get(target) - 1;
|
|
667
|
+
incoming.set(target, remaining);
|
|
668
|
+
if (remaining === 0)
|
|
669
|
+
ready.push(target);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
const stuck = new Set(groups
|
|
673
|
+
.map((group) => group.name)
|
|
674
|
+
.filter((name) => !ordered.includes(name)));
|
|
675
|
+
return { ordered, stuck, edges, blame };
|
|
676
|
+
};
|
|
677
|
+
let sorted = sort();
|
|
678
|
+
while (sorted.stuck.size > 0) {
|
|
679
|
+
const offender = [...collapsible].find((entry) => !inlined.has(entry) &&
|
|
680
|
+
entry.parts.filter((part) => sorted.stuck.has(part.className)).length >
|
|
681
|
+
1);
|
|
682
|
+
if (!offender)
|
|
683
|
+
break;
|
|
684
|
+
inlined.add(offender);
|
|
685
|
+
sorted = sort();
|
|
686
|
+
}
|
|
687
|
+
const order = new Map([...sorted.ordered, ...sorted.stuck].map((name, index) => [name, index]));
|
|
688
|
+
const overrides = {};
|
|
689
|
+
const pending = [];
|
|
690
|
+
for (const entry of resolved) {
|
|
691
|
+
if (inlined.has(entry))
|
|
692
|
+
continue;
|
|
693
|
+
if (entry.slots.some((slot) => slot.length !== 1))
|
|
694
|
+
continue;
|
|
695
|
+
const names = entry.slots.map((slot) => slot[0]);
|
|
696
|
+
const signature = (0, exports.compositionSignature)(names);
|
|
697
|
+
if (overrides[signature])
|
|
698
|
+
continue;
|
|
699
|
+
const perSlot = {};
|
|
700
|
+
for (let slot = 1; slot < names.length; slot++) {
|
|
701
|
+
const losers = names
|
|
702
|
+
.slice(0, slot)
|
|
703
|
+
.filter((earlier) => earlier !== names[slot] &&
|
|
704
|
+
conflicts(earlier, names[slot]) &&
|
|
705
|
+
order.get(earlier) > order.get(names[slot]));
|
|
706
|
+
if (losers.length === 0)
|
|
707
|
+
continue;
|
|
708
|
+
pending.push({ signature, slot, winner: names[slot], losers });
|
|
709
|
+
perSlot[slot] = '';
|
|
710
|
+
}
|
|
711
|
+
if (Object.keys(perSlot).length > 0)
|
|
712
|
+
overrides[signature] = perSlot;
|
|
713
|
+
}
|
|
714
|
+
for (const request of pending) {
|
|
715
|
+
const winner = members.get([...members.keys()].find((key) => members.get(key)?.className === request.winner));
|
|
716
|
+
if (!winner)
|
|
717
|
+
continue;
|
|
718
|
+
const disputed = new Map();
|
|
719
|
+
const mine = footprints.get(request.winner);
|
|
720
|
+
for (const loser of request.losers) {
|
|
721
|
+
const theirs = footprints.get(loser);
|
|
722
|
+
if (!mine || !theirs)
|
|
723
|
+
continue;
|
|
724
|
+
for (const [bucket, properties] of mine) {
|
|
725
|
+
const other = theirs.get(bucket);
|
|
726
|
+
if (!other)
|
|
727
|
+
continue;
|
|
728
|
+
for (const property of properties) {
|
|
729
|
+
if (!other.has(property))
|
|
730
|
+
continue;
|
|
731
|
+
const held = disputed.get(bucket) ?? new Set();
|
|
732
|
+
held.add(property);
|
|
733
|
+
disputed.set(bucket, held);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (disputed.size === 0)
|
|
738
|
+
continue;
|
|
739
|
+
const name = classNameFor(binding, camelJoin([request.winner, `over-${request.losers.join('-')}`]));
|
|
740
|
+
taken.add(name);
|
|
741
|
+
openGroup(name);
|
|
742
|
+
emit(winner.value, `.${name}:not(#\\#)`, [], values, 'both', undefined, disputed);
|
|
743
|
+
keys.push(name);
|
|
744
|
+
overrides[request.signature][request.slot] = name;
|
|
745
|
+
}
|
|
746
|
+
for (const entry of resolved) {
|
|
747
|
+
if (inlined.has(entry))
|
|
748
|
+
continue;
|
|
749
|
+
const signature = entry.slots.every((slot) => slot.length === 1) &&
|
|
750
|
+
(0, exports.compositionSignature)(entry.slots.map((slot) => slot[0]));
|
|
751
|
+
let unresolved;
|
|
752
|
+
for (let first = 0; first < entry.slots.length && !unresolved; first++) {
|
|
753
|
+
for (let second = first + 1; second < entry.slots.length; second++) {
|
|
754
|
+
if (signature && overrides[signature]?.[second])
|
|
755
|
+
continue;
|
|
756
|
+
for (const before of entry.slots[first]) {
|
|
757
|
+
for (const after of entry.slots[second]) {
|
|
758
|
+
if (before === after || !conflicts(before, after))
|
|
759
|
+
continue;
|
|
760
|
+
if (order.get(before) < order.get(after))
|
|
761
|
+
continue;
|
|
762
|
+
unresolved = [before, after];
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
if (!unresolved)
|
|
768
|
+
continue;
|
|
769
|
+
reports.push({
|
|
770
|
+
line: entry.composition.line,
|
|
771
|
+
column: entry.composition.column,
|
|
772
|
+
kind: 'composition-order',
|
|
773
|
+
hint: `\`${unresolved[1]}\` has to win over \`${unresolved[0]}\` here and lose elsewhere, and this call site cannot carry an override.`,
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
for (const entry of collapsible) {
|
|
777
|
+
const signature = (0, exports.compositionSignature)(entry.parts.map((part) => part.className));
|
|
778
|
+
if (merges[signature])
|
|
779
|
+
continue;
|
|
780
|
+
const merged = classNameFor(binding, camelJoin(entry.parts.map((part) => part.className)));
|
|
781
|
+
taken.add(merged);
|
|
782
|
+
openGroup(merged);
|
|
783
|
+
if (inlined.has(entry)) {
|
|
784
|
+
for (const part of entry.parts)
|
|
785
|
+
emit(part.value, `.${merged}`, [], values, 'declarations');
|
|
786
|
+
for (const part of entry.parts)
|
|
787
|
+
emit(part.value, `.${merged}`, [], values, 'nested');
|
|
788
|
+
}
|
|
789
|
+
else {
|
|
790
|
+
rules.push(`.${merged} {\n composes: ${entry.parts
|
|
791
|
+
.map((part) => part.className)
|
|
792
|
+
.join(' ')};\n}`);
|
|
793
|
+
}
|
|
794
|
+
keys.push(merged);
|
|
795
|
+
merges[signature] = merged;
|
|
796
|
+
}
|
|
797
|
+
const emitted = [
|
|
798
|
+
...sorted.ordered,
|
|
799
|
+
...[...sorted.stuck],
|
|
800
|
+
...groups.slice(position.size).map((group) => group.name),
|
|
801
|
+
];
|
|
802
|
+
const seen = new Set();
|
|
803
|
+
const css = emitted.flatMap((name) => {
|
|
804
|
+
if (seen.has(name))
|
|
805
|
+
return [];
|
|
806
|
+
seen.add(name);
|
|
807
|
+
return groups.find((group) => group.name === name)?.rules ?? [];
|
|
808
|
+
});
|
|
545
809
|
return {
|
|
546
810
|
binding,
|
|
547
|
-
css:
|
|
811
|
+
css: css.length > 0 ? `${css.join('\n\n')}\n` : '',
|
|
548
812
|
globalCss: `${themes.globalCss}${animations.globalCss}`,
|
|
549
813
|
keys,
|
|
550
814
|
functions,
|
|
551
815
|
aliases,
|
|
816
|
+
merges,
|
|
817
|
+
overrides,
|
|
552
818
|
definitionOnly: creates.every((create) => !bindsElsewhere(ast, create.binding, create.declaration.id)),
|
|
553
819
|
reports,
|
|
554
820
|
};
|
|
@@ -9,12 +9,16 @@ export interface ReleaseModule {
|
|
|
9
9
|
params: string[];
|
|
10
10
|
variables: string[];
|
|
11
11
|
}>;
|
|
12
|
+
merges?: Record<string, string>;
|
|
13
|
+
overrides?: Record<string, Record<number, string>>;
|
|
12
14
|
}
|
|
13
15
|
export interface ReleaseStylesOptions {
|
|
14
16
|
modules: Record<string, ReleaseModule>;
|
|
15
17
|
themes?: Record<string, string[]>;
|
|
16
18
|
animations?: Record<string, string[]>;
|
|
17
19
|
statics?: Record<string, string[]>;
|
|
20
|
+
values?: Record<string, Record<string, unknown>>;
|
|
21
|
+
complete?: boolean;
|
|
18
22
|
styleProp?: string;
|
|
19
23
|
active?: boolean;
|
|
20
24
|
}
|