@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
|
@@ -1,11 +1,29 @@
|
|
|
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
|
+
exports.extractPlumeriaStatics = extractPlumeriaStatics;
|
|
5
6
|
exports.extractPlumeriaAnimations = extractPlumeriaAnimations;
|
|
6
7
|
exports.convertPlumeriaModule = convertPlumeriaModule;
|
|
7
8
|
const parser_1 = require("@typescript-eslint/parser");
|
|
8
9
|
const zss_engine_1 = require("zss-engine");
|
|
10
|
+
const compositionSignature = (classNames) => classNames.join('|');
|
|
11
|
+
exports.compositionSignature = compositionSignature;
|
|
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('');
|
|
23
|
+
const markerVariable = (id, pseudo) => {
|
|
24
|
+
const hash = (0, zss_engine_1.genBase36Hash)({ [id]: pseudo }, 1, 8);
|
|
25
|
+
return `--${hash}-${toIdent(id)}-${toIdent(pseudo.split('(')[0])}`;
|
|
26
|
+
};
|
|
9
27
|
const propertyKey = (node) => {
|
|
10
28
|
if (!node)
|
|
11
29
|
return undefined;
|
|
@@ -35,11 +53,37 @@ const lookup = (path, values) => {
|
|
|
35
53
|
}
|
|
36
54
|
return value;
|
|
37
55
|
};
|
|
56
|
+
const markerArguments = (node, method, values) => {
|
|
57
|
+
if (node?.type !== 'CallExpression')
|
|
58
|
+
return undefined;
|
|
59
|
+
const path = memberPath(node.callee);
|
|
60
|
+
if (path?.length !== 2 || path[1] !== method)
|
|
61
|
+
return undefined;
|
|
62
|
+
const id = evaluate(node.arguments[0], values);
|
|
63
|
+
const pseudo = evaluate(node.arguments[1], values);
|
|
64
|
+
return typeof id === 'string' && typeof pseudo === 'string'
|
|
65
|
+
? { id, pseudo }
|
|
66
|
+
: undefined;
|
|
67
|
+
};
|
|
38
68
|
const evaluate = (node, values) => {
|
|
39
69
|
if (!node)
|
|
40
70
|
return undefined;
|
|
71
|
+
if (node.type === 'CallExpression') {
|
|
72
|
+
const extended = markerArguments(node, 'extended', values);
|
|
73
|
+
return extended
|
|
74
|
+
? `@container style(${markerVariable(extended.id, extended.pseudo)}: 1)`
|
|
75
|
+
: undefined;
|
|
76
|
+
}
|
|
41
77
|
if (node.type === 'Literal')
|
|
42
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
|
+
}
|
|
43
87
|
if (node.type === 'Identifier')
|
|
44
88
|
return values.get(node.name);
|
|
45
89
|
if (node.type === 'MemberExpression') {
|
|
@@ -78,7 +122,29 @@ const evaluate = (node, values) => {
|
|
|
78
122
|
}
|
|
79
123
|
return undefined;
|
|
80
124
|
};
|
|
81
|
-
|
|
125
|
+
const bindsElsewhere = (ast, name, declared) => {
|
|
126
|
+
const skipped = new Set(['loc', 'range', 'parent']);
|
|
127
|
+
const visit = (node, parent) => {
|
|
128
|
+
if (!node || typeof node !== 'object' || node === declared)
|
|
129
|
+
return false;
|
|
130
|
+
if (Array.isArray(node))
|
|
131
|
+
return node.some((child) => visit(child, parent));
|
|
132
|
+
if (typeof node.type !== 'string')
|
|
133
|
+
return false;
|
|
134
|
+
if (node.type === 'Identifier' && node.name === name) {
|
|
135
|
+
if (parent?.type === 'ExportSpecifier')
|
|
136
|
+
return false;
|
|
137
|
+
if (parent?.type === 'MemberExpression' && parent.property === node)
|
|
138
|
+
return !!parent.computed;
|
|
139
|
+
if (parent?.type === 'Property' && parent.key === node)
|
|
140
|
+
return !!parent.computed;
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
return Object.keys(node).some((key) => !skipped.has(key) && visit(node[key], node));
|
|
144
|
+
};
|
|
145
|
+
return visit(ast, undefined);
|
|
146
|
+
};
|
|
147
|
+
exports.__private = { propertyKey, memberPath, evaluate, bindsElsewhere };
|
|
82
148
|
const isCall = (node, method) => {
|
|
83
149
|
if (node?.type !== 'CallExpression')
|
|
84
150
|
return false;
|
|
@@ -192,6 +258,41 @@ const emitGlobalObject = (selector, object) => {
|
|
|
192
258
|
const content = [...declarations, ...children].join('\n');
|
|
193
259
|
return `${selector} {\n${content}\n}`;
|
|
194
260
|
};
|
|
261
|
+
function extractPlumeriaStatics(source, externalValues = {}) {
|
|
262
|
+
const ast = (0, parser_1.parse)(source, {
|
|
263
|
+
sourceType: 'module',
|
|
264
|
+
ecmaFeatures: { jsx: true },
|
|
265
|
+
loc: true,
|
|
266
|
+
range: true,
|
|
267
|
+
});
|
|
268
|
+
const values = new Map(Object.entries(externalValues));
|
|
269
|
+
const bindings = {};
|
|
270
|
+
const reports = [];
|
|
271
|
+
for (const raw of ast.body) {
|
|
272
|
+
const statement = declarationOf(raw);
|
|
273
|
+
if (statement?.type !== 'VariableDeclaration')
|
|
274
|
+
continue;
|
|
275
|
+
for (const declaration of statement.declarations) {
|
|
276
|
+
if (declaration.id.type !== 'Identifier' ||
|
|
277
|
+
!isCall(declaration.init, 'createStatic'))
|
|
278
|
+
continue;
|
|
279
|
+
const value = evaluate(declaration.init.arguments[0], values);
|
|
280
|
+
if (value && typeof value === 'object') {
|
|
281
|
+
values.set(declaration.id.name, value);
|
|
282
|
+
bindings[declaration.id.name] = value;
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
reports.push({
|
|
286
|
+
line: declaration.init.loc.start.line,
|
|
287
|
+
column: declaration.init.loc.start.column + 1,
|
|
288
|
+
kind: 'dynamic-create-static',
|
|
289
|
+
hint: '`createStatic` must contain values that can be written into CSS.',
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return { bindings, reports };
|
|
295
|
+
}
|
|
195
296
|
function extractPlumeriaAnimations(source, externalValues = {}) {
|
|
196
297
|
const ast = (0, parser_1.parse)(source, {
|
|
197
298
|
sourceType: 'module',
|
|
@@ -265,7 +366,7 @@ function extractPlumeriaAnimations(source, externalValues = {}) {
|
|
|
265
366
|
reports,
|
|
266
367
|
};
|
|
267
368
|
}
|
|
268
|
-
function convertPlumeriaModule(source, externalValues = {}) {
|
|
369
|
+
function convertPlumeriaModule(source, externalValues = {}, compositions = []) {
|
|
269
370
|
const ast = (0, parser_1.parse)(source, {
|
|
270
371
|
sourceType: 'module',
|
|
271
372
|
ecmaFeatures: { jsx: true },
|
|
@@ -311,7 +412,7 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
311
412
|
if (isCall(declaration.init, 'create')) {
|
|
312
413
|
const object = declaration.init.arguments[0];
|
|
313
414
|
if (object?.type === 'ObjectExpression') {
|
|
314
|
-
creates.push({ binding: declaration.id.name, object });
|
|
415
|
+
creates.push({ binding: declaration.id.name, object, declaration });
|
|
315
416
|
}
|
|
316
417
|
else {
|
|
317
418
|
report(declaration.init, 'dynamic-create', '`css.create` must receive an object literal.');
|
|
@@ -321,21 +422,75 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
321
422
|
}
|
|
322
423
|
if (creates.length === 0)
|
|
323
424
|
return null;
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const
|
|
328
|
-
|
|
425
|
+
const { binding } = creates[0];
|
|
426
|
+
const groups = [];
|
|
427
|
+
let rules = [];
|
|
428
|
+
const openGroup = (name) => {
|
|
429
|
+
rules = [];
|
|
430
|
+
groups.push({ name, rules });
|
|
431
|
+
};
|
|
329
432
|
const keys = [];
|
|
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
|
+
};
|
|
457
|
+
const taken = new Set();
|
|
330
458
|
const functions = {};
|
|
331
|
-
const
|
|
459
|
+
const classNameFor = (owner, key) => {
|
|
460
|
+
if (!taken.has(key))
|
|
461
|
+
return key;
|
|
462
|
+
const namespaced = `${owner}-${key}`;
|
|
463
|
+
let candidate = namespaced;
|
|
464
|
+
let suffix = 2;
|
|
465
|
+
while (taken.has(candidate))
|
|
466
|
+
candidate = `${namespaced}-${suffix++}`;
|
|
467
|
+
return candidate;
|
|
468
|
+
};
|
|
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) => {
|
|
332
475
|
if (style?.type !== 'ObjectExpression') {
|
|
333
476
|
report(style, 'dynamic-style', 'Only static style objects are exported.');
|
|
334
477
|
return;
|
|
335
478
|
}
|
|
479
|
+
const restrictRoot = restrict ? selectorRoot(selector) : 0;
|
|
336
480
|
const declarations = [];
|
|
337
481
|
const nested = [];
|
|
482
|
+
const markers = [];
|
|
338
483
|
for (const property of style.properties) {
|
|
484
|
+
if (property.type === 'SpreadElement') {
|
|
485
|
+
const marker = markerArguments(property.argument, 'marker', scope);
|
|
486
|
+
if (marker) {
|
|
487
|
+
markers.push({
|
|
488
|
+
pseudo: marker.pseudo,
|
|
489
|
+
variable: markerVariable(marker.id, marker.pseudo),
|
|
490
|
+
});
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
339
494
|
if (property.type !== 'Property' || property.kind !== 'init') {
|
|
340
495
|
report(property, 'spread-style', 'Spread styles must be expanded before exporting to CSS Modules.');
|
|
341
496
|
continue;
|
|
@@ -359,10 +514,23 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
359
514
|
continue;
|
|
360
515
|
}
|
|
361
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
|
+
}
|
|
362
528
|
declarations.push(` ${cssProperty}: ${(0, zss_engine_1.applyCssValue)(value, cssProperty)};`);
|
|
363
529
|
}
|
|
364
|
-
|
|
365
|
-
|
|
530
|
+
const pushRule = (target, lines) => {
|
|
531
|
+
if (lines.length === 0)
|
|
532
|
+
return;
|
|
533
|
+
let rule = `${target} {\n${lines.join('\n')}\n}`;
|
|
366
534
|
for (const wrapper of [...wrappers].reverse()) {
|
|
367
535
|
rule = `${wrapper} {\n${rule
|
|
368
536
|
.split('\n')
|
|
@@ -370,67 +538,284 @@ function convertPlumeriaModule(source, externalValues = {}) {
|
|
|
370
538
|
.join('\n')}\n}`;
|
|
371
539
|
}
|
|
372
540
|
rules.push(rule);
|
|
541
|
+
};
|
|
542
|
+
if (phase !== 'nested')
|
|
543
|
+
pushRule(selector, declarations);
|
|
544
|
+
if (phase === 'declarations')
|
|
545
|
+
return;
|
|
546
|
+
for (const marker of markers) {
|
|
547
|
+
pushRule(`${selector}${marker.pseudo}`, [` ${marker.variable}: 1;`]);
|
|
373
548
|
}
|
|
374
549
|
for (const child of nested) {
|
|
375
550
|
if (child.key.startsWith('@')) {
|
|
376
|
-
emit(child.value, selector, [...wrappers, child.key], scope);
|
|
551
|
+
emit(child.value, selector, [...wrappers, child.key], scope, 'both', footprint, restrict);
|
|
377
552
|
}
|
|
378
553
|
else {
|
|
379
|
-
emit(child.value, `${selector}${child.key}`, wrappers, scope);
|
|
554
|
+
emit(child.value, `${selector}${child.key}`, wrappers, scope, 'both', footprint, restrict);
|
|
380
555
|
}
|
|
381
556
|
}
|
|
382
557
|
};
|
|
383
|
-
for (const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
558
|
+
for (const create of creates) {
|
|
559
|
+
const owner = create.binding;
|
|
560
|
+
const alias = {};
|
|
561
|
+
for (const property of create.object.properties) {
|
|
562
|
+
if (property.type !== 'Property' || property.kind !== 'init') {
|
|
563
|
+
report(property, 'spread-create', 'Top-level spreads cannot name a CSS Module class.');
|
|
564
|
+
continue;
|
|
565
|
+
}
|
|
566
|
+
const key = property.computed
|
|
567
|
+
? evaluate(property.key, values)
|
|
568
|
+
: propertyKey(property.key);
|
|
569
|
+
if (typeof key !== 'string') {
|
|
570
|
+
report(property.key, 'dynamic-style-key', 'Style names must be statically known.');
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
const className = classNameFor(owner, key);
|
|
574
|
+
taken.add(className);
|
|
575
|
+
if (owner !== binding)
|
|
576
|
+
alias[key] = className;
|
|
577
|
+
members.set(`${owner}.${key}`, { className, value: property.value });
|
|
578
|
+
if (property.value.type === 'ArrowFunctionExpression' ||
|
|
579
|
+
property.value.type === 'FunctionExpression') {
|
|
580
|
+
const params = [];
|
|
581
|
+
let valid = true;
|
|
582
|
+
for (const parameter of property.value.params) {
|
|
583
|
+
if (parameter.type !== 'Identifier') {
|
|
584
|
+
valid = false;
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
params.push(parameter.name);
|
|
588
|
+
}
|
|
589
|
+
let body = property.value.body;
|
|
590
|
+
if (body.type === 'BlockStatement') {
|
|
591
|
+
const returned = body.body.find((statement) => statement.type === 'ReturnStatement');
|
|
592
|
+
body = returned?.argument;
|
|
593
|
+
}
|
|
594
|
+
if (!valid || body?.type !== 'ObjectExpression') {
|
|
595
|
+
report(property.value, 'function-style', 'Function style keys need identifier parameters and an object return value.');
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
const variables = params.map((parameter) => `--${(0, zss_engine_1.camelToKebabCase)(owner)}-${(0, zss_engine_1.camelToKebabCase)(key)}-${(0, zss_engine_1.camelToKebabCase)(parameter)}`);
|
|
599
|
+
const scope = new Map(values);
|
|
600
|
+
params.forEach((parameter, index) => {
|
|
601
|
+
scope.set(parameter, `var(${variables[index]})`);
|
|
602
|
+
});
|
|
603
|
+
functions[className] = { params, variables };
|
|
604
|
+
keys.push(className);
|
|
605
|
+
openGroup(className);
|
|
606
|
+
emit(body, `.${className}`, [], scope, 'both', track(className));
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
keys.push(className);
|
|
610
|
+
openGroup(className);
|
|
611
|
+
emit(property.value, `.${className}`, [], values, 'both', track(className));
|
|
394
612
|
}
|
|
395
|
-
if (
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
613
|
+
if (owner !== binding)
|
|
614
|
+
aliases[owner] = alias;
|
|
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
|
+
}
|
|
403
647
|
}
|
|
404
|
-
params.push(parameter.name);
|
|
405
648
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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);
|
|
410
670
|
}
|
|
411
|
-
|
|
412
|
-
|
|
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)
|
|
413
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
|
+
}
|
|
414
765
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
params.forEach((parameter, index) => {
|
|
418
|
-
scope.set(parameter, `var(${variables[index]})`);
|
|
419
|
-
});
|
|
420
|
-
functions[key] = { params, variables };
|
|
421
|
-
keys.push(key);
|
|
422
|
-
emit(body, `.${key}`, [], scope);
|
|
766
|
+
}
|
|
767
|
+
if (!unresolved)
|
|
423
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}`);
|
|
424
793
|
}
|
|
425
|
-
keys.push(
|
|
426
|
-
|
|
794
|
+
keys.push(merged);
|
|
795
|
+
merges[signature] = merged;
|
|
427
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
|
+
});
|
|
428
809
|
return {
|
|
429
810
|
binding,
|
|
430
|
-
css:
|
|
811
|
+
css: css.length > 0 ? `${css.join('\n\n')}\n` : '',
|
|
431
812
|
globalCss: `${themes.globalCss}${animations.globalCss}`,
|
|
432
813
|
keys,
|
|
433
814
|
functions,
|
|
815
|
+
aliases,
|
|
816
|
+
merges,
|
|
817
|
+
overrides,
|
|
818
|
+
definitionOnly: creates.every((create) => !bindsElsewhere(ast, create.binding, create.declaration.id)),
|
|
434
819
|
reports,
|
|
435
820
|
};
|
|
436
821
|
}
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import type { Rule } from 'eslint';
|
|
2
2
|
export interface ReleaseModule {
|
|
3
3
|
source: string;
|
|
4
|
+
target?: string;
|
|
4
5
|
binding: string;
|
|
6
|
+
definitionOnly?: boolean;
|
|
7
|
+
aliases?: Record<string, Record<string, string>>;
|
|
5
8
|
functions?: Record<string, {
|
|
6
9
|
params: string[];
|
|
7
10
|
variables: string[];
|
|
8
11
|
}>;
|
|
12
|
+
merges?: Record<string, string>;
|
|
13
|
+
overrides?: Record<string, Record<number, string>>;
|
|
9
14
|
}
|
|
10
15
|
export interface ReleaseStylesOptions {
|
|
11
16
|
modules: Record<string, ReleaseModule>;
|
|
12
17
|
themes?: Record<string, string[]>;
|
|
13
18
|
animations?: Record<string, string[]>;
|
|
19
|
+
statics?: Record<string, string[]>;
|
|
20
|
+
values?: Record<string, Record<string, unknown>>;
|
|
21
|
+
complete?: boolean;
|
|
14
22
|
styleProp?: string;
|
|
15
23
|
active?: boolean;
|
|
16
24
|
}
|