@shapeshift-labs/frontier-lang-css 0.1.11 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/postcss-parser-evidence.js +1 -1
- package/dist/semantic-merge-shorthand.js +142 -11
- package/dist/semantic-merge.js +2 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -232,11 +232,11 @@ const merge = safeMergeCssSource({
|
|
|
232
232
|
});
|
|
233
233
|
```
|
|
234
234
|
|
|
235
|
-
`sourceMap.mappings` links emitted rule blocks back to Frontier Lang semantic node ids. `createCssSemanticMergeEvidence` records selectors, specificity, declarations, custom properties, `var()` fallback references, animation/keyframe links, font-face links, URL asset references, cascade keys, statement-form at-rules, CSS Modules exports, ICSS edges, scoped cascade graph proof hashes, source spans, stable hashes, and fail-closed proof gaps for cascade/render-sensitive CSS surfaces. `safeMergeCssSource` admits independent declaration edits by cascade key, including existing scoped `@media` / `@supports` / `@container` / `@layer` declarations when a scoped cascade graph proof hash is supplied, carries source-bound dependency graph evidence into the merge result, preserves unchanged statement-form at-rules such as `@layer reset, components;`, and for `.module.css` files it classifies exported local classes, `composes`, and ICSS import/export records as explicit merge contracts. Cascade-sensitive source-shape changes still block by default, but a host can attach a `css-source-bound-cascade-runtime-proof` / `css-cascade-runtime-proof` that is bound to the exact source path, reason, side, shape key, and base/worker/head/output source hashes to admit that specific change.
|
|
235
|
+
`sourceMap.mappings` links emitted rule blocks back to Frontier Lang semantic node ids. `createCssSemanticMergeEvidence` records selectors, specificity, declarations, custom properties, `var()` fallback references, animation/keyframe links, font-face links, URL asset references, cascade keys, statement-form at-rules, CSS Modules exports, ICSS edges, scoped cascade graph proof hashes, source spans, stable hashes, and fail-closed proof gaps for cascade/render-sensitive CSS surfaces. `safeMergeCssSource` admits independent declaration edits by cascade key, including existing scoped `@media` / `@supports` / `@container` / `@layer` declarations when a scoped cascade graph proof hash is supplied, carries source-bound dependency graph evidence into the merge result, blocks parallel edits whose CSS shorthand expansions overlap a longhand or sub-shorthand, preserves unchanged statement-form at-rules such as `@layer reset, components;`, and for `.module.css` files it classifies exported local classes, `composes`, and ICSS import/export records as explicit merge contracts. Cascade-sensitive source-shape changes still block by default, but a host can attach a `css-source-bound-cascade-runtime-proof` / `css-cascade-runtime-proof` that is bound to the exact source path, reason, side, shape key, and base/worker/head/output source hashes to admit that specific change.
|
|
236
236
|
|
|
237
237
|
## Support Boundary
|
|
238
238
|
|
|
239
239
|
- Ready evidence: style rules, selectors, specificity, declarations, source-bound dependency graph hashes for custom properties, `var()` fallbacks, animations/keyframes, font faces, and URL assets, statement-form at-rules, CSS Modules local exports, generated class-name map coverage, JS/TS use-site graph hashes, composition graph hashes, ICSS graph hashes, scoped cascade graph hashes, source-bound cascade runtime proofs, source spans, stable hashes.
|
|
240
|
-
- Safe merge: independent declarations with non-overlapping cascade keys; unchanged statement-form at-rules preserved in canonical output; existing scoped declaration edits when scoped cascade graph proof is supplied; source-shape/cascade-sensitive edits only when an exact source-bound cascade runtime proof is supplied; explicit CSS Modules export additions/deletions when generated class-name and JS/TS use-site graph proof is supplied; composition edits when composition graph proof is supplied; ICSS edits when ICSS graph proof is supplied. Output is a canonical CSS render and not a byte/trivia-preserving claim.
|
|
241
|
-
- Review-only gaps: incomplete generated class-name maps, unproved CSS Modules use-site graphs, unproved composition or ICSS graphs,
|
|
240
|
+
- Safe merge: independent declarations with non-overlapping cascade keys and non-overlapping shorthand expansion sets; unchanged statement-form at-rules preserved in canonical output; existing scoped declaration edits when scoped cascade graph proof is supplied; source-shape/cascade-sensitive edits only when an exact source-bound cascade runtime proof is supplied; explicit CSS Modules export additions/deletions when generated class-name and JS/TS use-site graph proof is supplied; composition edits when composition graph proof is supplied; ICSS edits when ICSS graph proof is supplied. Output is a canonical CSS render and not a byte/trivia-preserving claim.
|
|
241
|
+
- Review-only gaps: incomplete generated class-name maps, unproved CSS Modules use-site graphs, unproved composition or ICSS graphs, shorthand value expansion/equivalence beyond known affected-property overlap checks, statement-form at-rule order/condition changes, one-sided or structurally changed scoped cascade under `@media` / `@supports` / `@container` / `@layer`, `@keyframes`, `@font-face`, `@page`, browser layout and render equivalence.
|
|
242
242
|
- Claims: dependency graph evidence is an admission/review signal only; `autoMergeClaim`, `semanticEquivalenceClaim`, and `browserRenderEquivalenceClaim` remain false. `browserCascadeEquivalenceClaim` is only true on a safe-merge result when a source-bound cascade runtime proof admits the specific cascade-sensitive source-shape change.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { hashSemanticValue } from '@shapeshift-labs/frontier-lang-kernel';
|
|
2
2
|
import postcss from 'postcss';
|
|
3
3
|
|
|
4
|
-
const ShorthandProperties = new Set(['all', 'animation', 'background', 'border', 'border-block', 'border-color', 'border-image', 'border-inline', 'border-radius', 'border-style', 'border-width', 'columns', 'flex', 'font', 'gap', 'grid', 'grid-area', 'grid-column', 'grid-row', 'inset', 'list-style', 'margin', 'offset', 'outline', 'overflow', 'padding', 'place-content', 'place-items', 'place-self', 'text-decoration', 'transition']);
|
|
4
|
+
const ShorthandProperties = new Set(['all', 'animation', 'background', 'border', 'border-block', 'border-block-color', 'border-block-end', 'border-block-start', 'border-block-style', 'border-block-width', 'border-bottom', 'border-color', 'border-image', 'border-inline', 'border-inline-color', 'border-inline-end', 'border-inline-start', 'border-inline-style', 'border-inline-width', 'border-left', 'border-radius', 'border-right', 'border-style', 'border-top', 'border-width', 'columns', 'flex', 'font', 'gap', 'grid', 'grid-area', 'grid-column', 'grid-row', 'inset', 'inset-block', 'inset-inline', 'list-style', 'margin', 'margin-block', 'margin-inline', 'offset', 'outline', 'overflow', 'overscroll-behavior', 'padding', 'padding-block', 'padding-inline', 'place-content', 'place-items', 'place-self', 'scroll-margin', 'scroll-margin-block', 'scroll-margin-inline', 'scroll-padding', 'scroll-padding-block', 'scroll-padding-inline', 'text-decoration', 'transition']);
|
|
5
5
|
const RuntimeAtRules = new Set(['keyframes', 'font-face', 'page', 'property']);
|
|
6
6
|
const ScopeAtRules = new Set(['media', 'supports', 'container', 'layer', 'scope']);
|
|
7
7
|
|
|
@@ -1,16 +1,147 @@
|
|
|
1
1
|
function shorthandGroupForProperty(property) {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
const normalized = normalizeProperty(property);
|
|
3
|
+
if (!normalized) return undefined;
|
|
4
|
+
if (ShorthandExpansionKeys.has(normalized)) return normalized;
|
|
5
|
+
for (const [group, keys] of ShorthandExpansionKeys) if (keys.has(normalized)) return group;
|
|
4
6
|
return undefined;
|
|
5
7
|
}
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
function declarationsOverlapByCssProperty(leftProperty, rightProperty) {
|
|
10
|
+
const left = normalizeProperty(leftProperty);
|
|
11
|
+
const right = normalizeProperty(rightProperty);
|
|
12
|
+
if (!left || !right) return false;
|
|
13
|
+
if (left === right) return true;
|
|
14
|
+
if (left.startsWith('--') || right.startsWith('--')) return false;
|
|
15
|
+
const leftKeys = affectedPropertyKeys(left);
|
|
16
|
+
const rightKeys = affectedPropertyKeys(right);
|
|
17
|
+
if (leftKeys.has(AllStandardProperties)) return !allPropertyExcluded(right);
|
|
18
|
+
if (rightKeys.has(AllStandardProperties)) return !allPropertyExcluded(left);
|
|
19
|
+
for (const key of leftKeys) if (rightKeys.has(key)) return true;
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function affectedPropertyKeys(property) {
|
|
24
|
+
return ShorthandExpansionKeys.get(property) ?? new Set([property]);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function normalizeProperty(property) {
|
|
28
|
+
return String(property ?? '').trim().toLowerCase();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function allPropertyExcluded(property) {
|
|
32
|
+
return property.startsWith('--') || property === 'direction' || property === 'unicode-bidi';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const AllStandardProperties = '*';
|
|
36
|
+
|
|
37
|
+
const ShorthandExpansionKeys = new Map(Object.entries({
|
|
38
|
+
all: [AllStandardProperties],
|
|
39
|
+
animation: ['animation-composition', 'animation-delay', 'animation-direction', 'animation-duration', 'animation-fill-mode', 'animation-iteration-count', 'animation-name', 'animation-play-state', 'animation-range-end', 'animation-range-start', 'animation-timeline', 'animation-timing-function'],
|
|
40
|
+
background: ['background-attachment', 'background-blend-mode', 'background-clip', 'background-color', 'background-image', 'background-origin', 'background-position', 'background-repeat', 'background-size'],
|
|
41
|
+
border: [
|
|
42
|
+
...edgeKeys('border', ['width', 'style', 'color']),
|
|
43
|
+
'border-width',
|
|
44
|
+
'border-style',
|
|
45
|
+
'border-color',
|
|
46
|
+
'border-top',
|
|
47
|
+
'border-right',
|
|
48
|
+
'border-bottom',
|
|
49
|
+
'border-left'
|
|
50
|
+
],
|
|
51
|
+
'border-block': [...logicalEdgeKeys('border-block', ['width', 'style', 'color']), 'border-block-width', 'border-block-style', 'border-block-color', 'border-block-start', 'border-block-end'],
|
|
52
|
+
'border-block-color': logicalEdgeKeys('border-block', ['color']),
|
|
53
|
+
'border-block-end': propertyTuple('border-block-end', ['width', 'style', 'color']),
|
|
54
|
+
'border-block-end-color': ['border-block-end-color'],
|
|
55
|
+
'border-block-end-style': ['border-block-end-style'],
|
|
56
|
+
'border-block-end-width': ['border-block-end-width'],
|
|
57
|
+
'border-block-start': propertyTuple('border-block-start', ['width', 'style', 'color']),
|
|
58
|
+
'border-block-start-color': ['border-block-start-color'],
|
|
59
|
+
'border-block-start-style': ['border-block-start-style'],
|
|
60
|
+
'border-block-start-width': ['border-block-start-width'],
|
|
61
|
+
'border-block-style': logicalEdgeKeys('border-block', ['style']),
|
|
62
|
+
'border-block-width': logicalEdgeKeys('border-block', ['width']),
|
|
63
|
+
'border-bottom': propertyTuple('border-bottom', ['width', 'style', 'color']),
|
|
64
|
+
'border-bottom-color': ['border-bottom-color'],
|
|
65
|
+
'border-bottom-left-radius': ['border-bottom-left-radius'],
|
|
66
|
+
'border-bottom-right-radius': ['border-bottom-right-radius'],
|
|
67
|
+
'border-bottom-style': ['border-bottom-style'],
|
|
68
|
+
'border-bottom-width': ['border-bottom-width'],
|
|
69
|
+
'border-color': edgeKeys('border', ['color']),
|
|
70
|
+
'border-image': ['border-image-source', 'border-image-slice', 'border-image-width', 'border-image-outset', 'border-image-repeat'],
|
|
71
|
+
'border-inline': [...logicalEdgeKeys('border-inline', ['width', 'style', 'color']), 'border-inline-width', 'border-inline-style', 'border-inline-color', 'border-inline-start', 'border-inline-end'],
|
|
72
|
+
'border-inline-color': logicalEdgeKeys('border-inline', ['color']),
|
|
73
|
+
'border-inline-end': propertyTuple('border-inline-end', ['width', 'style', 'color']),
|
|
74
|
+
'border-inline-end-color': ['border-inline-end-color'],
|
|
75
|
+
'border-inline-end-style': ['border-inline-end-style'],
|
|
76
|
+
'border-inline-end-width': ['border-inline-end-width'],
|
|
77
|
+
'border-inline-start': propertyTuple('border-inline-start', ['width', 'style', 'color']),
|
|
78
|
+
'border-inline-start-color': ['border-inline-start-color'],
|
|
79
|
+
'border-inline-start-style': ['border-inline-start-style'],
|
|
80
|
+
'border-inline-start-width': ['border-inline-start-width'],
|
|
81
|
+
'border-inline-style': logicalEdgeKeys('border-inline', ['style']),
|
|
82
|
+
'border-inline-width': logicalEdgeKeys('border-inline', ['width']),
|
|
83
|
+
'border-left': propertyTuple('border-left', ['width', 'style', 'color']),
|
|
84
|
+
'border-left-color': ['border-left-color'],
|
|
85
|
+
'border-left-style': ['border-left-style'],
|
|
86
|
+
'border-left-width': ['border-left-width'],
|
|
87
|
+
'border-radius': ['border-top-left-radius', 'border-top-right-radius', 'border-bottom-right-radius', 'border-bottom-left-radius'],
|
|
88
|
+
'border-right': propertyTuple('border-right', ['width', 'style', 'color']),
|
|
89
|
+
'border-right-color': ['border-right-color'],
|
|
90
|
+
'border-right-style': ['border-right-style'],
|
|
91
|
+
'border-right-width': ['border-right-width'],
|
|
92
|
+
'border-style': edgeKeys('border', ['style']),
|
|
93
|
+
'border-top': propertyTuple('border-top', ['width', 'style', 'color']),
|
|
94
|
+
'border-top-color': ['border-top-color'],
|
|
95
|
+
'border-top-left-radius': ['border-top-left-radius'],
|
|
96
|
+
'border-top-right-radius': ['border-top-right-radius'],
|
|
97
|
+
'border-top-style': ['border-top-style'],
|
|
98
|
+
'border-top-width': ['border-top-width'],
|
|
99
|
+
'border-width': edgeKeys('border', ['width']),
|
|
100
|
+
columns: ['column-width', 'column-count'],
|
|
101
|
+
flex: ['flex-grow', 'flex-shrink', 'flex-basis'],
|
|
102
|
+
font: ['font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'line-height'],
|
|
103
|
+
gap: ['row-gap', 'column-gap'],
|
|
104
|
+
grid: ['grid-template-rows', 'grid-template-columns', 'grid-template-areas', 'grid-auto-rows', 'grid-auto-columns', 'grid-auto-flow'],
|
|
105
|
+
'grid-area': ['grid-row-start', 'grid-column-start', 'grid-row-end', 'grid-column-end'],
|
|
106
|
+
'grid-column': ['grid-column-start', 'grid-column-end'],
|
|
107
|
+
'grid-row': ['grid-row-start', 'grid-row-end'],
|
|
108
|
+
inset: ['top', 'right', 'bottom', 'left'],
|
|
109
|
+
'inset-block': ['inset-block-start', 'inset-block-end'],
|
|
110
|
+
'inset-inline': ['inset-inline-start', 'inset-inline-end'],
|
|
111
|
+
'list-style': ['list-style-image', 'list-style-position', 'list-style-type'],
|
|
112
|
+
margin: ['margin-top', 'margin-right', 'margin-bottom', 'margin-left'],
|
|
113
|
+
'margin-block': ['margin-block-start', 'margin-block-end'],
|
|
114
|
+
'margin-inline': ['margin-inline-start', 'margin-inline-end'],
|
|
115
|
+
offset: ['offset-anchor', 'offset-distance', 'offset-path', 'offset-position', 'offset-rotate'],
|
|
116
|
+
outline: ['outline-color', 'outline-style', 'outline-width'],
|
|
117
|
+
overflow: ['overflow-x', 'overflow-y'],
|
|
118
|
+
'overscroll-behavior': ['overscroll-behavior-x', 'overscroll-behavior-y'],
|
|
119
|
+
padding: ['padding-top', 'padding-right', 'padding-bottom', 'padding-left'],
|
|
120
|
+
'padding-block': ['padding-block-start', 'padding-block-end'],
|
|
121
|
+
'padding-inline': ['padding-inline-start', 'padding-inline-end'],
|
|
122
|
+
'place-content': ['align-content', 'justify-content'],
|
|
123
|
+
'place-items': ['align-items', 'justify-items'],
|
|
124
|
+
'place-self': ['align-self', 'justify-self'],
|
|
125
|
+
'scroll-margin': ['scroll-margin-top', 'scroll-margin-right', 'scroll-margin-bottom', 'scroll-margin-left'],
|
|
126
|
+
'scroll-margin-block': ['scroll-margin-block-start', 'scroll-margin-block-end'],
|
|
127
|
+
'scroll-margin-inline': ['scroll-margin-inline-start', 'scroll-margin-inline-end'],
|
|
128
|
+
'scroll-padding': ['scroll-padding-top', 'scroll-padding-right', 'scroll-padding-bottom', 'scroll-padding-left'],
|
|
129
|
+
'scroll-padding-block': ['scroll-padding-block-start', 'scroll-padding-block-end'],
|
|
130
|
+
'scroll-padding-inline': ['scroll-padding-inline-start', 'scroll-padding-inline-end'],
|
|
131
|
+
'text-decoration': ['text-decoration-line', 'text-decoration-color', 'text-decoration-style', 'text-decoration-thickness'],
|
|
132
|
+
transition: ['transition-behavior', 'transition-delay', 'transition-duration', 'transition-property', 'transition-timing-function']
|
|
133
|
+
}).map(([property, keys]) => [property, new Set(keys)]));
|
|
134
|
+
|
|
135
|
+
function edgeKeys(prefix, attributes) {
|
|
136
|
+
return ['top', 'right', 'bottom', 'left'].flatMap((edge) => attributes.map((attribute) => `${prefix}-${edge}-${attribute}`));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function logicalEdgeKeys(prefix, attributes) {
|
|
140
|
+
return ['start', 'end'].flatMap((edge) => attributes.map((attribute) => `${prefix}-${edge}-${attribute}`));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function propertyTuple(prefix, attributes) {
|
|
144
|
+
return attributes.map((attribute) => `${prefix}-${attribute}`);
|
|
145
|
+
}
|
|
15
146
|
|
|
16
|
-
export { shorthandGroupForProperty };
|
|
147
|
+
export { declarationsOverlapByCssProperty, shorthandGroupForProperty };
|
package/dist/semantic-merge.js
CHANGED
|
@@ -2,7 +2,7 @@ import { cssModuleContractChanges, cssModuleContractConflicts, sheetOptions, uns
|
|
|
2
2
|
import { admitCascadeRuntimeProofs } from './semantic-merge-cascade-runtime.js';
|
|
3
3
|
import { mergeCssDependencyGraphEvidence } from './dependency-graph.js';
|
|
4
4
|
import { mergeSelectorTargetEvidence, planSelectorTargetRebase } from './semantic-merge-selector-targets.js';
|
|
5
|
-
import { shorthandGroupForProperty } from './semantic-merge-shorthand.js';
|
|
5
|
+
import { declarationsOverlapByCssProperty, shorthandGroupForProperty } from './semantic-merge-shorthand.js';
|
|
6
6
|
|
|
7
7
|
function safeMergeCssSource(input = {}, context = {}) {
|
|
8
8
|
const parseSheet = context.parseCssSemanticSheet;
|
|
@@ -197,9 +197,7 @@ function hasRelatedExistingDeclaration(entry, indexes) {
|
|
|
197
197
|
|
|
198
198
|
function declarationsOverlapByShorthandGroup(left, right) {
|
|
199
199
|
if (!left || !right || left.ruleKey !== right.ruleKey) return false;
|
|
200
|
-
|
|
201
|
-
const rightGroup = shorthandGroupForProperty(right.property);
|
|
202
|
-
return Boolean(leftGroup && rightGroup && leftGroup === rightGroup);
|
|
200
|
+
return declarationsOverlapByCssProperty(left.property, right.property);
|
|
203
201
|
}
|
|
204
202
|
|
|
205
203
|
function applyDeclarationChanges(index, changes) {
|
package/package.json
CHANGED