@tenphi/tasty 0.12.0 → 0.13.1
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/chunks/cacheKey.js +16 -8
- package/dist/chunks/cacheKey.js.map +1 -1
- package/dist/chunks/renderChunk.js +31 -32
- package/dist/chunks/renderChunk.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +6 -3
- package/dist/config.js.map +1 -1
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +3 -3
- package/dist/hooks/useStyles.js +4 -3
- package/dist/hooks/useStyles.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/dist/pipeline/index.d.ts +1 -1
- package/dist/pipeline/index.js +24 -14
- package/dist/pipeline/index.js.map +1 -1
- package/dist/pipeline/materialize.js +328 -79
- package/dist/pipeline/materialize.js.map +1 -1
- package/dist/pipeline/parseStateKey.d.ts +1 -1
- package/dist/pipeline/parseStateKey.js +2 -6
- package/dist/pipeline/parseStateKey.js.map +1 -1
- package/dist/states/index.js +10 -257
- package/dist/states/index.js.map +1 -1
- package/dist/tasty.d.ts +58 -58
- package/dist/tasty.js +23 -10
- package/dist/tasty.js.map +1 -1
- package/dist/utils/cache-wrapper.js +4 -8
- package/dist/utils/cache-wrapper.js.map +1 -1
- package/dist/utils/has-keys.js +13 -0
- package/dist/utils/has-keys.js.map +1 -0
- package/dist/utils/mod-attrs.js +1 -1
- package/dist/utils/styles.d.ts +1 -64
- package/dist/utils/styles.js +7 -319
- package/dist/utils/styles.js.map +1 -1
- package/dist/zero/babel.d.ts +8 -0
- package/dist/zero/babel.js +18 -3
- package/dist/zero/babel.js.map +1 -1
- package/dist/zero/next.js +14 -1
- package/dist/zero/next.js.map +1 -1
- package/docs/tasty-static.md +6 -7
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Lru } from "../parser/lru.js";
|
|
2
|
-
import { getConditionUniqueId
|
|
2
|
+
import { getConditionUniqueId } from "./conditions.js";
|
|
3
3
|
|
|
4
4
|
//#region src/pipeline/materialize.ts
|
|
5
5
|
/**
|
|
@@ -24,11 +24,12 @@ function emptyVariant() {
|
|
|
24
24
|
return {
|
|
25
25
|
modifierConditions: [],
|
|
26
26
|
pseudoConditions: [],
|
|
27
|
-
|
|
27
|
+
selectorGroups: [],
|
|
28
|
+
ownGroups: [],
|
|
28
29
|
mediaConditions: [],
|
|
29
30
|
containerConditions: [],
|
|
30
31
|
supportsConditions: [],
|
|
31
|
-
|
|
32
|
+
rootGroups: [],
|
|
32
33
|
parentGroups: [],
|
|
33
34
|
startingStyle: false
|
|
34
35
|
};
|
|
@@ -63,9 +64,9 @@ function stateToCSS(state) {
|
|
|
63
64
|
}),
|
|
64
65
|
isImpossible: false
|
|
65
66
|
};
|
|
66
|
-
case "root": return innerConditionToVariants(state.innerCondition, state.negated ?? false, "
|
|
67
|
+
case "root": return innerConditionToVariants(state.innerCondition, state.negated ?? false, "rootGroups");
|
|
67
68
|
case "parent": return parentConditionToVariants(state.innerCondition, state.negated ?? false, state.direct);
|
|
68
|
-
case "own": return innerConditionToVariants(state.innerCondition, state.negated ?? false, "
|
|
69
|
+
case "own": return innerConditionToVariants(state.innerCondition, state.negated ?? false, "ownGroups");
|
|
69
70
|
case "modifier": {
|
|
70
71
|
const v = emptyVariant();
|
|
71
72
|
v.modifierConditions.push(modifierToParsed(state));
|
|
@@ -264,31 +265,40 @@ function collectSelectorConditions(variant) {
|
|
|
264
265
|
return [...variant.modifierConditions, ...variant.pseudoConditions];
|
|
265
266
|
}
|
|
266
267
|
/**
|
|
267
|
-
* Convert an inner condition tree into
|
|
268
|
-
*
|
|
268
|
+
* Convert an inner condition tree into a single SelectorVariant with
|
|
269
|
+
* one SelectorGroup whose branches represent the inner OR alternatives.
|
|
269
270
|
* Shared by @root() and @own().
|
|
271
|
+
*
|
|
272
|
+
* Both positive and negated cases produce one variant with one group.
|
|
273
|
+
* Negation simply sets the `negated` flag, which swaps :is() for :not()
|
|
274
|
+
* in the final CSS output — no De Morgan transformation is needed.
|
|
275
|
+
*
|
|
276
|
+
* This mirrors parentConditionToVariants: OR branches are kept inside
|
|
277
|
+
* a single group and rendered as comma-separated arguments in
|
|
278
|
+
* :is()/:not(), e.g. :root:is([a], [b]) or [el]:not([a], [b]).
|
|
270
279
|
*/
|
|
271
280
|
function innerConditionToVariants(innerCondition, negated, target) {
|
|
272
|
-
const innerCSS = conditionToCSS(
|
|
281
|
+
const innerCSS = conditionToCSS(innerCondition);
|
|
273
282
|
if (innerCSS.isImpossible || innerCSS.variants.length === 0) return {
|
|
274
283
|
variants: [],
|
|
275
284
|
isImpossible: true
|
|
276
285
|
};
|
|
277
|
-
const
|
|
286
|
+
const branches = [];
|
|
278
287
|
for (const innerVariant of innerCSS.variants) {
|
|
279
288
|
const conditions = collectSelectorConditions(innerVariant);
|
|
280
|
-
if (conditions.length > 0)
|
|
281
|
-
const v = emptyVariant();
|
|
282
|
-
v[target].push(...conditions);
|
|
283
|
-
variants.push(v);
|
|
284
|
-
}
|
|
289
|
+
if (conditions.length > 0) branches.push(conditions);
|
|
285
290
|
}
|
|
286
|
-
if (
|
|
291
|
+
if (branches.length === 0) return {
|
|
287
292
|
variants: [emptyVariant()],
|
|
288
293
|
isImpossible: false
|
|
289
294
|
};
|
|
295
|
+
const v = emptyVariant();
|
|
296
|
+
v[target].push({
|
|
297
|
+
branches,
|
|
298
|
+
negated
|
|
299
|
+
});
|
|
290
300
|
return {
|
|
291
|
-
variants,
|
|
301
|
+
variants: [v],
|
|
292
302
|
isImpossible: false
|
|
293
303
|
};
|
|
294
304
|
}
|
|
@@ -327,29 +337,157 @@ function parentConditionToVariants(innerCondition, negated, direct) {
|
|
|
327
337
|
};
|
|
328
338
|
}
|
|
329
339
|
/**
|
|
330
|
-
*
|
|
340
|
+
* Sort key for canonical condition output within selectors.
|
|
341
|
+
*
|
|
342
|
+
* Priority order:
|
|
343
|
+
* 0: Boolean attribute selectors ([data-hovered])
|
|
344
|
+
* 1: Value attribute selectors ([data-size="small"])
|
|
345
|
+
* 2: Negated boolean attributes (:not([data-disabled]))
|
|
346
|
+
* 3: Negated value attributes (:not([data-size="small"]))
|
|
347
|
+
* 4: Pseudo-classes (:hover, :focus)
|
|
348
|
+
* 5: Negated pseudo-classes (:not(:disabled))
|
|
349
|
+
*
|
|
350
|
+
* Secondary sort: alphabetical by attribute name / pseudo string.
|
|
351
|
+
*/
|
|
352
|
+
function conditionSortKey(cond) {
|
|
353
|
+
if ("attribute" in cond) {
|
|
354
|
+
const hasValue = cond.value !== void 0 ? 1 : 0;
|
|
355
|
+
return `${(cond.negated ? 2 : 0) + hasValue}|${cond.attribute}|${cond.value ?? ""}`;
|
|
356
|
+
}
|
|
357
|
+
return `${cond.negated ? 5 : 4}|${cond.pseudo}`;
|
|
358
|
+
}
|
|
359
|
+
function sortConditions(conditions) {
|
|
360
|
+
return conditions.toSorted((a, b) => conditionSortKey(a).localeCompare(conditionSortKey(b)));
|
|
361
|
+
}
|
|
362
|
+
function branchToCSS(branch) {
|
|
363
|
+
let parts = "";
|
|
364
|
+
for (const cond of sortConditions(branch)) parts += selectorConditionToCSS(cond);
|
|
365
|
+
return parts;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Wrap serialized selector arguments in :is() or :not().
|
|
369
|
+
* Arguments are sorted for canonical output.
|
|
370
|
+
*/
|
|
371
|
+
function wrapInIsOrNot(args, negated) {
|
|
372
|
+
return `${negated ? ":not" : ":is"}(${args.sort().join(", ")})`;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Convert a selector group to a CSS selector fragment.
|
|
376
|
+
*
|
|
377
|
+
* Single-branch groups are unwrapped (no :is() wrapper).
|
|
378
|
+
* Multi-branch groups use :is() or :not().
|
|
379
|
+
* Negation swaps :is() for :not().
|
|
380
|
+
*/
|
|
381
|
+
function selectorGroupToCSS(group) {
|
|
382
|
+
if (group.branches.length === 0) return "";
|
|
383
|
+
if (group.branches.length === 1) {
|
|
384
|
+
const parts = branchToCSS(group.branches[0]);
|
|
385
|
+
if (group.negated) return `:not(${parts})`;
|
|
386
|
+
return parts;
|
|
387
|
+
}
|
|
388
|
+
return wrapInIsOrNot(group.branches.map(branchToCSS), group.negated);
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Collect facts about modifier conditions for subsumption analysis.
|
|
392
|
+
* Tracks negated boolean attrs (:not([attr])) and positive exact values ([attr="X"]).
|
|
331
393
|
*/
|
|
332
|
-
function
|
|
333
|
-
|
|
394
|
+
function collectSubsumptionFacts(modifiers) {
|
|
395
|
+
const negatedBooleanAttrs = /* @__PURE__ */ new Set();
|
|
396
|
+
const positiveExactValuesByAttr = /* @__PURE__ */ new Map();
|
|
397
|
+
for (const mod of modifiers) {
|
|
398
|
+
if (mod.negated && mod.value === void 0) negatedBooleanAttrs.add(mod.attribute);
|
|
399
|
+
if (!mod.negated && mod.value !== void 0 && (mod.operator ?? "=") === "=") {
|
|
400
|
+
let vals = positiveExactValuesByAttr.get(mod.attribute);
|
|
401
|
+
if (!vals) {
|
|
402
|
+
vals = /* @__PURE__ */ new Set();
|
|
403
|
+
positiveExactValuesByAttr.set(mod.attribute, vals);
|
|
404
|
+
}
|
|
405
|
+
vals.add(mod.value);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return {
|
|
409
|
+
negatedBooleanAttrs,
|
|
410
|
+
positiveExactValuesByAttr
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Check if a negated-value modifier is subsumed by stronger facts:
|
|
415
|
+
* - :not([attr]) subsumes :not([attr="val"])
|
|
416
|
+
* - [attr="X"] implies :not([attr="Y"]) is redundant (single exact value)
|
|
417
|
+
*
|
|
418
|
+
* Only applies to exact-match (=) operators; substring operators don't
|
|
419
|
+
* imply exclusivity between values.
|
|
420
|
+
*/
|
|
421
|
+
function isSubsumedNegatedModifier(mod, facts) {
|
|
422
|
+
if (!mod.negated || mod.value === void 0) return false;
|
|
423
|
+
if (facts.negatedBooleanAttrs.has(mod.attribute)) return true;
|
|
424
|
+
if ((mod.operator ?? "=") === "=") {
|
|
425
|
+
const posVals = facts.positiveExactValuesByAttr.get(mod.attribute);
|
|
426
|
+
if (posVals && posVals.size === 1 && !posVals.has(mod.value)) return true;
|
|
427
|
+
}
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Remove redundant single-condition groups that are subsumed by stronger
|
|
432
|
+
* groups on the same attribute. O(n) — only inspects single-branch,
|
|
433
|
+
* single-condition groups.
|
|
434
|
+
*/
|
|
435
|
+
function optimizeGroups(groups) {
|
|
436
|
+
if (groups.length <= 1) return groups;
|
|
437
|
+
const seen = /* @__PURE__ */ new Set();
|
|
438
|
+
const result = [];
|
|
439
|
+
for (const g of groups) {
|
|
440
|
+
const key = getSelectorGroupKey(g);
|
|
441
|
+
if (!seen.has(key)) {
|
|
442
|
+
seen.add(key);
|
|
443
|
+
result.push(g);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (result.length <= 1) return result;
|
|
447
|
+
const effectiveModifiers = [];
|
|
448
|
+
for (const g of result) {
|
|
449
|
+
if (g.branches.length !== 1 || g.branches[0].length !== 1) continue;
|
|
450
|
+
const cond = g.branches[0][0];
|
|
451
|
+
if (!("attribute" in cond)) continue;
|
|
452
|
+
effectiveModifiers.push({
|
|
453
|
+
...cond,
|
|
454
|
+
negated: g.negated !== cond.negated
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
const facts = collectSubsumptionFacts(effectiveModifiers);
|
|
458
|
+
if (facts.negatedBooleanAttrs.size === 0 && facts.positiveExactValuesByAttr.size === 0) return result;
|
|
459
|
+
return result.filter((g) => {
|
|
460
|
+
if (g.branches.length !== 1 || g.branches[0].length !== 1) return true;
|
|
461
|
+
const cond = g.branches[0][0];
|
|
462
|
+
if (!("attribute" in cond) || !g.negated || cond.negated || cond.value === void 0) return true;
|
|
463
|
+
return !isSubsumedNegatedModifier({
|
|
464
|
+
...cond,
|
|
465
|
+
negated: true
|
|
466
|
+
}, facts);
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Convert root groups to CSS selector prefix (for final output)
|
|
471
|
+
*/
|
|
472
|
+
function rootGroupsToCSS(groups) {
|
|
473
|
+
if (groups.length === 0) return void 0;
|
|
474
|
+
const optimized = optimizeGroups(groups);
|
|
475
|
+
if (optimized.length === 0) return void 0;
|
|
334
476
|
let prefix = ":root";
|
|
335
|
-
for (const
|
|
477
|
+
for (const group of optimized) prefix += selectorGroupToCSS(group);
|
|
336
478
|
return prefix;
|
|
337
479
|
}
|
|
338
480
|
/**
|
|
339
481
|
* Convert parent groups to CSS selector fragments (for final output).
|
|
340
|
-
* Each group produces its own :is() wrapper
|
|
482
|
+
* Each group produces its own :is()/:not() wrapper with a combinator
|
|
483
|
+
* suffix (` *` or ` > *`) appended to each branch.
|
|
341
484
|
*/
|
|
342
485
|
function parentGroupsToCSS(groups) {
|
|
343
486
|
let result = "";
|
|
344
487
|
for (const group of groups) {
|
|
345
488
|
const combinator = group.direct ? " > *" : " *";
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
for (const cond of branch) parts += selectorConditionToCSS(cond);
|
|
349
|
-
return parts + combinator;
|
|
350
|
-
});
|
|
351
|
-
const wrapper = group.negated ? ":not" : ":is";
|
|
352
|
-
result += `${wrapper}(${selectorArgs.join(", ")})`;
|
|
489
|
+
const args = group.branches.map((branch) => branchToCSS(branch) + combinator);
|
|
490
|
+
result += wrapInIsOrNot(args, group.negated);
|
|
353
491
|
}
|
|
354
492
|
return result;
|
|
355
493
|
}
|
|
@@ -385,7 +523,7 @@ function getSelectorConditionKey(cond) {
|
|
|
385
523
|
*/
|
|
386
524
|
function dedupeSelectorConditions(conditions) {
|
|
387
525
|
const seen = /* @__PURE__ */ new Set();
|
|
388
|
-
|
|
526
|
+
const result = [];
|
|
389
527
|
for (const c of conditions) {
|
|
390
528
|
const key = getSelectorConditionKey(c);
|
|
391
529
|
if (!seen.has(key)) {
|
|
@@ -393,30 +531,12 @@ function dedupeSelectorConditions(conditions) {
|
|
|
393
531
|
result.push(c);
|
|
394
532
|
}
|
|
395
533
|
}
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
if (!("attribute" in c))
|
|
400
|
-
|
|
401
|
-
const op = c.operator ?? "=";
|
|
402
|
-
if (!c.negated && c.value !== void 0 && op === "=") {
|
|
403
|
-
let values = positiveExactValuesByAttr.get(c.attribute);
|
|
404
|
-
if (!values) {
|
|
405
|
-
values = /* @__PURE__ */ new Set();
|
|
406
|
-
positiveExactValuesByAttr.set(c.attribute, values);
|
|
407
|
-
}
|
|
408
|
-
values.add(c.value);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
result = result.filter((c) => {
|
|
412
|
-
if (!("attribute" in c) || !c.negated || c.value === void 0) return true;
|
|
413
|
-
if (negatedBooleanAttrs.has(c.attribute)) return false;
|
|
414
|
-
if ((c.operator ?? "=") !== "=") return true;
|
|
415
|
-
const positiveValues = positiveExactValuesByAttr.get(c.attribute);
|
|
416
|
-
if (positiveValues !== void 0 && positiveValues.size === 1 && !positiveValues.has(c.value)) return false;
|
|
417
|
-
return true;
|
|
534
|
+
const facts = collectSubsumptionFacts(result.filter((c) => "attribute" in c));
|
|
535
|
+
if (facts.negatedBooleanAttrs.size === 0 && facts.positiveExactValuesByAttr.size === 0) return result;
|
|
536
|
+
return result.filter((c) => {
|
|
537
|
+
if (!("attribute" in c)) return true;
|
|
538
|
+
return !isSubsumedNegatedModifier(c, facts);
|
|
418
539
|
});
|
|
419
|
-
return result;
|
|
420
540
|
}
|
|
421
541
|
/**
|
|
422
542
|
* Check for modifier contradiction: same attribute with opposite negation
|
|
@@ -478,21 +598,37 @@ function hasParentGroupContradiction(groups) {
|
|
|
478
598
|
return false;
|
|
479
599
|
}
|
|
480
600
|
/**
|
|
601
|
+
* Check for selector group contradiction: same branches with opposite negation.
|
|
602
|
+
* E.g. :is([data-a]) and :not([data-a]) in the same variant is impossible.
|
|
603
|
+
*/
|
|
604
|
+
function hasSelectorGroupContradiction(groups) {
|
|
605
|
+
const byBaseKey = /* @__PURE__ */ new Map();
|
|
606
|
+
for (const g of groups) {
|
|
607
|
+
const baseKey = getBranchesKey(g.branches);
|
|
608
|
+
const existing = byBaseKey.get(baseKey);
|
|
609
|
+
if (existing !== void 0 && existing !== !g.negated) return true;
|
|
610
|
+
byBaseKey.set(baseKey, !g.negated);
|
|
611
|
+
}
|
|
612
|
+
return false;
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
481
615
|
* Merge two selector variants (AND operation)
|
|
482
616
|
* Deduplicates conditions and checks for contradictions
|
|
483
617
|
*/
|
|
484
618
|
function mergeVariants(a, b) {
|
|
485
619
|
const mergedMedia = dedupeMediaConditions([...a.mediaConditions, ...b.mediaConditions]);
|
|
486
620
|
if (hasMediaContradiction(mergedMedia)) return null;
|
|
487
|
-
const
|
|
488
|
-
if (
|
|
621
|
+
const mergedRootGroups = optimizeGroups([...a.rootGroups, ...b.rootGroups]);
|
|
622
|
+
if (hasSelectorGroupContradiction(mergedRootGroups)) return null;
|
|
489
623
|
const mergedModifiers = dedupeSelectorConditions([...a.modifierConditions, ...b.modifierConditions]);
|
|
490
624
|
const mergedPseudos = dedupeSelectorConditions([...a.pseudoConditions, ...b.pseudoConditions]);
|
|
491
625
|
if (hasSelectorConditionContradiction([...mergedModifiers, ...mergedPseudos])) return null;
|
|
626
|
+
const mergedSelectorGroups = optimizeGroups([...a.selectorGroups, ...b.selectorGroups]);
|
|
627
|
+
if (hasSelectorGroupContradiction(mergedSelectorGroups)) return null;
|
|
492
628
|
const mergedParentGroups = [...a.parentGroups, ...b.parentGroups];
|
|
493
629
|
if (hasParentGroupContradiction(mergedParentGroups)) return null;
|
|
494
|
-
const
|
|
495
|
-
if (
|
|
630
|
+
const mergedOwnGroups = optimizeGroups([...a.ownGroups, ...b.ownGroups]);
|
|
631
|
+
if (hasSelectorGroupContradiction(mergedOwnGroups)) return null;
|
|
496
632
|
const mergedContainers = dedupeContainerConditions([...a.containerConditions, ...b.containerConditions]);
|
|
497
633
|
if (hasContainerStyleContradiction(mergedContainers)) return null;
|
|
498
634
|
const mergedSupports = dedupeSupportsConditions([...a.supportsConditions, ...b.supportsConditions]);
|
|
@@ -500,11 +636,12 @@ function mergeVariants(a, b) {
|
|
|
500
636
|
return {
|
|
501
637
|
modifierConditions: mergedModifiers,
|
|
502
638
|
pseudoConditions: mergedPseudos,
|
|
503
|
-
|
|
639
|
+
selectorGroups: mergedSelectorGroups,
|
|
640
|
+
ownGroups: mergedOwnGroups,
|
|
504
641
|
mediaConditions: mergedMedia,
|
|
505
642
|
containerConditions: mergedContainers,
|
|
506
643
|
supportsConditions: mergedSupports,
|
|
507
|
-
|
|
644
|
+
rootGroups: mergedRootGroups,
|
|
508
645
|
parentGroups: mergedParentGroups,
|
|
509
646
|
startingStyle: a.startingStyle || b.startingStyle
|
|
510
647
|
};
|
|
@@ -635,32 +772,43 @@ const variantKeyCache = /* @__PURE__ */ new WeakMap();
|
|
|
635
772
|
* Cached via WeakMap since variants are compared multiple times during
|
|
636
773
|
* deduplication and sorting.
|
|
637
774
|
*/
|
|
638
|
-
function
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
ownKey,
|
|
775
|
+
function getSelectorGroupKey(g) {
|
|
776
|
+
return `${g.negated ? "!" : ""}(${getBranchesKey(g.branches)})`;
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Get a context key for a variant — everything except flat modifier/pseudo
|
|
780
|
+
* conditions. Variants with the same context key can be merged into an
|
|
781
|
+
* :is() group. Also used by getVariantKey as the shared non-selector portion.
|
|
782
|
+
*/
|
|
783
|
+
function getVariantContextKey(v) {
|
|
784
|
+
return [
|
|
649
785
|
v.mediaConditions.map((c) => `${c.subtype}:${c.negated ? "!" : ""}${c.condition}`).sort().join("|"),
|
|
650
|
-
|
|
786
|
+
v.containerConditions.map((c) => `${c.name ?? ""}:${c.negated ? "!" : ""}${c.condition}`).sort().join("|"),
|
|
651
787
|
v.supportsConditions.map((c) => `${c.subtype}:${c.negated ? "!" : ""}${c.condition}`).sort().join("|"),
|
|
652
|
-
v.
|
|
788
|
+
v.rootGroups.map(getSelectorGroupKey).sort().join("|"),
|
|
653
789
|
v.parentGroups.map(getParentGroupKey).sort().join("|"),
|
|
790
|
+
v.ownGroups.map(getSelectorGroupKey).sort().join("|"),
|
|
791
|
+
v.selectorGroups.map(getSelectorGroupKey).sort().join("|"),
|
|
654
792
|
v.startingStyle ? "1" : "0"
|
|
655
793
|
].join("###");
|
|
794
|
+
}
|
|
795
|
+
function getVariantKey(v) {
|
|
796
|
+
const cached = variantKeyCache.get(v);
|
|
797
|
+
if (cached !== void 0) return cached;
|
|
798
|
+
const modifierKey = v.modifierConditions.map(getModifierKey).sort().join("|");
|
|
799
|
+
const pseudoKey = v.pseudoConditions.map(getPseudoKey).sort().join("|");
|
|
800
|
+
const key = modifierKey + "###" + pseudoKey + "###" + getVariantContextKey(v);
|
|
656
801
|
variantKeyCache.set(v, key);
|
|
657
802
|
return key;
|
|
658
803
|
}
|
|
659
804
|
/**
|
|
660
805
|
* Total number of leaf conditions in a variant (for superset / dedup comparisons).
|
|
661
806
|
*/
|
|
807
|
+
function groupConditionCount(groups) {
|
|
808
|
+
return groups.reduce((sum, g) => sum + g.branches.reduce((s, b) => s + b.length, 0), 0);
|
|
809
|
+
}
|
|
662
810
|
function variantConditionCount(v) {
|
|
663
|
-
return v.modifierConditions.length + v.pseudoConditions.length + v.
|
|
811
|
+
return v.modifierConditions.length + v.pseudoConditions.length + groupConditionCount(v.selectorGroups) + groupConditionCount(v.ownGroups) + v.mediaConditions.length + v.containerConditions.length + v.supportsConditions.length + groupConditionCount(v.rootGroups) + groupConditionCount(v.parentGroups);
|
|
664
812
|
}
|
|
665
813
|
/**
|
|
666
814
|
* Check if variant A is a superset of variant B (A is more restrictive)
|
|
@@ -675,13 +823,14 @@ function variantConditionCount(v) {
|
|
|
675
823
|
*/
|
|
676
824
|
function isVariantSuperset(a, b) {
|
|
677
825
|
if (a.startingStyle !== b.startingStyle) return false;
|
|
678
|
-
if (!
|
|
826
|
+
if (!isSelectorGroupsSuperset(a.rootGroups, b.rootGroups)) return false;
|
|
679
827
|
if (!isMediaConditionsSuperset(a.mediaConditions, b.mediaConditions)) return false;
|
|
680
828
|
if (!isContainerConditionsSuperset(a.containerConditions, b.containerConditions)) return false;
|
|
681
829
|
if (!isSupportsConditionsSuperset(a.supportsConditions, b.supportsConditions)) return false;
|
|
682
830
|
if (!isModifierConditionsSuperset(a.modifierConditions, b.modifierConditions)) return false;
|
|
683
831
|
if (!isPseudoConditionsSuperset(a.pseudoConditions, b.pseudoConditions)) return false;
|
|
684
|
-
if (!
|
|
832
|
+
if (!isSelectorGroupsSuperset(a.selectorGroups, b.selectorGroups)) return false;
|
|
833
|
+
if (!isSelectorGroupsSuperset(a.ownGroups, b.ownGroups)) return false;
|
|
685
834
|
if (!isParentGroupsSuperset(a.parentGroups, b.parentGroups)) return false;
|
|
686
835
|
return variantConditionCount(a) > variantConditionCount(b);
|
|
687
836
|
}
|
|
@@ -707,8 +856,9 @@ function isModifierConditionsSuperset(a, b) {
|
|
|
707
856
|
function isPseudoConditionsSuperset(a, b) {
|
|
708
857
|
return isConditionsSuperset(a, b, getPseudoKey);
|
|
709
858
|
}
|
|
710
|
-
function
|
|
711
|
-
|
|
859
|
+
function isSelectorGroupsSuperset(a, b) {
|
|
860
|
+
if (a.length < b.length) return false;
|
|
861
|
+
return isConditionsSuperset(a, b, getSelectorGroupKey);
|
|
712
862
|
}
|
|
713
863
|
/**
|
|
714
864
|
* Check if parent groups A is a superset of B.
|
|
@@ -729,6 +879,7 @@ function getParentGroupKey(g) {
|
|
|
729
879
|
* 2. Superset variants (more restrictive selectors that are redundant)
|
|
730
880
|
*/
|
|
731
881
|
function dedupeVariants(variants) {
|
|
882
|
+
if (variants.length <= 1) return variants;
|
|
732
883
|
const seen = /* @__PURE__ */ new Set();
|
|
733
884
|
const result = [];
|
|
734
885
|
for (const v of variants) {
|
|
@@ -738,6 +889,7 @@ function dedupeVariants(variants) {
|
|
|
738
889
|
result.push(v);
|
|
739
890
|
}
|
|
740
891
|
}
|
|
892
|
+
if (result.length <= 1) return result;
|
|
741
893
|
result.sort((a, b) => variantConditionCount(a) - variantConditionCount(b));
|
|
742
894
|
const filtered = [];
|
|
743
895
|
for (const candidate of result) {
|
|
@@ -787,8 +939,8 @@ function andToCSS(children) {
|
|
|
787
939
|
* Combine OR conditions into CSS
|
|
788
940
|
*
|
|
789
941
|
* OR in CSS means multiple selector variants (DNF).
|
|
790
|
-
*
|
|
791
|
-
*
|
|
942
|
+
* After deduplication, variants that differ only in their base
|
|
943
|
+
* modifier/pseudo conditions are merged into :is() groups.
|
|
792
944
|
*
|
|
793
945
|
* Note: OR exclusivity is handled at the pipeline level (expandOrConditions),
|
|
794
946
|
* so here we just collect all variants. Any remaining ORs in the condition
|
|
@@ -811,6 +963,103 @@ function orToCSS(children) {
|
|
|
811
963
|
};
|
|
812
964
|
}
|
|
813
965
|
/**
|
|
966
|
+
* Find keys present in ALL condition arrays.
|
|
967
|
+
*/
|
|
968
|
+
function findCommonKeys(conditionSets, getKey) {
|
|
969
|
+
if (conditionSets.length === 0) return /* @__PURE__ */ new Set();
|
|
970
|
+
const common = new Set(conditionSets[0].map(getKey));
|
|
971
|
+
for (let i = 1; i < conditionSets.length; i++) {
|
|
972
|
+
const keys = new Set(conditionSets[i].map(getKey));
|
|
973
|
+
for (const key of common) if (!keys.has(key)) common.delete(key);
|
|
974
|
+
}
|
|
975
|
+
return common;
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Merge OR variants that share the same "context" (at-rules, root, parent,
|
|
979
|
+
* own, starting) into a single variant with a SelectorGroup.
|
|
980
|
+
*
|
|
981
|
+
* Variants with no modifier/pseudo conditions are kept separate (they match
|
|
982
|
+
* unconditionally and can't be expressed inside :is()).
|
|
983
|
+
*/
|
|
984
|
+
function mergeVariantsIntoSelectorGroups(variants) {
|
|
985
|
+
if (variants.length <= 1) return variants;
|
|
986
|
+
const groups = /* @__PURE__ */ new Map();
|
|
987
|
+
for (const v of variants) {
|
|
988
|
+
const key = getVariantContextKey(v);
|
|
989
|
+
const group = groups.get(key);
|
|
990
|
+
if (group) group.push(v);
|
|
991
|
+
else groups.set(key, [v]);
|
|
992
|
+
}
|
|
993
|
+
const result = [];
|
|
994
|
+
for (const group of groups.values()) {
|
|
995
|
+
if (group.length === 1) {
|
|
996
|
+
result.push(group[0]);
|
|
997
|
+
continue;
|
|
998
|
+
}
|
|
999
|
+
const withSelectors = [];
|
|
1000
|
+
const withoutSelectors = [];
|
|
1001
|
+
for (const v of group) if (v.modifierConditions.length === 0 && v.pseudoConditions.length === 0) withoutSelectors.push(v);
|
|
1002
|
+
else withSelectors.push(v);
|
|
1003
|
+
result.push(...withoutSelectors);
|
|
1004
|
+
if (withSelectors.length <= 1) {
|
|
1005
|
+
result.push(...withSelectors);
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
result.push(factorAndGroup(withSelectors));
|
|
1009
|
+
}
|
|
1010
|
+
return result;
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Factor common modifier/pseudo conditions out of variants and create
|
|
1014
|
+
* a single variant with a SelectorGroup for the remaining (differing)
|
|
1015
|
+
* conditions.
|
|
1016
|
+
*
|
|
1017
|
+
* Precondition: all variants must share the same context key (identical
|
|
1018
|
+
* at-rules, root/parent/own/selector groups, startingStyle).
|
|
1019
|
+
*/
|
|
1020
|
+
function factorAndGroup(variants) {
|
|
1021
|
+
{
|
|
1022
|
+
const key0 = getVariantContextKey(variants[0]);
|
|
1023
|
+
for (let i = 1; i < variants.length; i++) {
|
|
1024
|
+
const keyI = getVariantContextKey(variants[i]);
|
|
1025
|
+
if (keyI !== key0) throw new Error(`factorAndGroup: context key mismatch at index ${i}.\n expected: ${key0}\n got: ${keyI}`);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
const commonModKeys = findCommonKeys(variants.map((v) => v.modifierConditions), getModifierKey);
|
|
1029
|
+
const commonPseudoKeys = findCommonKeys(variants.map((v) => v.pseudoConditions), getPseudoKey);
|
|
1030
|
+
const commonModifiers = variants[0].modifierConditions.filter((m) => commonModKeys.has(getModifierKey(m)));
|
|
1031
|
+
const commonPseudos = variants[0].pseudoConditions.filter((p) => commonPseudoKeys.has(getPseudoKey(p)));
|
|
1032
|
+
const branches = [];
|
|
1033
|
+
let hasEmptyBranch = false;
|
|
1034
|
+
for (const v of variants) {
|
|
1035
|
+
const branch = [];
|
|
1036
|
+
for (const mod of v.modifierConditions) if (!commonModKeys.has(getModifierKey(mod))) branch.push(mod);
|
|
1037
|
+
for (const pseudo of v.pseudoConditions) if (!commonPseudoKeys.has(getPseudoKey(pseudo))) branch.push(pseudo);
|
|
1038
|
+
if (branch.length > 0) branches.push(branch);
|
|
1039
|
+
else hasEmptyBranch = true;
|
|
1040
|
+
}
|
|
1041
|
+
if (hasEmptyBranch) return {
|
|
1042
|
+
...variants[0],
|
|
1043
|
+
modifierConditions: commonModifiers,
|
|
1044
|
+
pseudoConditions: commonPseudos
|
|
1045
|
+
};
|
|
1046
|
+
return {
|
|
1047
|
+
modifierConditions: commonModifiers,
|
|
1048
|
+
pseudoConditions: commonPseudos,
|
|
1049
|
+
selectorGroups: [...variants[0].selectorGroups, {
|
|
1050
|
+
branches,
|
|
1051
|
+
negated: false
|
|
1052
|
+
}],
|
|
1053
|
+
ownGroups: [...variants[0].ownGroups],
|
|
1054
|
+
mediaConditions: [...variants[0].mediaConditions],
|
|
1055
|
+
containerConditions: [...variants[0].containerConditions],
|
|
1056
|
+
supportsConditions: [...variants[0].supportsConditions],
|
|
1057
|
+
rootGroups: [...variants[0].rootGroups],
|
|
1058
|
+
parentGroups: [...variants[0].parentGroups],
|
|
1059
|
+
startingStyle: variants[0].startingStyle
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
/**
|
|
814
1063
|
* Build at-rules array from a variant
|
|
815
1064
|
*/
|
|
816
1065
|
function buildAtRulesFromVariant(variant) {
|
|
@@ -852,5 +1101,5 @@ function buildAtRulesFromVariant(variant) {
|
|
|
852
1101
|
}
|
|
853
1102
|
|
|
854
1103
|
//#endregion
|
|
855
|
-
export { buildAtRulesFromVariant, conditionToCSS,
|
|
1104
|
+
export { branchToCSS, buildAtRulesFromVariant, conditionToCSS, mergeVariantsIntoSelectorGroups, optimizeGroups, parentGroupsToCSS, rootGroupsToCSS, selectorGroupToCSS };
|
|
856
1105
|
//# sourceMappingURL=materialize.js.map
|