@tenphi/tasty 2.0.3 → 2.1.0
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/{ssr/async-storage.js → async-storage-B7_o6FKt.js} +2 -2
- package/dist/async-storage-B7_o6FKt.js.map +1 -0
- package/dist/{ssr/collector.js → collector-DrgDE7QB.js} +5 -10
- package/dist/collector-DrgDE7QB.js.map +1 -0
- package/dist/{ssr/collector.d.ts → collector-LuU1vZ68.d.ts} +3 -3
- package/dist/config-_aQ_PZ-P.js +10131 -0
- package/dist/config-_aQ_PZ-P.js.map +1 -0
- package/dist/config-vuCRkBWX.d.ts +884 -0
- package/dist/{ssr/context.js → context-CkSg-kDT.js} +11 -3
- package/dist/context-CkSg-kDT.js.map +1 -0
- package/dist/core/index.d.ts +5 -34
- package/dist/core/index.js +6 -27
- package/dist/core-BqO8pplb.js +1592 -0
- package/dist/core-BqO8pplb.js.map +1 -0
- package/dist/{zero/extractor.js → css-writer-D--REwtp.js} +74 -11
- package/dist/css-writer-D--REwtp.js.map +1 -0
- package/dist/{ssr/format-global-rules.js → format-global-rules-Dbc_1tc3.js} +2 -2
- package/dist/format-global-rules-Dbc_1tc3.js.map +1 -0
- package/dist/format-rules-xwteB7a1.js +143 -0
- package/dist/format-rules-xwteB7a1.js.map +1 -0
- package/dist/{ssr/hydrate.js → hydrate-BvPT4ndL.js} +3 -3
- package/dist/hydrate-BvPT4ndL.js.map +1 -0
- package/dist/index-ZRxZWzlj.d.ts +1602 -0
- package/dist/{styles/types.d.ts → index-dUtwpOux.d.ts} +707 -5
- package/dist/index.d.ts +5 -51
- package/dist/index.js +732 -36
- package/dist/index.js.map +1 -0
- package/dist/keyframes-ClPFWy33.js +587 -0
- package/dist/keyframes-ClPFWy33.js.map +1 -0
- package/dist/{utils/merge-styles.js → merge-styles-BUQsEpbv.js} +3 -4
- package/dist/merge-styles-BUQsEpbv.js.map +1 -0
- package/dist/{utils/merge-styles.d.ts → merge-styles-CtDJMhpJ.d.ts} +3 -3
- package/dist/{utils/resolve-recipes.js → resolve-recipes-C0-AMzCz.js} +4 -6
- package/dist/resolve-recipes-C0-AMzCz.js.map +1 -0
- package/dist/ssr/astro-client.js +1 -1
- package/dist/ssr/astro.js +4 -4
- package/dist/ssr/index.d.ts +44 -4
- package/dist/ssr/index.js +4 -4
- package/dist/ssr/next.d.ts +1 -1
- package/dist/ssr/next.js +6 -6
- package/dist/ssr/next.js.map +1 -1
- package/dist/static/index.d.ts +91 -5
- package/dist/static/index.js +49 -3
- package/dist/static/index.js.map +1 -0
- package/dist/zero/babel.d.ts +1 -1
- package/dist/zero/babel.js +10 -6
- package/dist/zero/babel.js.map +1 -1
- package/dist/zero/index.d.ts +67 -3
- package/dist/zero/index.js +1 -2
- package/docs/injector.md +2 -2
- package/package.json +10 -9
- package/dist/_virtual/_rolldown/runtime.js +0 -7
- package/dist/chunks/cacheKey.d.ts +0 -1
- package/dist/chunks/cacheKey.js +0 -77
- package/dist/chunks/cacheKey.js.map +0 -1
- package/dist/chunks/definitions.d.ts +0 -37
- package/dist/chunks/definitions.js +0 -258
- package/dist/chunks/definitions.js.map +0 -1
- package/dist/chunks/index.d.ts +0 -1
- package/dist/chunks/renderChunk.d.ts +0 -1
- package/dist/chunks/renderChunk.js +0 -59
- package/dist/chunks/renderChunk.js.map +0 -1
- package/dist/compute-styles.d.ts +0 -31
- package/dist/compute-styles.js +0 -322
- package/dist/compute-styles.js.map +0 -1
- package/dist/config.d.ts +0 -407
- package/dist/config.js +0 -591
- package/dist/config.js.map +0 -1
- package/dist/counter-style/index.js +0 -51
- package/dist/counter-style/index.js.map +0 -1
- package/dist/debug.d.ts +0 -89
- package/dist/debug.js +0 -453
- package/dist/debug.js.map +0 -1
- package/dist/font-face/index.js +0 -63
- package/dist/font-face/index.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -7
- package/dist/hooks/useCounterStyle.d.ts +0 -36
- package/dist/hooks/useCounterStyle.js +0 -65
- package/dist/hooks/useCounterStyle.js.map +0 -1
- package/dist/hooks/useFontFace.d.ts +0 -45
- package/dist/hooks/useFontFace.js +0 -66
- package/dist/hooks/useFontFace.js.map +0 -1
- package/dist/hooks/useGlobalStyles.d.ts +0 -46
- package/dist/hooks/useGlobalStyles.js +0 -88
- package/dist/hooks/useGlobalStyles.js.map +0 -1
- package/dist/hooks/useKeyframes.d.ts +0 -58
- package/dist/hooks/useKeyframes.js +0 -55
- package/dist/hooks/useKeyframes.js.map +0 -1
- package/dist/hooks/useProperty.d.ts +0 -81
- package/dist/hooks/useProperty.js +0 -96
- package/dist/hooks/useProperty.js.map +0 -1
- package/dist/hooks/useRawCSS.d.ts +0 -22
- package/dist/hooks/useRawCSS.js +0 -103
- package/dist/hooks/useRawCSS.js.map +0 -1
- package/dist/hooks/useStyles.d.ts +0 -40
- package/dist/hooks/useStyles.js +0 -31
- package/dist/hooks/useStyles.js.map +0 -1
- package/dist/injector/index.d.ts +0 -182
- package/dist/injector/index.js +0 -185
- package/dist/injector/index.js.map +0 -1
- package/dist/injector/injector.d.ts +0 -198
- package/dist/injector/injector.js +0 -651
- package/dist/injector/injector.js.map +0 -1
- package/dist/injector/sheet-manager.d.ts +0 -132
- package/dist/injector/sheet-manager.js +0 -699
- package/dist/injector/sheet-manager.js.map +0 -1
- package/dist/injector/types.d.ts +0 -235
- package/dist/keyframes/index.js +0 -206
- package/dist/keyframes/index.js.map +0 -1
- package/dist/parser/classify.js +0 -319
- package/dist/parser/classify.js.map +0 -1
- package/dist/parser/const.js +0 -60
- package/dist/parser/const.js.map +0 -1
- package/dist/parser/lru.js +0 -109
- package/dist/parser/lru.js.map +0 -1
- package/dist/parser/parser.d.ts +0 -25
- package/dist/parser/parser.js +0 -115
- package/dist/parser/parser.js.map +0 -1
- package/dist/parser/tokenizer.js +0 -69
- package/dist/parser/tokenizer.js.map +0 -1
- package/dist/parser/types.d.ts +0 -51
- package/dist/parser/types.js +0 -46
- package/dist/parser/types.js.map +0 -1
- package/dist/pipeline/conditions.d.ts +0 -134
- package/dist/pipeline/conditions.js +0 -406
- package/dist/pipeline/conditions.js.map +0 -1
- package/dist/pipeline/exclusive.js +0 -389
- package/dist/pipeline/exclusive.js.map +0 -1
- package/dist/pipeline/index.d.ts +0 -55
- package/dist/pipeline/index.js +0 -749
- package/dist/pipeline/index.js.map +0 -1
- package/dist/pipeline/materialize-contradictions.js +0 -125
- package/dist/pipeline/materialize-contradictions.js.map +0 -1
- package/dist/pipeline/materialize.js +0 -1038
- package/dist/pipeline/materialize.js.map +0 -1
- package/dist/pipeline/parseStateKey.d.ts +0 -15
- package/dist/pipeline/parseStateKey.js +0 -446
- package/dist/pipeline/parseStateKey.js.map +0 -1
- package/dist/pipeline/simplify.js +0 -725
- package/dist/pipeline/simplify.js.map +0 -1
- package/dist/pipeline/warnings.js +0 -18
- package/dist/pipeline/warnings.js.map +0 -1
- package/dist/plugins/index.d.ts +0 -2
- package/dist/plugins/okhsl-plugin.d.ts +0 -35
- package/dist/plugins/okhsl-plugin.js +0 -97
- package/dist/plugins/okhsl-plugin.js.map +0 -1
- package/dist/plugins/types.d.ts +0 -87
- package/dist/properties/index.js +0 -222
- package/dist/properties/index.js.map +0 -1
- package/dist/properties/property-type-resolver.d.ts +0 -24
- package/dist/properties/property-type-resolver.js +0 -90
- package/dist/properties/property-type-resolver.js.map +0 -1
- package/dist/rsc-cache.js +0 -79
- package/dist/rsc-cache.js.map +0 -1
- package/dist/ssr/async-storage.d.ts +0 -17
- package/dist/ssr/async-storage.js.map +0 -1
- package/dist/ssr/collect-auto-properties.js +0 -58
- package/dist/ssr/collect-auto-properties.js.map +0 -1
- package/dist/ssr/collector.js.map +0 -1
- package/dist/ssr/context.js.map +0 -1
- package/dist/ssr/format-global-rules.js.map +0 -1
- package/dist/ssr/format-keyframes.js +0 -69
- package/dist/ssr/format-keyframes.js.map +0 -1
- package/dist/ssr/format-property.js +0 -49
- package/dist/ssr/format-property.js.map +0 -1
- package/dist/ssr/format-rules.js +0 -73
- package/dist/ssr/format-rules.js.map +0 -1
- package/dist/ssr/hydrate.d.ts +0 -29
- package/dist/ssr/hydrate.js.map +0 -1
- package/dist/ssr/ssr-collector-ref.js +0 -29
- package/dist/ssr/ssr-collector-ref.js.map +0 -1
- package/dist/states/index.d.ts +0 -49
- package/dist/states/index.js +0 -170
- package/dist/states/index.js.map +0 -1
- package/dist/static/tastyStatic.d.ts +0 -46
- package/dist/static/tastyStatic.js +0 -30
- package/dist/static/tastyStatic.js.map +0 -1
- package/dist/static/types.d.ts +0 -49
- package/dist/static/types.js +0 -24
- package/dist/static/types.js.map +0 -1
- package/dist/styles/border.d.ts +0 -25
- package/dist/styles/border.js +0 -120
- package/dist/styles/border.js.map +0 -1
- package/dist/styles/color.d.ts +0 -14
- package/dist/styles/color.js +0 -26
- package/dist/styles/color.js.map +0 -1
- package/dist/styles/const.js +0 -17
- package/dist/styles/const.js.map +0 -1
- package/dist/styles/createStyle.js +0 -79
- package/dist/styles/createStyle.js.map +0 -1
- package/dist/styles/dimension.js +0 -109
- package/dist/styles/dimension.js.map +0 -1
- package/dist/styles/directional.js +0 -133
- package/dist/styles/directional.js.map +0 -1
- package/dist/styles/display.d.ts +0 -30
- package/dist/styles/display.js +0 -73
- package/dist/styles/display.js.map +0 -1
- package/dist/styles/fade.d.ts +0 -15
- package/dist/styles/fade.js +0 -62
- package/dist/styles/fade.js.map +0 -1
- package/dist/styles/fill.d.ts +0 -42
- package/dist/styles/fill.js +0 -51
- package/dist/styles/fill.js.map +0 -1
- package/dist/styles/flow.d.ts +0 -16
- package/dist/styles/flow.js +0 -12
- package/dist/styles/flow.js.map +0 -1
- package/dist/styles/gap.d.ts +0 -31
- package/dist/styles/gap.js +0 -38
- package/dist/styles/gap.js.map +0 -1
- package/dist/styles/height.d.ts +0 -17
- package/dist/styles/height.js +0 -19
- package/dist/styles/height.js.map +0 -1
- package/dist/styles/index.d.ts +0 -1
- package/dist/styles/index.js +0 -8
- package/dist/styles/index.js.map +0 -1
- package/dist/styles/inset.d.ts +0 -24
- package/dist/styles/inset.js +0 -34
- package/dist/styles/inset.js.map +0 -1
- package/dist/styles/list.d.ts +0 -16
- package/dist/styles/list.js +0 -100
- package/dist/styles/list.js.map +0 -1
- package/dist/styles/margin.d.ts +0 -24
- package/dist/styles/margin.js +0 -32
- package/dist/styles/margin.js.map +0 -1
- package/dist/styles/outline.d.ts +0 -29
- package/dist/styles/outline.js +0 -55
- package/dist/styles/outline.js.map +0 -1
- package/dist/styles/padding.d.ts +0 -24
- package/dist/styles/padding.js +0 -32
- package/dist/styles/padding.js.map +0 -1
- package/dist/styles/placement.d.ts +0 -37
- package/dist/styles/placement.js +0 -74
- package/dist/styles/placement.js.map +0 -1
- package/dist/styles/predefined.d.ts +0 -71
- package/dist/styles/predefined.js +0 -237
- package/dist/styles/predefined.js.map +0 -1
- package/dist/styles/preset.d.ts +0 -52
- package/dist/styles/preset.js +0 -127
- package/dist/styles/preset.js.map +0 -1
- package/dist/styles/radius.d.ts +0 -12
- package/dist/styles/radius.js +0 -83
- package/dist/styles/radius.js.map +0 -1
- package/dist/styles/scrollMargin.d.ts +0 -24
- package/dist/styles/scrollMargin.js +0 -32
- package/dist/styles/scrollMargin.js.map +0 -1
- package/dist/styles/scrollbar.d.ts +0 -25
- package/dist/styles/scrollbar.js +0 -51
- package/dist/styles/scrollbar.js.map +0 -1
- package/dist/styles/shadow.d.ts +0 -14
- package/dist/styles/shadow.js +0 -25
- package/dist/styles/shadow.js.map +0 -1
- package/dist/styles/shared.js +0 -17
- package/dist/styles/shared.js.map +0 -1
- package/dist/styles/transition.d.ts +0 -14
- package/dist/styles/transition.js +0 -159
- package/dist/styles/transition.js.map +0 -1
- package/dist/styles/width.d.ts +0 -17
- package/dist/styles/width.js +0 -19
- package/dist/styles/width.js.map +0 -1
- package/dist/tasty.d.ts +0 -134
- package/dist/tasty.js +0 -248
- package/dist/tasty.js.map +0 -1
- package/dist/types.d.ts +0 -184
- package/dist/utils/cache-wrapper.js +0 -21
- package/dist/utils/cache-wrapper.js.map +0 -1
- package/dist/utils/case-converter.js +0 -8
- package/dist/utils/case-converter.js.map +0 -1
- package/dist/utils/color-math.d.ts +0 -46
- package/dist/utils/color-math.js +0 -749
- package/dist/utils/color-math.js.map +0 -1
- package/dist/utils/color-space.d.ts +0 -5
- package/dist/utils/color-space.js +0 -228
- package/dist/utils/color-space.js.map +0 -1
- package/dist/utils/colors.d.ts +0 -5
- package/dist/utils/colors.js +0 -10
- package/dist/utils/colors.js.map +0 -1
- package/dist/utils/css-types.d.ts +0 -7
- package/dist/utils/deps-equal.js +0 -15
- package/dist/utils/deps-equal.js.map +0 -1
- package/dist/utils/dotize.d.ts +0 -26
- package/dist/utils/dotize.js +0 -122
- package/dist/utils/dotize.js.map +0 -1
- package/dist/utils/filter-base-props.d.ts +0 -15
- package/dist/utils/filter-base-props.js +0 -45
- package/dist/utils/filter-base-props.js.map +0 -1
- package/dist/utils/get-display-name.d.ts +0 -7
- package/dist/utils/get-display-name.js +0 -10
- package/dist/utils/get-display-name.js.map +0 -1
- package/dist/utils/has-keys.js +0 -13
- package/dist/utils/has-keys.js.map +0 -1
- package/dist/utils/hash.js +0 -14
- package/dist/utils/hash.js.map +0 -1
- package/dist/utils/is-dev-env.js +0 -19
- package/dist/utils/is-dev-env.js.map +0 -1
- package/dist/utils/is-valid-element-type.js +0 -15
- package/dist/utils/is-valid-element-type.js.map +0 -1
- package/dist/utils/merge-styles.js.map +0 -1
- package/dist/utils/mod-attrs.d.ts +0 -6
- package/dist/utils/mod-attrs.js +0 -20
- package/dist/utils/mod-attrs.js.map +0 -1
- package/dist/utils/process-tokens.d.ts +0 -17
- package/dist/utils/process-tokens.js +0 -83
- package/dist/utils/process-tokens.js.map +0 -1
- package/dist/utils/resolve-recipes.d.ts +0 -17
- package/dist/utils/resolve-recipes.js.map +0 -1
- package/dist/utils/selector-transform.js +0 -32
- package/dist/utils/selector-transform.js.map +0 -1
- package/dist/utils/string.js +0 -8
- package/dist/utils/string.js.map +0 -1
- package/dist/utils/styles.d.ts +0 -99
- package/dist/utils/styles.js +0 -220
- package/dist/utils/styles.js.map +0 -1
- package/dist/utils/typography.d.ts +0 -58
- package/dist/utils/typography.js +0 -51
- package/dist/utils/typography.js.map +0 -1
- package/dist/utils/warnings.d.ts +0 -16
- package/dist/utils/warnings.js +0 -16
- package/dist/utils/warnings.js.map +0 -1
- package/dist/zero/css-writer.d.ts +0 -45
- package/dist/zero/css-writer.js +0 -73
- package/dist/zero/css-writer.js.map +0 -1
- package/dist/zero/extractor.d.ts +0 -24
- package/dist/zero/extractor.js.map +0 -1
|
@@ -1,725 +0,0 @@
|
|
|
1
|
-
import { Lru } from "../parser/lru.js";
|
|
2
|
-
import { and, createOwnCondition, createParentCondition, createRootCondition, falseCondition, getConditionUniqueId, not, or, trueCondition } from "./conditions.js";
|
|
3
|
-
//#region src/pipeline/simplify.ts
|
|
4
|
-
/**
|
|
5
|
-
* Condition Simplification Engine
|
|
6
|
-
*
|
|
7
|
-
* Simplifies condition trees by applying boolean algebra rules,
|
|
8
|
-
* detecting contradictions, merging ranges, and deduplicating terms.
|
|
9
|
-
*
|
|
10
|
-
* This is critical for:
|
|
11
|
-
* 1. Detecting invalid combinations (A & !A → FALSE)
|
|
12
|
-
* 2. Reducing CSS output size
|
|
13
|
-
* 3. Producing cleaner selectors
|
|
14
|
-
*/
|
|
15
|
-
const simplifyCache = new Lru(5e3);
|
|
16
|
-
/**
|
|
17
|
-
* Simplify a condition tree aggressively.
|
|
18
|
-
*
|
|
19
|
-
* This applies all possible simplification rules:
|
|
20
|
-
* - Boolean algebra (identity, annihilator, idempotent, absorption)
|
|
21
|
-
* - Contradiction detection (A & !A → FALSE)
|
|
22
|
-
* - Tautology detection (A | !A → TRUE)
|
|
23
|
-
* - Range intersection for numeric queries
|
|
24
|
-
* - Attribute value conflict detection
|
|
25
|
-
* - Deduplication and sorting
|
|
26
|
-
*/
|
|
27
|
-
function simplifyCondition(node) {
|
|
28
|
-
const key = getConditionUniqueId(node);
|
|
29
|
-
const cached = simplifyCache.get(key);
|
|
30
|
-
if (cached) return cached;
|
|
31
|
-
const result = simplifyInner(node);
|
|
32
|
-
simplifyCache.set(key, result);
|
|
33
|
-
return result;
|
|
34
|
-
}
|
|
35
|
-
function simplifyInner(node) {
|
|
36
|
-
if (node.kind === "true" || node.kind === "false") return node;
|
|
37
|
-
if (node.kind === "state") {
|
|
38
|
-
if ((node.type === "root" || node.type === "own" || node.type === "parent") && !node.negated) {
|
|
39
|
-
const simplifiedInner = simplifyInner(node.innerCondition);
|
|
40
|
-
if (simplifiedInner.kind === "false") return falseCondition();
|
|
41
|
-
if (simplifiedInner === node.innerCondition) return node;
|
|
42
|
-
if (node.type === "root") return createRootCondition(simplifiedInner, false, node.raw);
|
|
43
|
-
if (node.type === "own") return createOwnCondition(simplifiedInner, false, node.raw);
|
|
44
|
-
return createParentCondition(simplifiedInner, node.direct, false, node.raw);
|
|
45
|
-
}
|
|
46
|
-
return node;
|
|
47
|
-
}
|
|
48
|
-
if (node.kind === "compound") {
|
|
49
|
-
const simplifiedChildren = node.children.map((c) => simplifyInner(c));
|
|
50
|
-
if (node.operator === "AND") return simplifyAnd(simplifiedChildren);
|
|
51
|
-
else return simplifyOr(simplifiedChildren);
|
|
52
|
-
}
|
|
53
|
-
return node;
|
|
54
|
-
}
|
|
55
|
-
function simplifyAnd(children) {
|
|
56
|
-
let terms = [];
|
|
57
|
-
for (const child of children) {
|
|
58
|
-
if (child.kind === "false") return falseCondition();
|
|
59
|
-
if (child.kind === "true") continue;
|
|
60
|
-
if (child.kind === "compound" && child.operator === "AND") terms.push(...child.children);
|
|
61
|
-
else terms.push(child);
|
|
62
|
-
}
|
|
63
|
-
if (terms.length === 0) return trueCondition();
|
|
64
|
-
if (terms.length === 1) return terms[0];
|
|
65
|
-
if (hasContradiction(terms)) return falseCondition();
|
|
66
|
-
if (hasRangeContradiction(terms)) return falseCondition();
|
|
67
|
-
if (hasAttributeConflict(terms)) return falseCondition();
|
|
68
|
-
if (hasContainerStyleConflict(terms)) return falseCondition();
|
|
69
|
-
terms = removeImpliedNegations(terms);
|
|
70
|
-
terms = deduplicateTerms(terms);
|
|
71
|
-
terms = mergeRanges(terms);
|
|
72
|
-
terms = sortTerms(terms);
|
|
73
|
-
terms = applyAbsorptionAnd(terms);
|
|
74
|
-
terms = applyConsensusAnd(terms);
|
|
75
|
-
if (terms.length === 0) return trueCondition();
|
|
76
|
-
if (terms.length === 1) return terms[0];
|
|
77
|
-
return {
|
|
78
|
-
kind: "compound",
|
|
79
|
-
operator: "AND",
|
|
80
|
-
children: terms
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
function simplifyOr(children) {
|
|
84
|
-
let terms = [];
|
|
85
|
-
for (const child of children) {
|
|
86
|
-
if (child.kind === "true") return trueCondition();
|
|
87
|
-
if (child.kind === "false") continue;
|
|
88
|
-
if (child.kind === "compound" && child.operator === "OR") terms.push(...child.children);
|
|
89
|
-
else terms.push(child);
|
|
90
|
-
}
|
|
91
|
-
if (terms.length === 0) return falseCondition();
|
|
92
|
-
if (terms.length === 1) return terms[0];
|
|
93
|
-
if (hasTautology(terms)) return trueCondition();
|
|
94
|
-
terms = deduplicateTerms(terms);
|
|
95
|
-
terms = sortTerms(terms);
|
|
96
|
-
terms = applyAbsorptionOr(terms);
|
|
97
|
-
terms = applyComplementaryFactoring(terms);
|
|
98
|
-
if (terms.length === 0) return falseCondition();
|
|
99
|
-
if (terms.length === 1) return terms[0];
|
|
100
|
-
return {
|
|
101
|
-
kind: "compound",
|
|
102
|
-
operator: "OR",
|
|
103
|
-
children: terms
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Check if any pair of terms has complementary negation (A and !A).
|
|
108
|
-
* Used for both contradiction detection (in AND) and tautology detection (in OR),
|
|
109
|
-
* since the underlying check is identical: the context determines the semantics.
|
|
110
|
-
*/
|
|
111
|
-
function hasComplementaryPair(terms) {
|
|
112
|
-
const uniqueIds = /* @__PURE__ */ new Set();
|
|
113
|
-
for (const term of terms) {
|
|
114
|
-
if (term.kind !== "state") continue;
|
|
115
|
-
const id = term.uniqueId;
|
|
116
|
-
const negatedId = term.negated ? id.slice(1) : `!${id}`;
|
|
117
|
-
if (uniqueIds.has(negatedId)) return true;
|
|
118
|
-
uniqueIds.add(id);
|
|
119
|
-
}
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
const hasContradiction = hasComplementaryPair;
|
|
123
|
-
const hasTautology = hasComplementaryPair;
|
|
124
|
-
/**
|
|
125
|
-
* Check for range contradictions in media/container queries
|
|
126
|
-
* e.g., @media(w < 400px) & @media(w > 800px) → FALSE
|
|
127
|
-
*
|
|
128
|
-
* Also handles negated conditions:
|
|
129
|
-
* - Single-bound negations are inverted (not (w < 600px) → w >= 600px)
|
|
130
|
-
* - Range negations create excluded ranges that are checked against positive bounds
|
|
131
|
-
*/
|
|
132
|
-
function hasRangeContradiction(terms) {
|
|
133
|
-
const mediaByDim = /* @__PURE__ */ new Map();
|
|
134
|
-
const containerByDim = /* @__PURE__ */ new Map();
|
|
135
|
-
for (const term of terms) {
|
|
136
|
-
if (term.kind !== "state") continue;
|
|
137
|
-
if (term.type === "media" && term.subtype === "dimension") {
|
|
138
|
-
const key = term.dimension || "width";
|
|
139
|
-
if (!mediaByDim.has(key)) mediaByDim.set(key, {
|
|
140
|
-
positive: [],
|
|
141
|
-
negated: []
|
|
142
|
-
});
|
|
143
|
-
const group = mediaByDim.get(key);
|
|
144
|
-
if (term.negated) group.negated.push(term);
|
|
145
|
-
else group.positive.push(term);
|
|
146
|
-
}
|
|
147
|
-
if (term.type === "container" && term.subtype === "dimension") {
|
|
148
|
-
const key = `${term.containerName || "_"}:${term.dimension || "width"}`;
|
|
149
|
-
if (!containerByDim.has(key)) containerByDim.set(key, {
|
|
150
|
-
positive: [],
|
|
151
|
-
negated: []
|
|
152
|
-
});
|
|
153
|
-
const group = containerByDim.get(key);
|
|
154
|
-
if (term.negated) group.negated.push(term);
|
|
155
|
-
else group.positive.push(term);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
for (const group of mediaByDim.values()) if (rangesAreImpossibleWithNegations(group.positive, group.negated)) return true;
|
|
159
|
-
for (const group of containerByDim.values()) if (rangesAreImpossibleWithNegations(group.positive, group.negated)) return true;
|
|
160
|
-
return false;
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Check if conditions are impossible, including negated conditions.
|
|
164
|
-
*
|
|
165
|
-
* For negated single-bound conditions:
|
|
166
|
-
* not (w < 600px) → w >= 600px (inverted to lower bound)
|
|
167
|
-
* not (w >= 800px) → w < 800px (inverted to upper bound)
|
|
168
|
-
*
|
|
169
|
-
* For negated range conditions:
|
|
170
|
-
* not (400px <= w < 800px) → excludes [400, 800)
|
|
171
|
-
* If the effective bounds fall entirely within an excluded range, it's impossible.
|
|
172
|
-
*/
|
|
173
|
-
function rangesAreImpossibleWithNegations(positive, negated) {
|
|
174
|
-
const bounds = computeEffectiveBounds(positive);
|
|
175
|
-
const excludedRanges = [];
|
|
176
|
-
for (const cond of negated) {
|
|
177
|
-
const hasLower = cond.lowerBound?.valueNumeric != null;
|
|
178
|
-
const hasUpper = cond.upperBound?.valueNumeric != null;
|
|
179
|
-
if (hasLower && hasUpper) excludedRanges.push({
|
|
180
|
-
lower: cond.lowerBound.valueNumeric,
|
|
181
|
-
lowerInclusive: cond.lowerBound.inclusive,
|
|
182
|
-
upper: cond.upperBound.valueNumeric,
|
|
183
|
-
upperInclusive: cond.upperBound.inclusive
|
|
184
|
-
});
|
|
185
|
-
else if (hasUpper) {
|
|
186
|
-
const value = cond.upperBound.valueNumeric;
|
|
187
|
-
const inclusive = !cond.upperBound.inclusive;
|
|
188
|
-
if (bounds.lowerBound === null || value > bounds.lowerBound) {
|
|
189
|
-
bounds.lowerBound = value;
|
|
190
|
-
bounds.lowerInclusive = inclusive;
|
|
191
|
-
} else if (value === bounds.lowerBound && !inclusive) bounds.lowerInclusive = false;
|
|
192
|
-
} else if (hasLower) {
|
|
193
|
-
const value = cond.lowerBound.valueNumeric;
|
|
194
|
-
const inclusive = !cond.lowerBound.inclusive;
|
|
195
|
-
if (bounds.upperBound === null || value < bounds.upperBound) {
|
|
196
|
-
bounds.upperBound = value;
|
|
197
|
-
bounds.upperInclusive = inclusive;
|
|
198
|
-
} else if (value === bounds.upperBound && !inclusive) bounds.upperInclusive = false;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
if (bounds.lowerBound !== null && bounds.upperBound !== null) {
|
|
202
|
-
if (bounds.lowerBound > bounds.upperBound) return true;
|
|
203
|
-
if (bounds.lowerBound === bounds.upperBound && (!bounds.lowerInclusive || !bounds.upperInclusive)) return true;
|
|
204
|
-
}
|
|
205
|
-
if (bounds.lowerBound !== null && bounds.upperBound !== null && excludedRanges.length > 0) {
|
|
206
|
-
for (const excluded of excludedRanges) if (boundsWithinExcludedRange(bounds, excluded)) return true;
|
|
207
|
-
}
|
|
208
|
-
return false;
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* Compute effective bounds from positive (non-negated) conditions
|
|
212
|
-
*/
|
|
213
|
-
function computeEffectiveBounds(conditions) {
|
|
214
|
-
let lowerBound = null;
|
|
215
|
-
let lowerInclusive = false;
|
|
216
|
-
let upperBound = null;
|
|
217
|
-
let upperInclusive = false;
|
|
218
|
-
for (const cond of conditions) {
|
|
219
|
-
if (cond.lowerBound?.valueNumeric != null) {
|
|
220
|
-
const value = cond.lowerBound.valueNumeric;
|
|
221
|
-
const inclusive = cond.lowerBound.inclusive;
|
|
222
|
-
if (lowerBound === null || value > lowerBound) {
|
|
223
|
-
lowerBound = value;
|
|
224
|
-
lowerInclusive = inclusive;
|
|
225
|
-
} else if (value === lowerBound && !inclusive) lowerInclusive = false;
|
|
226
|
-
}
|
|
227
|
-
if (cond.upperBound?.valueNumeric != null) {
|
|
228
|
-
const value = cond.upperBound.valueNumeric;
|
|
229
|
-
const inclusive = cond.upperBound.inclusive;
|
|
230
|
-
if (upperBound === null || value < upperBound) {
|
|
231
|
-
upperBound = value;
|
|
232
|
-
upperInclusive = inclusive;
|
|
233
|
-
} else if (value === upperBound && !inclusive) upperInclusive = false;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
return {
|
|
237
|
-
lowerBound,
|
|
238
|
-
lowerInclusive,
|
|
239
|
-
upperBound,
|
|
240
|
-
upperInclusive
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Check if effective bounds fall entirely within an excluded range.
|
|
245
|
-
*
|
|
246
|
-
* For example:
|
|
247
|
-
* Effective: [400, 800)
|
|
248
|
-
* Excluded: [400, 800)
|
|
249
|
-
* → bounds fall entirely within excluded range → impossible
|
|
250
|
-
*/
|
|
251
|
-
function boundsWithinExcludedRange(bounds, excluded) {
|
|
252
|
-
if (bounds.lowerBound === null || bounds.upperBound === null) return false;
|
|
253
|
-
let lowerOk = false;
|
|
254
|
-
if (bounds.lowerBound > excluded.lower) lowerOk = true;
|
|
255
|
-
else if (bounds.lowerBound === excluded.lower) lowerOk = excluded.lowerInclusive || !bounds.lowerInclusive;
|
|
256
|
-
let upperOk = false;
|
|
257
|
-
if (bounds.upperBound < excluded.upper) upperOk = true;
|
|
258
|
-
else if (bounds.upperBound === excluded.upper) upperOk = excluded.upperInclusive || !bounds.upperInclusive;
|
|
259
|
-
return lowerOk && upperOk;
|
|
260
|
-
}
|
|
261
|
-
/**
|
|
262
|
-
* Check for attribute value conflicts
|
|
263
|
-
* e.g., [data-theme="dark"] & [data-theme="light"] → FALSE
|
|
264
|
-
* e.g., [data-theme="dark"] & ![data-theme] → FALSE
|
|
265
|
-
*/
|
|
266
|
-
/**
|
|
267
|
-
* Generic value-conflict checker for grouped conditions.
|
|
268
|
-
*
|
|
269
|
-
* Groups terms by a key, splits into positive/negated, then checks:
|
|
270
|
-
* 1. Multiple distinct positive values → conflict
|
|
271
|
-
* 2. Positive value + negated existence (value === undefined) → conflict
|
|
272
|
-
* 3. Positive value + negated same value → conflict
|
|
273
|
-
*/
|
|
274
|
-
function hasGroupedValueConflict(terms, match, groupKey, getValue) {
|
|
275
|
-
const groups = /* @__PURE__ */ new Map();
|
|
276
|
-
for (const term of terms) {
|
|
277
|
-
const matched = match(term);
|
|
278
|
-
if (!matched) continue;
|
|
279
|
-
const key = groupKey(matched);
|
|
280
|
-
let group = groups.get(key);
|
|
281
|
-
if (!group) {
|
|
282
|
-
group = {
|
|
283
|
-
positive: [],
|
|
284
|
-
negated: []
|
|
285
|
-
};
|
|
286
|
-
groups.set(key, group);
|
|
287
|
-
}
|
|
288
|
-
if (matched.negated) group.negated.push(matched);
|
|
289
|
-
else group.positive.push(matched);
|
|
290
|
-
}
|
|
291
|
-
for (const [, group] of groups) {
|
|
292
|
-
const positiveValues = group.positive.map(getValue).filter((v) => v !== void 0);
|
|
293
|
-
if (new Set(positiveValues).size > 1) return true;
|
|
294
|
-
const hasPositiveValue = positiveValues.length > 0;
|
|
295
|
-
const hasNegatedExistence = group.negated.some((t) => getValue(t) === void 0);
|
|
296
|
-
if (hasPositiveValue && hasNegatedExistence) return true;
|
|
297
|
-
for (const pos of group.positive) {
|
|
298
|
-
const posVal = getValue(pos);
|
|
299
|
-
if (posVal !== void 0) {
|
|
300
|
-
for (const neg of group.negated) if (getValue(neg) === posVal) return true;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
return false;
|
|
305
|
-
}
|
|
306
|
-
function hasAttributeConflict(terms) {
|
|
307
|
-
if (hasGroupedValueConflict(terms, (t) => t.kind === "state" && t.type === "modifier" ? t : null, (t) => t.attribute, (t) => t.value)) return true;
|
|
308
|
-
if (hasNestedModifierConflict(terms, "root")) return true;
|
|
309
|
-
if (hasNestedModifierConflict(terms, "own")) return true;
|
|
310
|
-
return false;
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Collect modifier conditions inside non-negated state wrappers of `kind`
|
|
314
|
-
* and check for the same value-conflict pattern as the top-level pass.
|
|
315
|
-
*
|
|
316
|
-
* Modifiers from multiple sibling wrappers are combined — both wrappers
|
|
317
|
-
* scope to the same element (one :root, one own scope), so an attribute
|
|
318
|
-
* conflict across wrappers is still impossible.
|
|
319
|
-
*/
|
|
320
|
-
function hasNestedModifierConflict(terms, wrapperType) {
|
|
321
|
-
const innerTerms = [];
|
|
322
|
-
for (const term of terms) if (term.kind === "state" && term.type === wrapperType && !term.negated) flattenAndIntoArray(term.innerCondition, innerTerms);
|
|
323
|
-
if (innerTerms.length < 2) return false;
|
|
324
|
-
return hasGroupedValueConflict(innerTerms, (t) => t.kind === "state" && t.type === "modifier" ? t : null, (t) => t.attribute, (t) => t.value);
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Flatten a top-level AND tree into a flat list of conjuncts. Non-AND nodes
|
|
328
|
-
* are pushed as-is.
|
|
329
|
-
*/
|
|
330
|
-
function flattenAndIntoArray(node, into) {
|
|
331
|
-
if (node.kind === "compound" && node.operator === "AND") for (const child of node.children) flattenAndIntoArray(child, into);
|
|
332
|
-
else into.push(node);
|
|
333
|
-
}
|
|
334
|
-
function hasContainerStyleConflict(terms) {
|
|
335
|
-
return hasGroupedValueConflict(terms, (t) => t.kind === "state" && t.type === "container" && t.subtype === "style" ? t : null, (t) => `${t.containerName || "_"}:${t.property}`, (t) => t.propertyValue);
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* Remove negations that are implied by positive terms.
|
|
339
|
-
*
|
|
340
|
-
* Key optimizations:
|
|
341
|
-
* 1. style(--variant: danger) implies NOT style(--variant: success)
|
|
342
|
-
* → If we have style(--variant: danger) & not style(--variant: success),
|
|
343
|
-
* the negation is redundant and can be removed.
|
|
344
|
-
*
|
|
345
|
-
* 2. [data-theme="dark"] implies NOT [data-theme="light"]
|
|
346
|
-
* → Same logic for attribute selectors.
|
|
347
|
-
*
|
|
348
|
-
* This produces cleaner CSS:
|
|
349
|
-
* Before: @container style(--variant: danger) and (not style(--variant: success))
|
|
350
|
-
* After: @container style(--variant: danger)
|
|
351
|
-
*/
|
|
352
|
-
/**
|
|
353
|
-
* Collect positive values from terms and build a "is this negation implied?" check.
|
|
354
|
-
*
|
|
355
|
-
* A negation is implied (redundant) when a positive term for the same group
|
|
356
|
-
* already pins a specific value, making "NOT other-value" obvious.
|
|
357
|
-
* e.g. style(--variant: danger) implies NOT style(--variant: success).
|
|
358
|
-
*/
|
|
359
|
-
function buildImpliedNegationCheck(terms) {
|
|
360
|
-
const positiveValues = /* @__PURE__ */ new Map();
|
|
361
|
-
for (const term of terms) {
|
|
362
|
-
if (term.kind !== "state" || term.negated) continue;
|
|
363
|
-
if (term.type === "container" && term.subtype === "style") {
|
|
364
|
-
if (term.propertyValue !== void 0) positiveValues.set(`c:${term.containerName || "_"}:${term.property}`, term.propertyValue);
|
|
365
|
-
} else if (term.type === "modifier" && term.value !== void 0) positiveValues.set(`m:${term.attribute}`, term.value);
|
|
366
|
-
}
|
|
367
|
-
return (term) => {
|
|
368
|
-
if (term.kind !== "state" || !term.negated) return false;
|
|
369
|
-
if (term.type === "container" && term.subtype === "style") {
|
|
370
|
-
if (term.propertyValue === void 0) return false;
|
|
371
|
-
const pos = positiveValues.get(`c:${term.containerName || "_"}:${term.property}`);
|
|
372
|
-
return pos !== void 0 && term.propertyValue !== pos;
|
|
373
|
-
}
|
|
374
|
-
if (term.type === "modifier" && term.value !== void 0) {
|
|
375
|
-
const pos = positiveValues.get(`m:${term.attribute}`);
|
|
376
|
-
return pos !== void 0 && term.value !== pos;
|
|
377
|
-
}
|
|
378
|
-
return false;
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
function removeImpliedNegations(terms) {
|
|
382
|
-
const isImplied = buildImpliedNegationCheck(terms);
|
|
383
|
-
return terms.filter((t) => !isImplied(t));
|
|
384
|
-
}
|
|
385
|
-
function deduplicateTerms(terms) {
|
|
386
|
-
const seen = /* @__PURE__ */ new Set();
|
|
387
|
-
const result = [];
|
|
388
|
-
for (const term of terms) {
|
|
389
|
-
const id = getConditionUniqueId(term);
|
|
390
|
-
if (!seen.has(id)) {
|
|
391
|
-
seen.add(id);
|
|
392
|
-
result.push(term);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
return result;
|
|
396
|
-
}
|
|
397
|
-
/**
|
|
398
|
-
* Merge compatible range conditions
|
|
399
|
-
* e.g., @media(w >= 400px) & @media(w <= 800px) → @media(400px <= w <= 800px)
|
|
400
|
-
*/
|
|
401
|
-
function mergeRanges(terms) {
|
|
402
|
-
const mediaByDim = /* @__PURE__ */ new Map();
|
|
403
|
-
const containerByDim = /* @__PURE__ */ new Map();
|
|
404
|
-
terms.forEach((term, index) => {
|
|
405
|
-
if (term.kind !== "state") return;
|
|
406
|
-
if (term.type === "media" && term.subtype === "dimension" && !term.negated) {
|
|
407
|
-
const key = term.dimension || "width";
|
|
408
|
-
if (!mediaByDim.has(key)) mediaByDim.set(key, {
|
|
409
|
-
conditions: [],
|
|
410
|
-
indices: []
|
|
411
|
-
});
|
|
412
|
-
const group = mediaByDim.get(key);
|
|
413
|
-
group.conditions.push(term);
|
|
414
|
-
group.indices.push(index);
|
|
415
|
-
}
|
|
416
|
-
if (term.type === "container" && term.subtype === "dimension" && !term.negated) {
|
|
417
|
-
const key = `${term.containerName || "_"}:${term.dimension || "width"}`;
|
|
418
|
-
if (!containerByDim.has(key)) containerByDim.set(key, {
|
|
419
|
-
conditions: [],
|
|
420
|
-
indices: []
|
|
421
|
-
});
|
|
422
|
-
const group = containerByDim.get(key);
|
|
423
|
-
group.conditions.push(term);
|
|
424
|
-
group.indices.push(index);
|
|
425
|
-
}
|
|
426
|
-
});
|
|
427
|
-
const indicesToRemove = /* @__PURE__ */ new Set();
|
|
428
|
-
const mergedTerms = [];
|
|
429
|
-
for (const [_dim, group] of mediaByDim) if (group.conditions.length > 1) {
|
|
430
|
-
const merged = mergeMediaRanges(group.conditions);
|
|
431
|
-
if (merged) {
|
|
432
|
-
group.indices.forEach((i) => indicesToRemove.add(i));
|
|
433
|
-
mergedTerms.push(merged);
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
for (const [, group] of containerByDim) if (group.conditions.length > 1) {
|
|
437
|
-
const merged = mergeContainerRanges(group.conditions);
|
|
438
|
-
if (merged) {
|
|
439
|
-
group.indices.forEach((i) => indicesToRemove.add(i));
|
|
440
|
-
mergedTerms.push(merged);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
const result = [];
|
|
444
|
-
terms.forEach((term, index) => {
|
|
445
|
-
if (!indicesToRemove.has(index)) result.push(term);
|
|
446
|
-
});
|
|
447
|
-
result.push(...mergedTerms);
|
|
448
|
-
return result;
|
|
449
|
-
}
|
|
450
|
-
/**
|
|
451
|
-
* Tighten bounds by picking the most restrictive lower and upper bounds
|
|
452
|
-
* from a set of conditions that have lowerBound/upperBound fields.
|
|
453
|
-
*/
|
|
454
|
-
function tightenBounds(conditions) {
|
|
455
|
-
let lowerBound;
|
|
456
|
-
let upperBound;
|
|
457
|
-
for (const cond of conditions) {
|
|
458
|
-
if (cond.lowerBound) {
|
|
459
|
-
if (!lowerBound || (cond.lowerBound.valueNumeric ?? -Infinity) > (lowerBound.valueNumeric ?? -Infinity)) lowerBound = cond.lowerBound;
|
|
460
|
-
}
|
|
461
|
-
if (cond.upperBound) {
|
|
462
|
-
if (!upperBound || (cond.upperBound.valueNumeric ?? Infinity) < (upperBound.valueNumeric ?? Infinity)) upperBound = cond.upperBound;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
return {
|
|
466
|
-
lowerBound,
|
|
467
|
-
upperBound
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
function appendBoundsToUniqueId(parts, lowerBound, upperBound) {
|
|
471
|
-
if (lowerBound) {
|
|
472
|
-
parts.push(lowerBound.inclusive ? ">=" : ">");
|
|
473
|
-
parts.push(lowerBound.value);
|
|
474
|
-
}
|
|
475
|
-
if (upperBound) {
|
|
476
|
-
parts.push(upperBound.inclusive ? "<=" : "<");
|
|
477
|
-
parts.push(upperBound.value);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
function mergeDimensionRanges(conditions, idPrefix) {
|
|
481
|
-
if (conditions.length === 0) return null;
|
|
482
|
-
const { lowerBound, upperBound } = tightenBounds(conditions);
|
|
483
|
-
const base = conditions[0];
|
|
484
|
-
const parts = [...idPrefix];
|
|
485
|
-
appendBoundsToUniqueId(parts, lowerBound, upperBound);
|
|
486
|
-
return {
|
|
487
|
-
...base,
|
|
488
|
-
negated: false,
|
|
489
|
-
raw: buildMergedRaw(base.dimension || "width", lowerBound, upperBound),
|
|
490
|
-
uniqueId: parts.join(":"),
|
|
491
|
-
lowerBound,
|
|
492
|
-
upperBound
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
function mergeMediaRanges(conditions) {
|
|
496
|
-
return mergeDimensionRanges(conditions, [
|
|
497
|
-
"media",
|
|
498
|
-
"dim",
|
|
499
|
-
conditions[0]?.dimension ?? "width"
|
|
500
|
-
]);
|
|
501
|
-
}
|
|
502
|
-
function mergeContainerRanges(conditions) {
|
|
503
|
-
const base = conditions[0];
|
|
504
|
-
if (!base) return null;
|
|
505
|
-
return mergeDimensionRanges(conditions, [
|
|
506
|
-
"container",
|
|
507
|
-
"dim",
|
|
508
|
-
base.containerName || "_",
|
|
509
|
-
base.dimension ?? "width"
|
|
510
|
-
]);
|
|
511
|
-
}
|
|
512
|
-
function buildMergedRaw(dimension, lowerBound, upperBound) {
|
|
513
|
-
if (lowerBound && upperBound) {
|
|
514
|
-
const lowerOp = lowerBound.inclusive ? "<=" : "<";
|
|
515
|
-
const upperOp = upperBound.inclusive ? "<=" : "<";
|
|
516
|
-
return `@media(${lowerBound.value} ${lowerOp} ${dimension} ${upperOp} ${upperBound.value})`;
|
|
517
|
-
} else if (upperBound) return `@media(${dimension} ${upperBound.inclusive ? "<=" : "<"} ${upperBound.value})`;
|
|
518
|
-
else if (lowerBound) return `@media(${dimension} ${lowerBound.inclusive ? ">=" : ">"} ${lowerBound.value})`;
|
|
519
|
-
return "@media()";
|
|
520
|
-
}
|
|
521
|
-
/**
|
|
522
|
-
* Apply complementary factoring: (A & B) | (A & !B) → A
|
|
523
|
-
*
|
|
524
|
-
* Finds pairs of AND compounds that share all children except one,
|
|
525
|
-
* where the differing child is complementary (X vs !X).
|
|
526
|
-
* Replaces the pair with the common terms.
|
|
527
|
-
*
|
|
528
|
-
* This is applied iteratively until no more reductions are possible,
|
|
529
|
-
* since factoring can expose further simplification opportunities.
|
|
530
|
-
*/
|
|
531
|
-
function applyComplementaryFactoring(terms) {
|
|
532
|
-
let changed = true;
|
|
533
|
-
while (changed) {
|
|
534
|
-
changed = false;
|
|
535
|
-
for (let i = 0; i < terms.length; i++) {
|
|
536
|
-
const a = terms[i];
|
|
537
|
-
if (a.kind !== "compound" || a.operator !== "AND") continue;
|
|
538
|
-
for (let j = i + 1; j < terms.length; j++) {
|
|
539
|
-
const b = terms[j];
|
|
540
|
-
if (b.kind !== "compound" || b.operator !== "AND") continue;
|
|
541
|
-
const factored = tryFactorPair(a.children, b.children);
|
|
542
|
-
if (factored) {
|
|
543
|
-
const replacement = simplifyInner(factored);
|
|
544
|
-
terms = [
|
|
545
|
-
...terms.slice(0, i),
|
|
546
|
-
...terms.slice(i + 1, j),
|
|
547
|
-
...terms.slice(j + 1),
|
|
548
|
-
replacement
|
|
549
|
-
];
|
|
550
|
-
changed = true;
|
|
551
|
-
break;
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
if (changed) break;
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
return terms;
|
|
558
|
-
}
|
|
559
|
-
/**
|
|
560
|
-
* Try to factor two AND children lists.
|
|
561
|
-
*
|
|
562
|
-
* Extracts the common children (by uniqueId). If the remaining
|
|
563
|
-
* (non-common) parts of each side OR to TRUE, the common part alone
|
|
564
|
-
* is sufficient: `(common & restA) | (common & restB) → common`
|
|
565
|
-
* when `restA | restB → TRUE`.
|
|
566
|
-
*
|
|
567
|
-
* Also handles the simpler case where exactly one child is
|
|
568
|
-
* complementary: `[A, B, C]` and `[A, !B, C]` → `AND(A, C)`.
|
|
569
|
-
*/
|
|
570
|
-
function tryFactorPair(aChildren, bChildren) {
|
|
571
|
-
const aIds = aChildren.map((c) => getConditionUniqueId(c));
|
|
572
|
-
const bIds = bChildren.map((c) => getConditionUniqueId(c));
|
|
573
|
-
const bIdSet = new Set(bIds);
|
|
574
|
-
const aIdSet = new Set(aIds);
|
|
575
|
-
const commonIndicesA = [];
|
|
576
|
-
const restIndicesA = [];
|
|
577
|
-
for (let i = 0; i < aIds.length; i++) if (bIdSet.has(aIds[i])) commonIndicesA.push(i);
|
|
578
|
-
else restIndicesA.push(i);
|
|
579
|
-
const restIndicesB = [];
|
|
580
|
-
for (let i = 0; i < bIds.length; i++) if (!aIdSet.has(bIds[i])) restIndicesB.push(i);
|
|
581
|
-
if (commonIndicesA.length === 0) return null;
|
|
582
|
-
if (restIndicesA.length === 0 && restIndicesB.length === 0) return null;
|
|
583
|
-
const restA = restIndicesA.length === 0 ? trueCondition() : restIndicesA.length === 1 ? aChildren[restIndicesA[0]] : and(...restIndicesA.map((i) => aChildren[i]));
|
|
584
|
-
const restB = restIndicesB.length === 0 ? trueCondition() : restIndicesB.length === 1 ? bChildren[restIndicesB[0]] : and(...restIndicesB.map((i) => bChildren[i]));
|
|
585
|
-
if (simplifyInner({
|
|
586
|
-
kind: "compound",
|
|
587
|
-
operator: "OR",
|
|
588
|
-
children: [restA, restB]
|
|
589
|
-
}).kind !== "true") {
|
|
590
|
-
const simplifiedRestB = simplifyInner(restB);
|
|
591
|
-
if (getConditionUniqueId(simplifyInner(not(restA))) !== getConditionUniqueId(simplifiedRestB)) return null;
|
|
592
|
-
}
|
|
593
|
-
const common = commonIndicesA.map((i) => aChildren[i]);
|
|
594
|
-
if (common.length === 0) return trueCondition();
|
|
595
|
-
if (common.length === 1) return common[0];
|
|
596
|
-
return and(...common);
|
|
597
|
-
}
|
|
598
|
-
function sortTerms(terms) {
|
|
599
|
-
const withIds = terms.map((t) => [getConditionUniqueId(t), t]);
|
|
600
|
-
withIds.sort((a, b) => a[0].localeCompare(b[0]));
|
|
601
|
-
return withIds.map(([, t]) => t);
|
|
602
|
-
}
|
|
603
|
-
/**
|
|
604
|
-
* Apply the absorption law: removes compound terms that are absorbed by
|
|
605
|
-
* another term already present (simple or compound).
|
|
606
|
-
*
|
|
607
|
-
* For AND context: A & (A | B) → A (absorbs OR compounds)
|
|
608
|
-
* For OR context: A | (A & B) → A (absorbs AND compounds)
|
|
609
|
-
*
|
|
610
|
-
* After flattening, a compound A = OR(X, Y) becomes [X, Y, ...] in the
|
|
611
|
-
* outer OR. A child AND(A, B) = AND(OR(X, Y), B) still references the
|
|
612
|
-
* original un-flattened compound. We reconstruct possible compound
|
|
613
|
-
* absorbers from the flattened terms so absorption works across nesting.
|
|
614
|
-
*/
|
|
615
|
-
function applyAbsorption(terms, absorbedOperator) {
|
|
616
|
-
const absorberIds = /* @__PURE__ */ new Set();
|
|
617
|
-
for (const term of terms) absorberIds.add(getConditionUniqueId(term));
|
|
618
|
-
let changed = true;
|
|
619
|
-
while (changed) {
|
|
620
|
-
changed = false;
|
|
621
|
-
for (const term of terms) {
|
|
622
|
-
if (term.kind !== "compound" || term.operator !== absorbedOperator) continue;
|
|
623
|
-
for (const child of term.children) {
|
|
624
|
-
if (child.kind !== "compound") continue;
|
|
625
|
-
const childId = getConditionUniqueId(child);
|
|
626
|
-
if (absorberIds.has(childId)) continue;
|
|
627
|
-
if (child.children.every((c) => absorberIds.has(getConditionUniqueId(c)))) {
|
|
628
|
-
absorberIds.add(childId);
|
|
629
|
-
changed = true;
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
return terms.filter((term) => {
|
|
635
|
-
if (term.kind === "compound" && term.operator === absorbedOperator) {
|
|
636
|
-
for (const child of term.children) if (absorberIds.has(getConditionUniqueId(child))) return false;
|
|
637
|
-
}
|
|
638
|
-
return true;
|
|
639
|
-
});
|
|
640
|
-
}
|
|
641
|
-
function applyAbsorptionAnd(terms) {
|
|
642
|
-
return applyAbsorption(terms, "OR");
|
|
643
|
-
}
|
|
644
|
-
function applyAbsorptionOr(terms) {
|
|
645
|
-
return applyAbsorption(terms, "AND");
|
|
646
|
-
}
|
|
647
|
-
/**
|
|
648
|
-
* Apply the consensus/resolution rule for AND:
|
|
649
|
-
* (A | B) & (A | !B) → A
|
|
650
|
-
*
|
|
651
|
-
* This is the dual of complementary factoring in OR context:
|
|
652
|
-
* (A & B) | (A & !B) → A
|
|
653
|
-
*
|
|
654
|
-
* Extracts common children from two OR terms. If the remaining
|
|
655
|
-
* parts of each side AND to FALSE, the common part alone is
|
|
656
|
-
* sufficient.
|
|
657
|
-
*/
|
|
658
|
-
function applyConsensusAnd(terms) {
|
|
659
|
-
let changed = true;
|
|
660
|
-
while (changed) {
|
|
661
|
-
changed = false;
|
|
662
|
-
for (let i = 0; i < terms.length; i++) {
|
|
663
|
-
const a = terms[i];
|
|
664
|
-
if (a.kind !== "compound" || a.operator !== "OR") continue;
|
|
665
|
-
for (let j = i + 1; j < terms.length; j++) {
|
|
666
|
-
const b = terms[j];
|
|
667
|
-
if (b.kind !== "compound" || b.operator !== "OR") continue;
|
|
668
|
-
const resolved = tryResolvePair(a.children, b.children);
|
|
669
|
-
if (resolved) {
|
|
670
|
-
const replacement = simplifyInner(resolved);
|
|
671
|
-
terms = [
|
|
672
|
-
...terms.slice(0, i),
|
|
673
|
-
...terms.slice(i + 1, j),
|
|
674
|
-
...terms.slice(j + 1),
|
|
675
|
-
replacement
|
|
676
|
-
];
|
|
677
|
-
changed = true;
|
|
678
|
-
break;
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
if (changed) break;
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
return terms;
|
|
685
|
-
}
|
|
686
|
-
/**
|
|
687
|
-
* Try to resolve two OR children lists.
|
|
688
|
-
*
|
|
689
|
-
* Extracts common children (by uniqueId). If the remaining
|
|
690
|
-
* (non-common) parts AND to FALSE, the common part alone
|
|
691
|
-
* is sufficient: `(common | restA) & (common | restB) → common`
|
|
692
|
-
* when `restA & restB → FALSE`.
|
|
693
|
-
*/
|
|
694
|
-
function tryResolvePair(aChildren, bChildren) {
|
|
695
|
-
const aIds = aChildren.map((c) => getConditionUniqueId(c));
|
|
696
|
-
const bIds = bChildren.map((c) => getConditionUniqueId(c));
|
|
697
|
-
const bIdSet = new Set(bIds);
|
|
698
|
-
const aIdSet = new Set(aIds);
|
|
699
|
-
const commonIndicesA = [];
|
|
700
|
-
const restIndicesA = [];
|
|
701
|
-
for (let i = 0; i < aIds.length; i++) if (bIdSet.has(aIds[i])) commonIndicesA.push(i);
|
|
702
|
-
else restIndicesA.push(i);
|
|
703
|
-
const restIndicesB = [];
|
|
704
|
-
for (let i = 0; i < bIds.length; i++) if (!aIdSet.has(bIds[i])) restIndicesB.push(i);
|
|
705
|
-
if (commonIndicesA.length === 0) return null;
|
|
706
|
-
if (restIndicesA.length === 0 && restIndicesB.length === 0) return null;
|
|
707
|
-
const restA = restIndicesA.length === 0 ? falseCondition() : restIndicesA.length === 1 ? aChildren[restIndicesA[0]] : or(...restIndicesA.map((i) => aChildren[i]));
|
|
708
|
-
const restB = restIndicesB.length === 0 ? falseCondition() : restIndicesB.length === 1 ? bChildren[restIndicesB[0]] : or(...restIndicesB.map((i) => bChildren[i]));
|
|
709
|
-
if (simplifyInner({
|
|
710
|
-
kind: "compound",
|
|
711
|
-
operator: "AND",
|
|
712
|
-
children: [restA, restB]
|
|
713
|
-
}).kind !== "false") {
|
|
714
|
-
const simplifiedRestB = simplifyInner(restB);
|
|
715
|
-
if (getConditionUniqueId(simplifyInner(not(restA))) !== getConditionUniqueId(simplifiedRestB)) return null;
|
|
716
|
-
}
|
|
717
|
-
const common = commonIndicesA.map((i) => aChildren[i]);
|
|
718
|
-
if (common.length === 0) return falseCondition();
|
|
719
|
-
if (common.length === 1) return common[0];
|
|
720
|
-
return or(...common);
|
|
721
|
-
}
|
|
722
|
-
//#endregion
|
|
723
|
-
export { simplifyCondition };
|
|
724
|
-
|
|
725
|
-
//# sourceMappingURL=simplify.js.map
|