@tenphi/tasty 2.0.2 → 2.0.3
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/pipeline/exclusive.js +8 -1
- package/dist/pipeline/exclusive.js.map +1 -1
- package/dist/pipeline/index.js +91 -50
- package/dist/pipeline/index.js.map +1 -1
- package/dist/pipeline/materialize-contradictions.js +125 -0
- package/dist/pipeline/materialize-contradictions.js.map +1 -0
- package/dist/pipeline/materialize.js +1 -120
- package/dist/pipeline/materialize.js.map +1 -1
- package/dist/pipeline/simplify.js +38 -3
- package/dist/pipeline/simplify.js.map +1 -1
- package/dist/tasty.d.ts +1 -1
- package/docs/pipeline.md +204 -50
- package/package.json +1 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
//#region src/pipeline/materialize-contradictions.ts
|
|
2
|
+
/**
|
|
3
|
+
* Generic deduplication by a key extraction function.
|
|
4
|
+
* Preserves insertion order, keeping the first occurrence of each key.
|
|
5
|
+
*/
|
|
6
|
+
function dedupeByKey(items, getKey) {
|
|
7
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8
|
+
const result = [];
|
|
9
|
+
for (const item of items) {
|
|
10
|
+
const key = getKey(item);
|
|
11
|
+
if (!seen.has(key)) {
|
|
12
|
+
seen.add(key);
|
|
13
|
+
result.push(item);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
function dedupeMediaConditions(conditions) {
|
|
19
|
+
return dedupeByKey(conditions, (c) => `${c.subtype}|${c.condition}|${c.negated}`);
|
|
20
|
+
}
|
|
21
|
+
function dedupeContainerConditions(conditions) {
|
|
22
|
+
return dedupeByKey(conditions, (c) => `${c.name ?? ""}|${c.condition}|${c.negated}`);
|
|
23
|
+
}
|
|
24
|
+
function dedupeSupportsConditions(conditions) {
|
|
25
|
+
return dedupeByKey(conditions, (c) => `${c.subtype}|${c.condition}|${c.negated}`);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Check if supports conditions contain contradictions
|
|
29
|
+
* e.g., @supports(display: grid) AND NOT @supports(display: grid)
|
|
30
|
+
*/
|
|
31
|
+
function hasSupportsContradiction(conditions) {
|
|
32
|
+
const conditionMap = /* @__PURE__ */ new Map();
|
|
33
|
+
for (const cond of conditions) {
|
|
34
|
+
const key = `${cond.subtype}|${cond.condition}`;
|
|
35
|
+
const existing = conditionMap.get(key);
|
|
36
|
+
if (existing !== void 0 && existing !== !cond.negated) return true;
|
|
37
|
+
conditionMap.set(key, !cond.negated);
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Check if a set of media conditions contains contradictions
|
|
43
|
+
* e.g., (prefers-color-scheme: light) AND NOT (prefers-color-scheme: light)
|
|
44
|
+
* or (width >= 900px) AND (width < 600px)
|
|
45
|
+
*
|
|
46
|
+
* Uses parsed media conditions for efficient analysis without regex parsing.
|
|
47
|
+
*/
|
|
48
|
+
function hasMediaContradiction(conditions) {
|
|
49
|
+
const featureConditions = /* @__PURE__ */ new Map();
|
|
50
|
+
const typeConditions = /* @__PURE__ */ new Map();
|
|
51
|
+
const dimensionConditions = /* @__PURE__ */ new Map();
|
|
52
|
+
const dimensionsByDim = /* @__PURE__ */ new Map();
|
|
53
|
+
for (const cond of conditions) if (cond.subtype === "type") {
|
|
54
|
+
const key = cond.mediaType || "all";
|
|
55
|
+
const existing = typeConditions.get(key);
|
|
56
|
+
if (existing !== void 0 && existing !== !cond.negated) return true;
|
|
57
|
+
typeConditions.set(key, !cond.negated);
|
|
58
|
+
} else if (cond.subtype === "feature") {
|
|
59
|
+
const key = cond.condition;
|
|
60
|
+
const existing = featureConditions.get(key);
|
|
61
|
+
if (existing !== void 0 && existing !== !cond.negated) return true;
|
|
62
|
+
featureConditions.set(key, !cond.negated);
|
|
63
|
+
} else if (cond.subtype === "dimension") {
|
|
64
|
+
const condKey = cond.condition;
|
|
65
|
+
const existing = dimensionConditions.get(condKey);
|
|
66
|
+
if (existing !== void 0 && existing !== !cond.negated) return true;
|
|
67
|
+
dimensionConditions.set(condKey, !cond.negated);
|
|
68
|
+
if (!cond.negated) {
|
|
69
|
+
const dim = cond.dimension || "width";
|
|
70
|
+
let bounds = dimensionsByDim.get(dim);
|
|
71
|
+
if (!bounds) {
|
|
72
|
+
bounds = {
|
|
73
|
+
lowerBound: null,
|
|
74
|
+
upperBound: null
|
|
75
|
+
};
|
|
76
|
+
dimensionsByDim.set(dim, bounds);
|
|
77
|
+
}
|
|
78
|
+
if (cond.lowerBound?.valueNumeric != null) {
|
|
79
|
+
const value = cond.lowerBound.valueNumeric;
|
|
80
|
+
if (bounds.lowerBound === null || value > bounds.lowerBound) bounds.lowerBound = value;
|
|
81
|
+
}
|
|
82
|
+
if (cond.upperBound?.valueNumeric != null) {
|
|
83
|
+
const value = cond.upperBound.valueNumeric;
|
|
84
|
+
if (bounds.upperBound === null || value < bounds.upperBound) bounds.upperBound = value;
|
|
85
|
+
}
|
|
86
|
+
if (bounds.lowerBound !== null && bounds.upperBound !== null && bounds.lowerBound >= bounds.upperBound) return true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Check if container conditions contain contradictions in style queries
|
|
93
|
+
* e.g., style(--variant: danger) and style(--variant: success) together
|
|
94
|
+
* Same property with different values = always false
|
|
95
|
+
*
|
|
96
|
+
* Uses parsed container conditions for efficient analysis without regex parsing.
|
|
97
|
+
*/
|
|
98
|
+
function hasContainerStyleContradiction(conditions) {
|
|
99
|
+
const styleQueries = /* @__PURE__ */ new Map();
|
|
100
|
+
for (const cond of conditions) {
|
|
101
|
+
if (cond.subtype !== "style" || !cond.property) continue;
|
|
102
|
+
const property = cond.property;
|
|
103
|
+
const value = cond.propertyValue;
|
|
104
|
+
if (!styleQueries.has(property)) styleQueries.set(property, {
|
|
105
|
+
hasExistence: false,
|
|
106
|
+
values: /* @__PURE__ */ new Set(),
|
|
107
|
+
hasNegatedExistence: false
|
|
108
|
+
});
|
|
109
|
+
const entry = styleQueries.get(property);
|
|
110
|
+
if (cond.negated) {
|
|
111
|
+
if (value === void 0) entry.hasNegatedExistence = true;
|
|
112
|
+
} else if (value === void 0) entry.hasExistence = true;
|
|
113
|
+
else entry.values.add(value);
|
|
114
|
+
}
|
|
115
|
+
for (const [, entry] of styleQueries) {
|
|
116
|
+
if (entry.hasExistence && entry.hasNegatedExistence) return true;
|
|
117
|
+
if (entry.values.size > 1) return true;
|
|
118
|
+
if (entry.hasNegatedExistence && entry.values.size > 0) return true;
|
|
119
|
+
}
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
//#endregion
|
|
123
|
+
export { dedupeContainerConditions, dedupeMediaConditions, dedupeSupportsConditions, hasContainerStyleContradiction, hasMediaContradiction, hasSupportsContradiction };
|
|
124
|
+
|
|
125
|
+
//# sourceMappingURL=materialize-contradictions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"materialize-contradictions.js","names":[],"sources":["../../src/pipeline/materialize-contradictions.ts"],"sourcesContent":["/**\n * Contradiction Detection for Parsed CSS Conditions\n *\n * After conditions are converted into their parsed CSS shapes\n * (`ParsedMediaCondition`, `ParsedContainerCondition`,\n * `ParsedSupportsCondition`), variant merging in `materialize.ts` needs to\n * detect impossible combinations a second time at this lower level — the\n * tree-level simplifier in `simplify.ts` operates on `ConditionNode`s,\n * which can't see post-parse details like dimension bounds collapsing or\n * style-query property conflicts.\n *\n * Functions in this module are pure and self-contained; they take parsed\n * conditions and return booleans. They share no state with each other or\n * with the rest of the materialization pipeline.\n */\n\nimport type {\n ParsedContainerCondition,\n ParsedMediaCondition,\n ParsedSupportsCondition,\n} from './materialize-types';\n\n/**\n * Generic deduplication by a key extraction function.\n * Preserves insertion order, keeping the first occurrence of each key.\n */\nfunction dedupeByKey<T>(items: T[], getKey: (item: T) => string): T[] {\n const seen = new Set<string>();\n const result: T[] = [];\n for (const item of items) {\n const key = getKey(item);\n if (!seen.has(key)) {\n seen.add(key);\n result.push(item);\n }\n }\n return result;\n}\n\nexport function dedupeMediaConditions(\n conditions: ParsedMediaCondition[],\n): ParsedMediaCondition[] {\n return dedupeByKey(\n conditions,\n (c) => `${c.subtype}|${c.condition}|${c.negated}`,\n );\n}\n\nexport function dedupeContainerConditions(\n conditions: ParsedContainerCondition[],\n): ParsedContainerCondition[] {\n return dedupeByKey(\n conditions,\n (c) => `${c.name ?? ''}|${c.condition}|${c.negated}`,\n );\n}\n\nexport function dedupeSupportsConditions(\n conditions: ParsedSupportsCondition[],\n): ParsedSupportsCondition[] {\n return dedupeByKey(\n conditions,\n (c) => `${c.subtype}|${c.condition}|${c.negated}`,\n );\n}\n\n/**\n * Check if supports conditions contain contradictions\n * e.g., @supports(display: grid) AND NOT @supports(display: grid)\n */\nexport function hasSupportsContradiction(\n conditions: ParsedSupportsCondition[],\n): boolean {\n const conditionMap = new Map<string, boolean>(); // key -> isPositive\n\n for (const cond of conditions) {\n const key = `${cond.subtype}|${cond.condition}`;\n const existing = conditionMap.get(key);\n if (existing !== undefined && existing !== !cond.negated) {\n return true; // Contradiction: positive AND negated\n }\n conditionMap.set(key, !cond.negated);\n }\n\n return false;\n}\n\n/**\n * Check if a set of media conditions contains contradictions\n * e.g., (prefers-color-scheme: light) AND NOT (prefers-color-scheme: light)\n * or (width >= 900px) AND (width < 600px)\n *\n * Uses parsed media conditions for efficient analysis without regex parsing.\n */\nexport function hasMediaContradiction(\n conditions: ParsedMediaCondition[],\n): boolean {\n // Track conditions by their key (condition string) to detect A and NOT A\n const featureConditions = new Map<string, boolean>(); // key -> isPositive\n const typeConditions = new Map<string, boolean>(); // mediaType -> isPositive\n const dimensionConditions = new Map<string, boolean>(); // condition -> isPositive\n\n // Track dimension conditions for range contradiction detection (non-negated only)\n const dimensionsByDim = new Map<\n string,\n { lowerBound: number | null; upperBound: number | null }\n >();\n\n for (const cond of conditions) {\n if (cond.subtype === 'type') {\n // Type query: check for direct contradiction (print AND NOT print)\n const key = cond.mediaType || 'all';\n const existing = typeConditions.get(key);\n if (existing !== undefined && existing !== !cond.negated) {\n return true; // Contradiction: positive AND negated\n }\n typeConditions.set(key, !cond.negated);\n } else if (cond.subtype === 'feature') {\n // Feature query: check for direct contradiction\n const key = cond.condition;\n const existing = featureConditions.get(key);\n if (existing !== undefined && existing !== !cond.negated) {\n return true; // Contradiction: positive AND negated\n }\n featureConditions.set(key, !cond.negated);\n } else if (cond.subtype === 'dimension') {\n // First, check for direct contradiction: (width < 600px) AND NOT (width < 600px)\n const condKey = cond.condition;\n const existing = dimensionConditions.get(condKey);\n if (existing !== undefined && existing !== !cond.negated) {\n return true; // Contradiction: positive AND negated\n }\n dimensionConditions.set(condKey, !cond.negated);\n\n // For range analysis, only consider non-negated conditions\n // Negated conditions are handled via the direct contradiction check above\n if (!cond.negated) {\n const dim = cond.dimension || 'width';\n let bounds = dimensionsByDim.get(dim);\n if (!bounds) {\n bounds = { lowerBound: null, upperBound: null };\n dimensionsByDim.set(dim, bounds);\n }\n\n // Track the effective bounds\n if (cond.lowerBound?.valueNumeric != null) {\n const value = cond.lowerBound.valueNumeric;\n if (bounds.lowerBound === null || value > bounds.lowerBound) {\n bounds.lowerBound = value;\n }\n }\n if (cond.upperBound?.valueNumeric != null) {\n const value = cond.upperBound.valueNumeric;\n if (bounds.upperBound === null || value < bounds.upperBound) {\n bounds.upperBound = value;\n }\n }\n\n // Check for impossible range\n if (\n bounds.lowerBound !== null &&\n bounds.upperBound !== null &&\n bounds.lowerBound >= bounds.upperBound\n ) {\n return true;\n }\n }\n }\n }\n\n return false;\n}\n\n/**\n * Check if container conditions contain contradictions in style queries\n * e.g., style(--variant: danger) and style(--variant: success) together\n * Same property with different values = always false\n *\n * Uses parsed container conditions for efficient analysis without regex parsing.\n */\nexport function hasContainerStyleContradiction(\n conditions: ParsedContainerCondition[],\n): boolean {\n // Track style queries by property name\n // key: property name, value: { hasExistence: boolean, values: Set<string>, hasNegatedExistence: boolean }\n const styleQueries = new Map<\n string,\n { hasExistence: boolean; values: Set<string>; hasNegatedExistence: boolean }\n >();\n\n for (const cond of conditions) {\n // Only analyze style queries\n if (cond.subtype !== 'style' || !cond.property) {\n continue;\n }\n\n const property = cond.property;\n const value = cond.propertyValue;\n\n if (!styleQueries.has(property)) {\n styleQueries.set(property, {\n hasExistence: false,\n values: new Set(),\n hasNegatedExistence: false,\n });\n }\n\n const entry = styleQueries.get(property)!;\n\n if (cond.negated) {\n if (value === undefined) {\n // not style(--prop) - negated existence check\n entry.hasNegatedExistence = true;\n }\n // Negated value checks don't contradict positive value checks directly\n // They just mean \"not this value\"\n } else {\n if (value === undefined) {\n // style(--prop) - existence check\n entry.hasExistence = true;\n } else {\n // style(--prop: value) - value check\n entry.values.add(value);\n }\n }\n }\n\n // Check for contradictions\n for (const [, entry] of styleQueries) {\n // Contradiction: existence check + negated existence check\n if (entry.hasExistence && entry.hasNegatedExistence) {\n return true;\n }\n\n // Contradiction: multiple different values for same property\n // style(--variant: danger) AND style(--variant: success) is impossible\n if (entry.values.size > 1) {\n return true;\n }\n\n // Contradiction: negated existence + value check\n // not style(--variant) AND style(--variant: danger) is impossible\n if (entry.hasNegatedExistence && entry.values.size > 0) {\n return true;\n }\n }\n\n return false;\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Lru } from "../parser/lru.js";
|
|
2
2
|
import { and, getConditionUniqueId, isCompoundCondition, not } from "./conditions.js";
|
|
3
3
|
import { simplifyCondition } from "./simplify.js";
|
|
4
|
+
import { dedupeContainerConditions, dedupeMediaConditions, dedupeSupportsConditions, hasContainerStyleContradiction, hasMediaContradiction, hasSupportsContradiction } from "./materialize-contradictions.js";
|
|
4
5
|
//#region src/pipeline/materialize.ts
|
|
5
6
|
/**
|
|
6
7
|
* CSS Materialization
|
|
@@ -646,126 +647,6 @@ function mergeVariants(a, b) {
|
|
|
646
647
|
startingStyle: a.startingStyle || b.startingStyle
|
|
647
648
|
};
|
|
648
649
|
}
|
|
649
|
-
/**
|
|
650
|
-
* Generic deduplication by a key extraction function.
|
|
651
|
-
* Preserves insertion order, keeping the first occurrence of each key.
|
|
652
|
-
*/
|
|
653
|
-
function dedupeByKey(items, getKey) {
|
|
654
|
-
const seen = /* @__PURE__ */ new Set();
|
|
655
|
-
const result = [];
|
|
656
|
-
for (const item of items) {
|
|
657
|
-
const key = getKey(item);
|
|
658
|
-
if (!seen.has(key)) {
|
|
659
|
-
seen.add(key);
|
|
660
|
-
result.push(item);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
return result;
|
|
664
|
-
}
|
|
665
|
-
function dedupeMediaConditions(conditions) {
|
|
666
|
-
return dedupeByKey(conditions, (c) => `${c.subtype}|${c.condition}|${c.negated}`);
|
|
667
|
-
}
|
|
668
|
-
function dedupeContainerConditions(conditions) {
|
|
669
|
-
return dedupeByKey(conditions, (c) => `${c.name ?? ""}|${c.condition}|${c.negated}`);
|
|
670
|
-
}
|
|
671
|
-
function dedupeSupportsConditions(conditions) {
|
|
672
|
-
return dedupeByKey(conditions, (c) => `${c.subtype}|${c.condition}|${c.negated}`);
|
|
673
|
-
}
|
|
674
|
-
/**
|
|
675
|
-
* Check if supports conditions contain contradictions
|
|
676
|
-
* e.g., @supports(display: grid) AND NOT @supports(display: grid)
|
|
677
|
-
*/
|
|
678
|
-
function hasSupportsContradiction(conditions) {
|
|
679
|
-
const conditionMap = /* @__PURE__ */ new Map();
|
|
680
|
-
for (const cond of conditions) {
|
|
681
|
-
const key = `${cond.subtype}|${cond.condition}`;
|
|
682
|
-
const existing = conditionMap.get(key);
|
|
683
|
-
if (existing !== void 0 && existing !== !cond.negated) return true;
|
|
684
|
-
conditionMap.set(key, !cond.negated);
|
|
685
|
-
}
|
|
686
|
-
return false;
|
|
687
|
-
}
|
|
688
|
-
/**
|
|
689
|
-
* Check if a set of media conditions contains contradictions
|
|
690
|
-
* e.g., (prefers-color-scheme: light) AND NOT (prefers-color-scheme: light)
|
|
691
|
-
* or (width >= 900px) AND (width < 600px)
|
|
692
|
-
*
|
|
693
|
-
* Uses parsed media conditions for efficient analysis without regex parsing.
|
|
694
|
-
*/
|
|
695
|
-
function hasMediaContradiction(conditions) {
|
|
696
|
-
const featureConditions = /* @__PURE__ */ new Map();
|
|
697
|
-
const typeConditions = /* @__PURE__ */ new Map();
|
|
698
|
-
const dimensionConditions = /* @__PURE__ */ new Map();
|
|
699
|
-
const dimensionsByDim = /* @__PURE__ */ new Map();
|
|
700
|
-
for (const cond of conditions) if (cond.subtype === "type") {
|
|
701
|
-
const key = cond.mediaType || "all";
|
|
702
|
-
const existing = typeConditions.get(key);
|
|
703
|
-
if (existing !== void 0 && existing !== !cond.negated) return true;
|
|
704
|
-
typeConditions.set(key, !cond.negated);
|
|
705
|
-
} else if (cond.subtype === "feature") {
|
|
706
|
-
const key = cond.condition;
|
|
707
|
-
const existing = featureConditions.get(key);
|
|
708
|
-
if (existing !== void 0 && existing !== !cond.negated) return true;
|
|
709
|
-
featureConditions.set(key, !cond.negated);
|
|
710
|
-
} else if (cond.subtype === "dimension") {
|
|
711
|
-
const condKey = cond.condition;
|
|
712
|
-
const existing = dimensionConditions.get(condKey);
|
|
713
|
-
if (existing !== void 0 && existing !== !cond.negated) return true;
|
|
714
|
-
dimensionConditions.set(condKey, !cond.negated);
|
|
715
|
-
if (!cond.negated) {
|
|
716
|
-
const dim = cond.dimension || "width";
|
|
717
|
-
let bounds = dimensionsByDim.get(dim);
|
|
718
|
-
if (!bounds) {
|
|
719
|
-
bounds = {
|
|
720
|
-
lowerBound: null,
|
|
721
|
-
upperBound: null
|
|
722
|
-
};
|
|
723
|
-
dimensionsByDim.set(dim, bounds);
|
|
724
|
-
}
|
|
725
|
-
if (cond.lowerBound?.valueNumeric != null) {
|
|
726
|
-
const value = cond.lowerBound.valueNumeric;
|
|
727
|
-
if (bounds.lowerBound === null || value > bounds.lowerBound) bounds.lowerBound = value;
|
|
728
|
-
}
|
|
729
|
-
if (cond.upperBound?.valueNumeric != null) {
|
|
730
|
-
const value = cond.upperBound.valueNumeric;
|
|
731
|
-
if (bounds.upperBound === null || value < bounds.upperBound) bounds.upperBound = value;
|
|
732
|
-
}
|
|
733
|
-
if (bounds.lowerBound !== null && bounds.upperBound !== null && bounds.lowerBound >= bounds.upperBound) return true;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
return false;
|
|
737
|
-
}
|
|
738
|
-
/**
|
|
739
|
-
* Check if container conditions contain contradictions in style queries
|
|
740
|
-
* e.g., style(--variant: danger) and style(--variant: success) together
|
|
741
|
-
* Same property with different values = always false
|
|
742
|
-
*
|
|
743
|
-
* Uses parsed container conditions for efficient analysis without regex parsing.
|
|
744
|
-
*/
|
|
745
|
-
function hasContainerStyleContradiction(conditions) {
|
|
746
|
-
const styleQueries = /* @__PURE__ */ new Map();
|
|
747
|
-
for (const cond of conditions) {
|
|
748
|
-
if (cond.subtype !== "style" || !cond.property) continue;
|
|
749
|
-
const property = cond.property;
|
|
750
|
-
const value = cond.propertyValue;
|
|
751
|
-
if (!styleQueries.has(property)) styleQueries.set(property, {
|
|
752
|
-
hasExistence: false,
|
|
753
|
-
values: /* @__PURE__ */ new Set(),
|
|
754
|
-
hasNegatedExistence: false
|
|
755
|
-
});
|
|
756
|
-
const entry = styleQueries.get(property);
|
|
757
|
-
if (cond.negated) {
|
|
758
|
-
if (value === void 0) entry.hasNegatedExistence = true;
|
|
759
|
-
} else if (value === void 0) entry.hasExistence = true;
|
|
760
|
-
else entry.values.add(value);
|
|
761
|
-
}
|
|
762
|
-
for (const [, entry] of styleQueries) {
|
|
763
|
-
if (entry.hasExistence && entry.hasNegatedExistence) return true;
|
|
764
|
-
if (entry.values.size > 1) return true;
|
|
765
|
-
if (entry.hasNegatedExistence && entry.values.size > 0) return true;
|
|
766
|
-
}
|
|
767
|
-
return false;
|
|
768
|
-
}
|
|
769
650
|
const variantKeyCache = /* @__PURE__ */ new WeakMap();
|
|
770
651
|
/**
|
|
771
652
|
* Get a unique key for a variant (for deduplication).
|