@plumeria/codemod 18.2.25 → 18.2.27
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/dist/cascade.d.ts +14 -0
- package/dist/cascade.js +57 -0
- package/dist/cli.js +31 -1
- package/dist/migrate.d.ts +1 -1
- package/dist/migrate.js +8 -1
- package/dist/transforms/adopt-styles.d.ts +11 -0
- package/dist/transforms/adopt-styles.js +219 -1
- package/dist/transforms/css-modules.d.ts +12 -0
- package/dist/transforms/css-modules.js +201 -56
- package/dist/transforms/from-plumeria.js +8 -43
- package/package.json +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const depthOf: (property: string) => number;
|
|
2
|
+
export declare const rankOf: (property: string, conditional: boolean) => number;
|
|
3
|
+
export declare const coverageOf: (property: string) => Set<string>;
|
|
4
|
+
export declare const overlaps: (first: string, second: string) => boolean;
|
|
5
|
+
export interface Held {
|
|
6
|
+
property: string;
|
|
7
|
+
suffix: string;
|
|
8
|
+
rank: number;
|
|
9
|
+
place: number;
|
|
10
|
+
}
|
|
11
|
+
export declare const unrepresentable: (left: Held[], right: Held[]) => {
|
|
12
|
+
earlier: Held;
|
|
13
|
+
later: Held;
|
|
14
|
+
} | undefined;
|
package/dist/cascade.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.unrepresentable = exports.overlaps = exports.coverageOf = exports.rankOf = exports.depthOf = void 0;
|
|
4
|
+
const zss_engine_1 = require("zss-engine");
|
|
5
|
+
const SHORTHANDS_OF = {};
|
|
6
|
+
for (const [shorthand, longhands] of Object.entries(zss_engine_1.DIRECT_LONGHANDS))
|
|
7
|
+
for (const longhand of longhands)
|
|
8
|
+
(SHORTHANDS_OF[longhand] ??= []).push(shorthand);
|
|
9
|
+
const depths = new Map();
|
|
10
|
+
const depthOf = (property) => {
|
|
11
|
+
const cached = depths.get(property);
|
|
12
|
+
if (cached !== undefined)
|
|
13
|
+
return cached;
|
|
14
|
+
depths.set(property, 0);
|
|
15
|
+
let depth = 0;
|
|
16
|
+
for (const shorthand of SHORTHANDS_OF[property] ?? [])
|
|
17
|
+
depth = Math.max(depth, (0, exports.depthOf)(shorthand) + 1);
|
|
18
|
+
depths.set(property, depth);
|
|
19
|
+
return depth;
|
|
20
|
+
};
|
|
21
|
+
exports.depthOf = depthOf;
|
|
22
|
+
const rankOf = (property, conditional) => property.startsWith('--') ? 0 : 1 + (conditional ? 1 : 0) + (0, exports.depthOf)(property);
|
|
23
|
+
exports.rankOf = rankOf;
|
|
24
|
+
const coverages = new Map();
|
|
25
|
+
const coverageOf = (property) => {
|
|
26
|
+
const cached = coverages.get(property);
|
|
27
|
+
if (cached)
|
|
28
|
+
return cached;
|
|
29
|
+
const longhands = zss_engine_1.DIRECT_LONGHANDS[property];
|
|
30
|
+
const coverage = longhands
|
|
31
|
+
? new Set(longhands.flatMap((longhand) => [...(0, exports.coverageOf)(longhand)]))
|
|
32
|
+
: new Set([property]);
|
|
33
|
+
coverages.set(property, coverage);
|
|
34
|
+
return coverage;
|
|
35
|
+
};
|
|
36
|
+
exports.coverageOf = coverageOf;
|
|
37
|
+
const overlaps = (first, second) => {
|
|
38
|
+
const left = (0, exports.coverageOf)(first);
|
|
39
|
+
return [...(0, exports.coverageOf)(second)].some((leaf) => left.has(leaf));
|
|
40
|
+
};
|
|
41
|
+
exports.overlaps = overlaps;
|
|
42
|
+
const unrepresentable = (left, right) => {
|
|
43
|
+
for (const mine of left)
|
|
44
|
+
for (const theirs of right) {
|
|
45
|
+
if (mine.suffix !== theirs.suffix)
|
|
46
|
+
continue;
|
|
47
|
+
if (mine.place === theirs.place)
|
|
48
|
+
continue;
|
|
49
|
+
if (!(0, exports.overlaps)(mine.property, theirs.property))
|
|
50
|
+
continue;
|
|
51
|
+
const [earlier, later] = mine.place < theirs.place ? [mine, theirs] : [theirs, mine];
|
|
52
|
+
if (earlier.rank > later.rank)
|
|
53
|
+
return { earlier, later };
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
};
|
|
57
|
+
exports.unrepresentable = unrepresentable;
|
package/dist/cli.js
CHANGED
|
@@ -301,6 +301,36 @@ async function migrateFromCssModules(options) {
|
|
|
301
301
|
console.log('No *.module.css found.');
|
|
302
302
|
return 0;
|
|
303
303
|
}
|
|
304
|
+
const held = new Map();
|
|
305
|
+
const detected = await new eslint_1.ESLint({
|
|
306
|
+
overrideConfigFile: true,
|
|
307
|
+
overrideConfig: migrateConfig(modules),
|
|
308
|
+
errorOnUnmatchedPattern: false,
|
|
309
|
+
}).lintFiles(targets);
|
|
310
|
+
for (const result of detected)
|
|
311
|
+
for (const message of result.messages) {
|
|
312
|
+
if (message.messageId !== 'missing' && message.messageId !== 'cascade')
|
|
313
|
+
continue;
|
|
314
|
+
const [sheet, rest] = message.message.split(' :: ');
|
|
315
|
+
if (!modules[sheet])
|
|
316
|
+
continue;
|
|
317
|
+
const named = held.get(sheet) ?? new Set();
|
|
318
|
+
named.add(rest);
|
|
319
|
+
held.set(sheet, named);
|
|
320
|
+
}
|
|
321
|
+
for (const [sheet, named] of held) {
|
|
322
|
+
delete modules[sheet];
|
|
323
|
+
const listed = [...named].map((name) => `\`${name}\``).join(', ');
|
|
324
|
+
stylesheets
|
|
325
|
+
.find((s) => s.source === sheet)
|
|
326
|
+
?.reports.push({
|
|
327
|
+
line: 0,
|
|
328
|
+
column: 0,
|
|
329
|
+
kind: 'held-for-consumer',
|
|
330
|
+
source: '',
|
|
331
|
+
hint: `${listed} ${named.size === 1 ? 'is' : 'are'} read by a consumer the migration cannot answer for, so nothing was written and no consumer was moved. Settle the rules above and run again.`,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
304
334
|
for (const sheet of stylesheets) {
|
|
305
335
|
if (!modules[sheet.source])
|
|
306
336
|
continue;
|
|
@@ -320,7 +350,7 @@ async function migrateFromCssModules(options) {
|
|
|
320
350
|
}
|
|
321
351
|
else {
|
|
322
352
|
warnIfDirty();
|
|
323
|
-
(0, migrate_1.write)(targets);
|
|
353
|
+
(0, migrate_1.write)(targets, new Set(held.keys()));
|
|
324
354
|
const applied = await new eslint_1.ESLint({ ...config, fix: true }).lintFiles(targets);
|
|
325
355
|
await eslint_1.ESLint.outputFixes(applied);
|
|
326
356
|
const touched = applied.filter((r) => r.output !== undefined);
|
package/dist/migrate.d.ts
CHANGED
|
@@ -12,5 +12,5 @@ export interface Plan {
|
|
|
12
12
|
export declare function findStylesheets(targets: string[]): string[];
|
|
13
13
|
export declare const targetPath: (stylesheet: string) => string;
|
|
14
14
|
export declare function plan(targets: string[]): Plan;
|
|
15
|
-
export declare function write(targets: string[]): Plan;
|
|
15
|
+
export declare function write(targets: string[], skip?: Set<string>): Plan;
|
|
16
16
|
export declare function formatReports(sheets: Stylesheet[], cwd: string): string[];
|
package/dist/migrate.js
CHANGED
|
@@ -103,18 +103,25 @@ function plan(targets) {
|
|
|
103
103
|
modules[source] = {
|
|
104
104
|
source: `./${path.basename(target, '.ts')}`,
|
|
105
105
|
target,
|
|
106
|
+
stylesheet: source,
|
|
106
107
|
names: converted.names,
|
|
108
|
+
order: converted.order,
|
|
109
|
+
held: converted.held,
|
|
107
110
|
composes: converted.composes,
|
|
108
111
|
functions: converted.functions,
|
|
112
|
+
tags: converted.tags,
|
|
113
|
+
unconvertible: converted.unconvertible,
|
|
109
114
|
};
|
|
110
115
|
}
|
|
111
116
|
return { stylesheets, modules };
|
|
112
117
|
}
|
|
113
|
-
function write(targets) {
|
|
118
|
+
function write(targets, skip) {
|
|
114
119
|
const result = plan(targets);
|
|
115
120
|
for (const sheet of result.stylesheets) {
|
|
116
121
|
if (!result.modules[sheet.source])
|
|
117
122
|
continue;
|
|
123
|
+
if (skip?.has(sheet.source))
|
|
124
|
+
continue;
|
|
118
125
|
const converted = (0, css_modules_1.convertStylesheet)(fs.readFileSync(sheet.source, 'utf-8'));
|
|
119
126
|
fs.writeFileSync(sheet.target, converted.code, 'utf-8');
|
|
120
127
|
}
|
|
@@ -3,8 +3,19 @@ export interface ModuleMap {
|
|
|
3
3
|
source: string;
|
|
4
4
|
target?: string;
|
|
5
5
|
names: Record<string, string>;
|
|
6
|
+
order?: Record<string, number>;
|
|
7
|
+
held?: Record<string, import('../cascade').Held[]>;
|
|
6
8
|
composes?: Record<string, string[]>;
|
|
7
9
|
functions?: Record<string, string[]>;
|
|
10
|
+
tags?: {
|
|
11
|
+
key: string;
|
|
12
|
+
tag: string;
|
|
13
|
+
under: string;
|
|
14
|
+
direct?: boolean;
|
|
15
|
+
order?: number;
|
|
16
|
+
}[];
|
|
17
|
+
stylesheet?: string;
|
|
18
|
+
unconvertible?: string[];
|
|
8
19
|
}
|
|
9
20
|
export interface AdoptStylesOptions {
|
|
10
21
|
modules: Record<string, ModuleMap>;
|
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.adoptStyles = void 0;
|
|
37
37
|
const path = __importStar(require("node:path"));
|
|
38
38
|
const resolve_1 = require("../resolve");
|
|
39
|
+
const cascade_1 = require("../cascade");
|
|
39
40
|
const CORE = '@plumeria/core';
|
|
40
41
|
const readOptions = (context) => {
|
|
41
42
|
const raw = (context.options[0] ?? {});
|
|
@@ -61,6 +62,9 @@ exports.adoptStyles = {
|
|
|
61
62
|
value: 'A style read as a value goes back through `css.use`.',
|
|
62
63
|
joined: 'A joined class list is written as an array.',
|
|
63
64
|
call: 'The custom properties are the arguments of a function style.',
|
|
65
|
+
tag: 'A rule reached this <{{tag}}> through a class; carry it as "{{key}}".',
|
|
66
|
+
missing: '{{stylesheet}} :: {{name}}',
|
|
67
|
+
cascade: '{{stylesheet}} :: {{name}}',
|
|
64
68
|
},
|
|
65
69
|
schema: [
|
|
66
70
|
{
|
|
@@ -76,6 +80,7 @@ exports.adoptStyles = {
|
|
|
76
80
|
create(context) {
|
|
77
81
|
const { modules, styleProp } = readOptions(context);
|
|
78
82
|
const bindings = new Map();
|
|
83
|
+
const tagged = new Set();
|
|
79
84
|
let hasCore = false;
|
|
80
85
|
let stylesTaken = false;
|
|
81
86
|
let coreLocal;
|
|
@@ -182,6 +187,61 @@ exports.adoptStyles = {
|
|
|
182
187
|
current.parent.callee?.type === 'MemberExpression' &&
|
|
183
188
|
current.parent.callee.property?.name === 'use');
|
|
184
189
|
};
|
|
190
|
+
const at = (element) => {
|
|
191
|
+
const read = styleRead(element);
|
|
192
|
+
const binding = bindings.get(read?.object?.name);
|
|
193
|
+
if (!binding)
|
|
194
|
+
return null;
|
|
195
|
+
const written = read.computed && read.property.type === 'Literal'
|
|
196
|
+
? String(read.property.value)
|
|
197
|
+
: !read.computed && read.property.type === 'Identifier'
|
|
198
|
+
? read.property.name
|
|
199
|
+
: null;
|
|
200
|
+
if (written === null)
|
|
201
|
+
return null;
|
|
202
|
+
const key = binding.entry.names[written] ?? written;
|
|
203
|
+
const place = binding.entry.order?.[key];
|
|
204
|
+
return place === undefined
|
|
205
|
+
? null
|
|
206
|
+
: {
|
|
207
|
+
module: binding.entry,
|
|
208
|
+
place,
|
|
209
|
+
key,
|
|
210
|
+
entry: binding.entry,
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
const inStylesheetOrder = (elements) => {
|
|
214
|
+
const sorted = [...elements];
|
|
215
|
+
const groups = new Map();
|
|
216
|
+
elements.forEach((element, index) => {
|
|
217
|
+
const found = at(element);
|
|
218
|
+
if (!found)
|
|
219
|
+
return;
|
|
220
|
+
const slots = groups.get(found.module) ?? [];
|
|
221
|
+
slots.push(index);
|
|
222
|
+
groups.set(found.module, slots);
|
|
223
|
+
});
|
|
224
|
+
for (const slots of groups.values()) {
|
|
225
|
+
const ordered = slots
|
|
226
|
+
.map((index) => elements[index])
|
|
227
|
+
.sort((a, b) => at(a).place - at(b).place);
|
|
228
|
+
slots.forEach((index, at_) => (sorted[index] = ordered[at_]));
|
|
229
|
+
}
|
|
230
|
+
return sorted;
|
|
231
|
+
};
|
|
232
|
+
const unsettled = (held) => held.flatMap((mine, index) => held.slice(index + 1).flatMap((theirs) => {
|
|
233
|
+
if (mine.entry !== theirs.entry)
|
|
234
|
+
return [];
|
|
235
|
+
const left = mine.entry.held?.[mine.key];
|
|
236
|
+
const right = theirs.entry.held?.[theirs.key];
|
|
237
|
+
if (!left || !right)
|
|
238
|
+
return [];
|
|
239
|
+
return (0, cascade_1.unrepresentable)(left, right) ? [mine, theirs] : [];
|
|
240
|
+
}));
|
|
241
|
+
const readsOf = (elements) => elements.flatMap((element) => {
|
|
242
|
+
const found = at(element);
|
|
243
|
+
return found ? [{ entry: found.entry, key: found.key }] : [];
|
|
244
|
+
});
|
|
185
245
|
return {
|
|
186
246
|
Program(program) {
|
|
187
247
|
hasCore = program.body.some((n) => n.type === 'ImportDeclaration' && n.source.value === CORE);
|
|
@@ -328,6 +388,16 @@ exports.adoptStyles = {
|
|
|
328
388
|
});
|
|
329
389
|
return;
|
|
330
390
|
}
|
|
391
|
+
const entry = binding.entry;
|
|
392
|
+
if (entry.stylesheet &&
|
|
393
|
+
(entry.unconvertible?.includes(written) || !(written in entry.names))) {
|
|
394
|
+
context.report({
|
|
395
|
+
node: node.property,
|
|
396
|
+
messageId: 'missing',
|
|
397
|
+
data: { stylesheet: entry.stylesheet, name: written },
|
|
398
|
+
});
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
331
401
|
const key = binding.entry.names[written] ?? written;
|
|
332
402
|
if (coreLocal && !stylingPosition(node) && !insideUse(node)) {
|
|
333
403
|
context.report({
|
|
@@ -422,6 +492,131 @@ exports.adoptStyles = {
|
|
|
422
492
|
],
|
|
423
493
|
});
|
|
424
494
|
},
|
|
495
|
+
JSXElement(node) {
|
|
496
|
+
const scoped = [];
|
|
497
|
+
for (const attribute of node.openingElement?.attributes ?? []) {
|
|
498
|
+
if (attribute.type !== 'JSXAttribute')
|
|
499
|
+
continue;
|
|
500
|
+
const name = attribute.name?.name;
|
|
501
|
+
if (name !== 'className' && name !== styleProp)
|
|
502
|
+
continue;
|
|
503
|
+
if (attribute.value?.type !== 'JSXExpressionContainer')
|
|
504
|
+
continue;
|
|
505
|
+
const reads = [];
|
|
506
|
+
const gather = (expression) => {
|
|
507
|
+
if (!expression || typeof expression !== 'object')
|
|
508
|
+
return;
|
|
509
|
+
if (expression.type === 'MemberExpression') {
|
|
510
|
+
reads.push(expression);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
if (expression.type === 'ArrayExpression')
|
|
514
|
+
expression.elements.forEach(gather);
|
|
515
|
+
else if (expression.type === 'TemplateLiteral')
|
|
516
|
+
expression.expressions.forEach(gather);
|
|
517
|
+
else if (expression.type === 'LogicalExpression') {
|
|
518
|
+
gather(expression.left);
|
|
519
|
+
gather(expression.right);
|
|
520
|
+
}
|
|
521
|
+
else if (expression.type === 'ConditionalExpression') {
|
|
522
|
+
gather(expression.consequent);
|
|
523
|
+
gather(expression.alternate);
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
gather(attribute.value.expression);
|
|
527
|
+
for (const read of reads) {
|
|
528
|
+
const name_ = read.object?.name;
|
|
529
|
+
const binding = bindings.get(name_) ??
|
|
530
|
+
[...bindings.values()].find((b) => b.local === name_);
|
|
531
|
+
if (!binding)
|
|
532
|
+
continue;
|
|
533
|
+
const written = read.property?.name ?? read.property?.value;
|
|
534
|
+
if (written == null)
|
|
535
|
+
continue;
|
|
536
|
+
const key = binding.entry.names[written] ?? written;
|
|
537
|
+
for (const pair of binding.entry.tags ?? [])
|
|
538
|
+
if (pair.under === key)
|
|
539
|
+
scoped.push({
|
|
540
|
+
tag: pair.tag,
|
|
541
|
+
key: pair.key,
|
|
542
|
+
local: binding.local,
|
|
543
|
+
direct: pair.direct === true,
|
|
544
|
+
order: pair.order ?? 0,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
if (!scoped.length)
|
|
549
|
+
return;
|
|
550
|
+
const walk = (children, depth) => {
|
|
551
|
+
for (const child of children ?? []) {
|
|
552
|
+
if (child.type !== 'JSXElement')
|
|
553
|
+
continue;
|
|
554
|
+
const opening = child.openingElement;
|
|
555
|
+
const named = opening?.name?.type === 'JSXIdentifier'
|
|
556
|
+
? opening.name.name
|
|
557
|
+
: null;
|
|
558
|
+
const attributes = opening?.attributes ?? [];
|
|
559
|
+
const already = attributes.find((attribute) => attribute.type === 'JSXAttribute' &&
|
|
560
|
+
attribute.name?.name === styleProp &&
|
|
561
|
+
attribute.value?.type === 'JSXExpressionContainer');
|
|
562
|
+
const pending = attributes.some((attribute) => attribute.type === 'JSXAttribute' &&
|
|
563
|
+
attribute.name?.name === 'className');
|
|
564
|
+
const written = already?.value.expression.type === 'ArrayExpression'
|
|
565
|
+
? already.value.expression.elements.filter(Boolean)
|
|
566
|
+
: [];
|
|
567
|
+
const carried = new Set(already
|
|
568
|
+
? already.value.expression.type === 'ArrayExpression'
|
|
569
|
+
? written.map((element) => context.sourceCode.getText(element))
|
|
570
|
+
: [context.sourceCode.getText(already.value.expression)]
|
|
571
|
+
: []);
|
|
572
|
+
for (const pair of scoped) {
|
|
573
|
+
if (named !== pair.tag || pending)
|
|
574
|
+
continue;
|
|
575
|
+
if (pair.direct && depth > 1)
|
|
576
|
+
continue;
|
|
577
|
+
const read = `${pair.local}.${pair.key}`;
|
|
578
|
+
if (carried.has(read))
|
|
579
|
+
continue;
|
|
580
|
+
if (tagged.has(opening))
|
|
581
|
+
continue;
|
|
582
|
+
tagged.add(opening);
|
|
583
|
+
const reads = new Map();
|
|
584
|
+
for (const { entry, local } of bindings.values())
|
|
585
|
+
for (const other of entry.tags ?? [])
|
|
586
|
+
reads.set(`${local}.${other.key}`, other.order ?? 0);
|
|
587
|
+
const placed = written.filter((element) => reads.has(context.sourceCode.getText(element)));
|
|
588
|
+
const before = placed.find((element) => (reads.get(context.sourceCode.getText(element)) ?? 0) >
|
|
589
|
+
pair.order);
|
|
590
|
+
const after = before ? undefined : placed[placed.length - 1];
|
|
591
|
+
context.report({
|
|
592
|
+
node: opening.name,
|
|
593
|
+
messageId: 'tag',
|
|
594
|
+
data: { tag: pair.tag, key: pair.key },
|
|
595
|
+
fix: (fixer) => {
|
|
596
|
+
if (!already)
|
|
597
|
+
return fixer.insertTextAfter(opening.name, ` ${styleProp}={${read}}`);
|
|
598
|
+
const expression = already.value.expression;
|
|
599
|
+
if (expression.type !== 'ArrayExpression') {
|
|
600
|
+
const held = context.sourceCode.getText(expression);
|
|
601
|
+
return fixer.replaceText(expression, reads.has(held)
|
|
602
|
+
? `[${held}, ${read}]`
|
|
603
|
+
: `[${read}, ${held}]`);
|
|
604
|
+
}
|
|
605
|
+
if (before)
|
|
606
|
+
return fixer.insertTextBefore(before, `${read}, `);
|
|
607
|
+
if (after)
|
|
608
|
+
return fixer.insertTextAfter(after, `, ${read}`);
|
|
609
|
+
return written.length === 0
|
|
610
|
+
? fixer.replaceText(expression, `[${read}]`)
|
|
611
|
+
: fixer.insertTextBefore(written[0], `${read}, `);
|
|
612
|
+
},
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
walk(child.children, depth + 1);
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
walk(node.children, 1);
|
|
619
|
+
},
|
|
425
620
|
JSXAttribute(node) {
|
|
426
621
|
if (node.name.type !== 'JSXIdentifier')
|
|
427
622
|
return;
|
|
@@ -437,7 +632,18 @@ exports.adoptStyles = {
|
|
|
437
632
|
return;
|
|
438
633
|
const styles_ = joined.filter((element) => bindings.has(styleRead(element)?.object?.name));
|
|
439
634
|
const carried = joined.filter((element) => !styles_.includes(element));
|
|
440
|
-
const
|
|
635
|
+
const beyond = unsettled(readsOf(styles_));
|
|
636
|
+
if (beyond.length > 0) {
|
|
637
|
+
for (const { entry, key } of beyond)
|
|
638
|
+
if (entry.stylesheet)
|
|
639
|
+
context.report({
|
|
640
|
+
node,
|
|
641
|
+
messageId: 'cascade',
|
|
642
|
+
data: { stylesheet: entry.stylesheet, name: key },
|
|
643
|
+
});
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
const list = inStylesheetOrder(styles_).map(renamedText).join(', ');
|
|
441
647
|
if (carried.length > 0) {
|
|
442
648
|
const kept = carried
|
|
443
649
|
.map((element) => context.sourceCode.getText(element))
|
|
@@ -496,8 +702,20 @@ exports.adoptStyles = {
|
|
|
496
702
|
const composed = key ? entry.composes?.[key] : undefined;
|
|
497
703
|
if (!composed || composed.length === 0)
|
|
498
704
|
return;
|
|
705
|
+
const beyond = unsettled(composed.concat(key).map((k) => ({ entry, key: k })));
|
|
706
|
+
if (beyond.length > 0) {
|
|
707
|
+
for (const held of beyond)
|
|
708
|
+
if (held.entry.stylesheet)
|
|
709
|
+
context.report({
|
|
710
|
+
node: expression,
|
|
711
|
+
messageId: 'cascade',
|
|
712
|
+
data: { stylesheet: held.entry.stylesheet, name: held.key },
|
|
713
|
+
});
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
499
716
|
const parts = composed
|
|
500
717
|
.concat(key)
|
|
718
|
+
.sort((a, b) => (entry.order?.[a] ?? 0) - (entry.order?.[b] ?? 0))
|
|
501
719
|
.map((k) => `${local}.${k}`)
|
|
502
720
|
.join(', ');
|
|
503
721
|
context.report({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Held } from '../cascade';
|
|
1
2
|
export interface Report {
|
|
2
3
|
line: number;
|
|
3
4
|
column: number;
|
|
@@ -10,8 +11,19 @@ export interface Converted {
|
|
|
10
11
|
names: Record<string, string>;
|
|
11
12
|
composes: Record<string, string[]>;
|
|
12
13
|
functions: Record<string, string[]>;
|
|
14
|
+
tags: Tags[];
|
|
15
|
+
held: Record<string, Held[]>;
|
|
16
|
+
order: Record<string, number>;
|
|
17
|
+
unconvertible: string[];
|
|
13
18
|
reports: Report[];
|
|
14
19
|
}
|
|
20
|
+
export interface Tags {
|
|
21
|
+
key: string;
|
|
22
|
+
tag: string;
|
|
23
|
+
under: string;
|
|
24
|
+
direct: boolean;
|
|
25
|
+
order: number;
|
|
26
|
+
}
|
|
15
27
|
interface Raw {
|
|
16
28
|
raw: string;
|
|
17
29
|
}
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.toValue = exports.toKey = exports.toProperty = void 0;
|
|
7
7
|
exports.convertStylesheet = convertStylesheet;
|
|
8
8
|
const postcss_1 = __importDefault(require("postcss"));
|
|
9
|
+
const cascade_1 = require("../cascade");
|
|
9
10
|
const postcss_selector_parser_1 = __importDefault(require("postcss-selector-parser"));
|
|
10
11
|
const DOC = 'https://plumeria.dev/docs/api-reference/javascript/marker';
|
|
11
12
|
const emptyNode = () => ({ decls: [], nested: [], markers: [] });
|
|
@@ -33,6 +34,7 @@ const toValue = (raw) => {
|
|
|
33
34
|
return value;
|
|
34
35
|
};
|
|
35
36
|
exports.toValue = toValue;
|
|
37
|
+
const last = (names) => names[names.length - 1];
|
|
36
38
|
const nest = (node, key) => {
|
|
37
39
|
const found = node.nested.find(([k]) => k === key);
|
|
38
40
|
if (found)
|
|
@@ -59,51 +61,43 @@ const readSelector = (selector) => {
|
|
|
59
61
|
};
|
|
60
62
|
(0, postcss_selector_parser_1.default)((root) => {
|
|
61
63
|
const nodes = root.first.nodes.filter((n) => n.type !== 'string' && !isSpecificityBoost(n));
|
|
62
|
-
const parts = [
|
|
63
|
-
{ pseudos: [] },
|
|
64
|
-
];
|
|
64
|
+
const parts = [{ classNames: [], pseudos: [] }];
|
|
65
65
|
let combinator = null;
|
|
66
66
|
let bad = null;
|
|
67
67
|
for (const node of nodes) {
|
|
68
68
|
if (node.type === 'class') {
|
|
69
|
-
|
|
70
|
-
if (current.className) {
|
|
71
|
-
bad = {
|
|
72
|
-
kind: 'unsupported-selector',
|
|
73
|
-
hint: 'Two classes on one element are written as an array at the call site.',
|
|
74
|
-
};
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
77
|
-
current.className = node.value;
|
|
69
|
+
parts[parts.length - 1].classNames.push(node.value);
|
|
78
70
|
}
|
|
79
71
|
else if (node.type === 'pseudo') {
|
|
80
72
|
if (node.value === ':global') {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
73
|
+
const inner = String(node)
|
|
74
|
+
.slice(':global'.length)
|
|
75
|
+
.replace(/^\(|\)$/g, '');
|
|
76
|
+
if (!inner) {
|
|
77
|
+
bad = {
|
|
78
|
+
kind: 'global',
|
|
79
|
+
hint: "A global class is written as a selector key: `':is(.name *)'`.",
|
|
80
|
+
};
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
parts[parts.length - 1].global = inner;
|
|
84
|
+
continue;
|
|
86
85
|
}
|
|
87
86
|
parts[parts.length - 1].pseudos.push(String(node));
|
|
88
87
|
}
|
|
88
|
+
else if (node.type === 'tag') {
|
|
89
|
+
parts[parts.length - 1].tag = node.value;
|
|
90
|
+
}
|
|
91
|
+
else if (node.type === 'attribute') {
|
|
92
|
+
const attr = node;
|
|
93
|
+
if (attr.value != null && attr.quoteMark === "'")
|
|
94
|
+
attr.quoteMark = '"';
|
|
95
|
+
parts[parts.length - 1].pseudos.push(`:is(${String(node).trim()})`);
|
|
96
|
+
}
|
|
89
97
|
else if (node.type === 'combinator') {
|
|
90
98
|
const value = node.value.trim();
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
kind: 'sibling-combinator',
|
|
94
|
-
hint: `A marker carries no order. Write the relation as a selector key: ${DOC}`,
|
|
95
|
-
};
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
if (parts.length > 1) {
|
|
99
|
-
bad = {
|
|
100
|
-
kind: 'unsupported-selector',
|
|
101
|
-
hint: 'Only one level of nesting is converted.',
|
|
102
|
-
};
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
combinator = value === '>' ? '>' : ' ';
|
|
106
|
-
parts.push({ pseudos: [] });
|
|
99
|
+
combinator = value === '>' ? '>' : value === ' ' ? ' ' : value;
|
|
100
|
+
parts.push({ classNames: [], pseudos: [], after: combinator });
|
|
107
101
|
}
|
|
108
102
|
else {
|
|
109
103
|
bad = {
|
|
@@ -117,26 +111,64 @@ const readSelector = (selector) => {
|
|
|
117
111
|
result = bad;
|
|
118
112
|
return;
|
|
119
113
|
}
|
|
120
|
-
|
|
114
|
+
const globals = parts.filter((p) => p.global && !p.classNames.length);
|
|
115
|
+
if (globals.length) {
|
|
116
|
+
const rest = parts.filter((p) => !(p.global && !p.classNames.length));
|
|
117
|
+
if (rest.length !== 1 || !rest[0].classNames.length)
|
|
118
|
+
return;
|
|
119
|
+
const ancestors = globals.map((p) => `${p.global} `).join('');
|
|
120
|
+
result = {
|
|
121
|
+
target: {
|
|
122
|
+
key: (0, exports.toKey)(last(rest[0].classNames)),
|
|
123
|
+
pseudo: `:is(${ancestors}*)${rest[0].pseudos.join('')}`,
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (parts.some((p) => p.after === '+' || p.after === '~')) {
|
|
129
|
+
result = {
|
|
130
|
+
kind: 'sibling-combinator',
|
|
131
|
+
hint: `A container query reaches descendants, never siblings, so no marker states this. Whether an ordinal such as \`:not(:first-child)\` is equivalent depends on the markup — write the relation as a selector key: ${DOC}`,
|
|
132
|
+
};
|
|
121
133
|
return;
|
|
122
|
-
|
|
123
|
-
|
|
134
|
+
}
|
|
135
|
+
const tagged = parts[parts.length - 1];
|
|
136
|
+
if (parts.length > 1 &&
|
|
137
|
+
tagged.tag &&
|
|
138
|
+
!tagged.classNames.length &&
|
|
139
|
+
parts.slice(0, -1).every((p) => p.classNames.length)) {
|
|
140
|
+
const above = parts.slice(0, -1);
|
|
141
|
+
const relation = tagged.after === '>' ? 'child-' : '';
|
|
124
142
|
result = {
|
|
125
143
|
target: {
|
|
126
|
-
key: (0, exports.toKey)(
|
|
127
|
-
pseudo:
|
|
144
|
+
key: (0, exports.toKey)(`${last(above[above.length - 1].classNames)}-${relation}${tagged.tag}`),
|
|
145
|
+
pseudo: tagged.pseudos.join(''),
|
|
146
|
+
tag: tagged.tag,
|
|
147
|
+
direct: tagged.after === '>',
|
|
148
|
+
ancestors: above.map((part) => ({
|
|
149
|
+
key: (0, exports.toKey)(last(part.classNames)),
|
|
150
|
+
pseudo: part.pseudos.join('') || ':defined',
|
|
151
|
+
})),
|
|
128
152
|
},
|
|
129
153
|
};
|
|
130
154
|
return;
|
|
131
155
|
}
|
|
156
|
+
if (parts.some((p) => !p.classNames.length))
|
|
157
|
+
return;
|
|
158
|
+
const target = parts[parts.length - 1];
|
|
159
|
+
const above = parts.slice(0, -1);
|
|
132
160
|
result = {
|
|
133
161
|
target: {
|
|
134
|
-
key: (0, exports.toKey)(
|
|
135
|
-
pseudo:
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
162
|
+
key: (0, exports.toKey)(last(target.classNames)),
|
|
163
|
+
pseudo: target.pseudos.join(''),
|
|
164
|
+
...(above.length
|
|
165
|
+
? {
|
|
166
|
+
ancestors: above.map((part) => ({
|
|
167
|
+
key: (0, exports.toKey)(last(part.classNames)),
|
|
168
|
+
pseudo: part.pseudos.join('') || ':defined',
|
|
169
|
+
})),
|
|
170
|
+
}
|
|
171
|
+
: {}),
|
|
140
172
|
},
|
|
141
173
|
};
|
|
142
174
|
void combinator;
|
|
@@ -172,15 +204,26 @@ function convertStylesheet(css) {
|
|
|
172
204
|
const root = postcss_1.default.parse(css);
|
|
173
205
|
const keys = new Map();
|
|
174
206
|
const names = {};
|
|
207
|
+
const candidates = {};
|
|
175
208
|
const composes = {};
|
|
176
209
|
const reports = [];
|
|
177
210
|
const markerPseudos = new Map();
|
|
211
|
+
const tags = [];
|
|
212
|
+
const unconvertible = new Set();
|
|
213
|
+
const order = {};
|
|
214
|
+
const held = {};
|
|
215
|
+
let wrote = 0;
|
|
216
|
+
let seen = 0;
|
|
217
|
+
const written = [];
|
|
178
218
|
const keyOf = (name) => {
|
|
179
219
|
if (!keys.has(name))
|
|
180
220
|
keys.set(name, emptyNode());
|
|
181
221
|
return keys.get(name);
|
|
182
222
|
};
|
|
183
223
|
const report = (node, kind, hint) => {
|
|
224
|
+
const rule = node.type === 'rule' ? node : node.parent;
|
|
225
|
+
for (const found of rule?.selector?.match(/\.([A-Za-z0-9_-]+)/g) ?? [])
|
|
226
|
+
unconvertible.add(found.slice(1));
|
|
184
227
|
const start = node.source?.start;
|
|
185
228
|
reports.push({
|
|
186
229
|
line: start?.line ?? 0,
|
|
@@ -201,23 +244,48 @@ function convertStylesheet(css) {
|
|
|
201
244
|
for (const className of selector.match(/\.([A-Za-z0-9_-]+)/g) ?? []) {
|
|
202
245
|
const bare = className.slice(1);
|
|
203
246
|
const key = (0, exports.toKey)(bare);
|
|
204
|
-
const taken = Object.entries(
|
|
247
|
+
const taken = Object.entries(candidates).find(([other, k]) => k === key && other !== bare);
|
|
205
248
|
if (taken) {
|
|
206
249
|
report(rule, 'key-collision', `\`${bare}\` and \`${taken[0]}\` both become \`${key}\`.`);
|
|
207
250
|
}
|
|
208
|
-
|
|
251
|
+
candidates[bare] = key;
|
|
209
252
|
}
|
|
210
253
|
let node = keyOf(target.key);
|
|
211
|
-
if (target.
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
254
|
+
if (target.tag && target.ancestors) {
|
|
255
|
+
const under = target.ancestors[target.ancestors.length - 1].key;
|
|
256
|
+
if (!tags.some((t) => t.key === target.key &&
|
|
257
|
+
t.tag === target.tag &&
|
|
258
|
+
t.direct === (target.direct === true)))
|
|
259
|
+
tags.push({
|
|
260
|
+
key: target.key,
|
|
261
|
+
tag: target.tag,
|
|
262
|
+
under,
|
|
263
|
+
direct: target.direct === true,
|
|
264
|
+
order: tags.length,
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
if (target.ancestors) {
|
|
268
|
+
let gate = null;
|
|
269
|
+
for (const { key: id, pseudo } of target.ancestors) {
|
|
270
|
+
const signature = `${gate ?? ''}>${pseudo}`;
|
|
271
|
+
const seen = markerPseudos.get(id) ?? new Set();
|
|
272
|
+
if (!seen.has(signature)) {
|
|
273
|
+
seen.add(signature);
|
|
274
|
+
markerPseudos.set(id, seen);
|
|
275
|
+
const holder = gate ? nest(keyOf(id), gate) : keyOf(id);
|
|
276
|
+
holder.markers.push(`css.marker('${id}', '${pseudo}')`);
|
|
277
|
+
}
|
|
278
|
+
gate = `css.extended('${id}', '${pseudo}')`;
|
|
218
279
|
}
|
|
219
|
-
node = nest(node,
|
|
280
|
+
node = nest(node, gate);
|
|
220
281
|
}
|
|
282
|
+
const place = seen++;
|
|
283
|
+
order[target.key] = place;
|
|
284
|
+
const props = new Set();
|
|
285
|
+
const where = `${atRuleKeys(rule).join('|')}::${target.pseudo}`;
|
|
286
|
+
written.push({ key: target.key, place, props });
|
|
287
|
+
const conditional = atRuleKeys(rule).length > 0;
|
|
288
|
+
const mine = (held[target.key] ??= []);
|
|
221
289
|
for (const key of atRuleKeys(rule))
|
|
222
290
|
node = nest(node, key);
|
|
223
291
|
if (target.pseudo)
|
|
@@ -228,13 +296,20 @@ function convertStylesheet(css) {
|
|
|
228
296
|
if (child.prop === 'composes') {
|
|
229
297
|
const [name, from] = child.value.split(/\s+from\s+/);
|
|
230
298
|
if (from) {
|
|
231
|
-
report(child, 'composes-external', 'A class composed from another file is not resolved.');
|
|
299
|
+
report(child, 'composes-external', 'A class composed from another file is not resolved. Convert that stylesheet too, then pass both at the call site as an array.');
|
|
232
300
|
return;
|
|
233
301
|
}
|
|
234
302
|
composes[target.key] = (composes[target.key] ?? []).concat(name.split(/\s+/).map(exports.toKey));
|
|
235
303
|
return;
|
|
236
304
|
}
|
|
237
305
|
const property = (0, exports.toProperty)(child.prop);
|
|
306
|
+
props.add(`${where}::${property}`);
|
|
307
|
+
mine.push({
|
|
308
|
+
property: child.prop,
|
|
309
|
+
suffix: target.pseudo,
|
|
310
|
+
rank: (0, cascade_1.rankOf)(child.prop, conditional),
|
|
311
|
+
place: wrote++,
|
|
312
|
+
});
|
|
238
313
|
const held = node.decls.findIndex(([name]) => name === property);
|
|
239
314
|
if (held !== -1)
|
|
240
315
|
node.decls.splice(held, 1);
|
|
@@ -291,5 +366,75 @@ export const styles = css.create({
|
|
|
291
366
|
${body}
|
|
292
367
|
});
|
|
293
368
|
`;
|
|
294
|
-
|
|
369
|
+
for (const [key, declarations] of Object.entries(held)) {
|
|
370
|
+
const beaten = (0, cascade_1.unrepresentable)(declarations, declarations);
|
|
371
|
+
if (beaten) {
|
|
372
|
+
reports.push({
|
|
373
|
+
line: 0,
|
|
374
|
+
column: 0,
|
|
375
|
+
kind: 'rank-order',
|
|
376
|
+
source: '',
|
|
377
|
+
hint: `${beaten.earlier.property === beaten.later.property
|
|
378
|
+
? `\`${key}\` sets \`${beaten.later.property}\` after setting it where Plumeria ranks it higher — an at-rule is one step up.`
|
|
379
|
+
: `\`${key}\` sets \`${beaten.later.property}\` after \`${beaten.earlier.property}\`, which Plumeria ranks above it.`} CSS gives the property to the later declaration and Plumeria cannot, and one key has no order to settle that with. Write the higher-ranked declaration last.`,
|
|
380
|
+
});
|
|
381
|
+
for (const [name, candidate] of Object.entries(candidates))
|
|
382
|
+
if (candidate === key)
|
|
383
|
+
unconvertible.add(name);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
for (const entry of written) {
|
|
387
|
+
const earlier = written.filter((other) => other.key === entry.key && other.place < entry.place);
|
|
388
|
+
if (earlier.length === 0)
|
|
389
|
+
continue;
|
|
390
|
+
for (const between of written) {
|
|
391
|
+
if (between.key === entry.key)
|
|
392
|
+
continue;
|
|
393
|
+
if (between.place > entry.place)
|
|
394
|
+
continue;
|
|
395
|
+
const above = new Set(earlier
|
|
396
|
+
.filter((other) => other.place < between.place)
|
|
397
|
+
.flatMap((other) => [...other.props]));
|
|
398
|
+
const shared = [...between.props].filter((prop) => above.has(prop));
|
|
399
|
+
if (shared.length === 0)
|
|
400
|
+
continue;
|
|
401
|
+
reports.push({
|
|
402
|
+
line: 0,
|
|
403
|
+
column: 0,
|
|
404
|
+
kind: 'split-order',
|
|
405
|
+
source: '',
|
|
406
|
+
hint: `\`${entry.key}\` is written on both sides of \`${between.key}\` and they share a property, so one place in the array cannot say which wins. Write the class once.`,
|
|
407
|
+
});
|
|
408
|
+
for (const [bare, key] of Object.entries(candidates))
|
|
409
|
+
if (key === entry.key || key === between.key)
|
|
410
|
+
unconvertible.add(bare);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
for (const pair of tags) {
|
|
414
|
+
const taken = Object.entries(candidates).find(([, key]) => key === pair.key);
|
|
415
|
+
if (!taken)
|
|
416
|
+
continue;
|
|
417
|
+
reports.push({
|
|
418
|
+
line: 0,
|
|
419
|
+
column: 0,
|
|
420
|
+
kind: 'key-collision',
|
|
421
|
+
source: '',
|
|
422
|
+
hint: `\`${taken[0]}\` and the rule reaching \`${pair.tag}\` both become \`${pair.key}\`.`,
|
|
423
|
+
});
|
|
424
|
+
unconvertible.add(taken[0]);
|
|
425
|
+
}
|
|
426
|
+
for (const [bare, key] of Object.entries(candidates))
|
|
427
|
+
if (keys.has(key))
|
|
428
|
+
names[bare] = key;
|
|
429
|
+
return {
|
|
430
|
+
code,
|
|
431
|
+
names,
|
|
432
|
+
order,
|
|
433
|
+
held,
|
|
434
|
+
composes,
|
|
435
|
+
functions,
|
|
436
|
+
tags,
|
|
437
|
+
unconvertible: [...unconvertible],
|
|
438
|
+
reports,
|
|
439
|
+
};
|
|
295
440
|
}
|
|
@@ -6,57 +6,22 @@ exports.extractPlumeriaStatics = extractPlumeriaStatics;
|
|
|
6
6
|
exports.extractPlumeriaAnimations = extractPlumeriaAnimations;
|
|
7
7
|
exports.convertPlumeriaModule = convertPlumeriaModule;
|
|
8
8
|
const parser_1 = require("@typescript-eslint/parser");
|
|
9
|
+
const cascade_1 = require("../cascade");
|
|
9
10
|
const zss_engine_1 = require("zss-engine");
|
|
10
11
|
const UNITLESS = new Set(zss_engine_1.exceptionCamelCase.map((property) => (0, zss_engine_1.camelToKebabCase)(property)));
|
|
11
12
|
const compositionSignature = (parts) => parts.map((part) => `${part.target}#${part.className}`).join('|');
|
|
12
13
|
exports.compositionSignature = compositionSignature;
|
|
13
14
|
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
15
|
const covers = (outer, inner) => {
|
|
47
16
|
if (outer === inner)
|
|
48
17
|
return false;
|
|
49
|
-
const wide = coverageOf(outer);
|
|
50
|
-
const narrow = coverageOf(inner);
|
|
18
|
+
const wide = (0, cascade_1.coverageOf)(outer);
|
|
19
|
+
const narrow = (0, cascade_1.coverageOf)(inner);
|
|
51
20
|
return wide.size > narrow.size && [...narrow].every((leaf) => wide.has(leaf));
|
|
52
21
|
};
|
|
53
|
-
const overlaps = (first, second) => {
|
|
54
|
-
const left = coverageOf(first);
|
|
55
|
-
return [...coverageOf(second)].some((leaf) => left.has(leaf));
|
|
56
|
-
};
|
|
57
22
|
const byRank = (written) => {
|
|
58
23
|
const outranked = (later, earlier) => (later.held ?? []).some((mine) => (earlier.held ?? []).some((theirs) => later.suffix === earlier.suffix &&
|
|
59
|
-
overlaps(mine.property, theirs.property) &&
|
|
24
|
+
(0, cascade_1.overlaps)(mine.property, theirs.property) &&
|
|
60
25
|
mine.rank > theirs.rank));
|
|
61
26
|
const ordered = [];
|
|
62
27
|
const pending = [...written];
|
|
@@ -530,7 +495,7 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = [], o
|
|
|
530
495
|
for (const theirs of right) {
|
|
531
496
|
if (mine.suffix !== theirs.suffix)
|
|
532
497
|
continue;
|
|
533
|
-
if (!overlaps(mine.property, theirs.property))
|
|
498
|
+
if (!(0, cascade_1.overlaps)(mine.property, theirs.property))
|
|
534
499
|
continue;
|
|
535
500
|
if (mine.rank > theirs.rank)
|
|
536
501
|
ranked ??= [second, first];
|
|
@@ -618,7 +583,7 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = [], o
|
|
|
618
583
|
bucket: bucketOf(wrappers, suffix),
|
|
619
584
|
suffix,
|
|
620
585
|
property: cssProperty,
|
|
621
|
-
rank: depthOf(cssProperty) + (wrappers.length > 0 ? 1 : 0),
|
|
586
|
+
rank: (0, cascade_1.depthOf)(cssProperty) + (wrappers.length > 0 ? 1 : 0),
|
|
622
587
|
});
|
|
623
588
|
}
|
|
624
589
|
declared.push({
|
|
@@ -646,7 +611,7 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = [], o
|
|
|
646
611
|
if (phase !== 'nested')
|
|
647
612
|
pushRule(selector, byContainment(declared), declared.map((entry) => ({
|
|
648
613
|
property: entry.property,
|
|
649
|
-
rank: depthOf(entry.property) + (wrappers.length > 0 ? 1 : 0),
|
|
614
|
+
rank: (0, cascade_1.depthOf)(entry.property) + (wrappers.length > 0 ? 1 : 0),
|
|
650
615
|
})));
|
|
651
616
|
if (phase === 'declarations')
|
|
652
617
|
return;
|
|
@@ -846,7 +811,7 @@ function convertPlumeriaModule(source, externalValues = {}, compositions = [], o
|
|
|
846
811
|
for (const entry of mine) {
|
|
847
812
|
const crossed = theirs.some((candidate) => candidate.suffix === entry.suffix &&
|
|
848
813
|
candidate.rank === entry.rank &&
|
|
849
|
-
overlaps(entry.property, candidate.property));
|
|
814
|
+
(0, cascade_1.overlaps)(entry.property, candidate.property));
|
|
850
815
|
if (!crossed)
|
|
851
816
|
continue;
|
|
852
817
|
const held = disputed.get(entry.bucket) ?? new Set();
|