@shapeshift-labs/frontier-lang-css 0.1.12 → 0.1.14
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 +5 -5
- package/dist/dependency-graph.d.ts +43 -0
- package/dist/dependency-graph.js +131 -6
- package/dist/index.d.ts +6 -1
- package/dist/index.js +1 -1
- package/dist/postcss-parser-evidence.js +1 -0
- package/dist/semantic-merge-at-rules.js +105 -0
- package/dist/semantic-merge-css-modules.js +16 -4
- package/dist/semantic-merge.js +25 -22
- 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, 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.
|
|
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, block-form runtime 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 dependency-affecting declaration edits unless an exact source-bound dependency graph proof is supplied, blocks parallel edits whose CSS shorthand expansions overlap a longhand or sub-shorthand, preserves unchanged statement-form at-rules such as `@layer reset, components;`, preserves unchanged block-form runtime at-rules such as `@keyframes` and `@font-face`, 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
|
-
- 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
|
|
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
|
-
- 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.
|
|
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, block-form runtime 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, non-overlapping shorthand expansion sets, and no changed dependency graph surface; unchanged statement-form at-rules and unchanged block-form runtime at-rules preserved in canonical output; existing scoped declaration edits when scoped cascade graph proof is supplied; dependency-affecting declaration edits only when an exact source-bound dependency 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 dependency graph changes, 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`, changed runtime blocks such as `@keyframes`, `@font-face`, `@page`, and `@property`, browser layout and render equivalence.
|
|
242
|
+
- Claims: dependency graph evidence is an admission/review signal only unless an exact source-bound dependency graph proof admits the changed dependency surface; `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.
|
|
@@ -30,6 +30,15 @@ export interface CssDependencyGraphRecordSets {
|
|
|
30
30
|
readonly urlAssetReferences?: readonly CssDependencyGraphRecord[];
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
export interface CssDependencyGraphChange {
|
|
34
|
+
readonly side: 'worker' | 'head' | string;
|
|
35
|
+
readonly cascadeKey: string;
|
|
36
|
+
readonly reasonCode: 'css-dependency-graph-proof-unproved' | string;
|
|
37
|
+
readonly changeKind?: string;
|
|
38
|
+
readonly before?: Readonly<Record<string, unknown>>;
|
|
39
|
+
readonly after?: Readonly<Record<string, unknown>>;
|
|
40
|
+
}
|
|
41
|
+
|
|
33
42
|
export interface CssDependencyGraphEvidence {
|
|
34
43
|
readonly kind: 'frontier.lang.cssDependencyGraphEvidence' | 'frontier.lang.cssSafeMergeDependencyGraphEvidence' | string;
|
|
35
44
|
readonly version: 1;
|
|
@@ -42,6 +51,7 @@ export interface CssDependencyGraphEvidence {
|
|
|
42
51
|
readonly dependencyGraphHash?: string;
|
|
43
52
|
readonly cssDependencyGraphHash?: string;
|
|
44
53
|
readonly changedDependencySurfaceCount?: number;
|
|
54
|
+
readonly changedDependencySurfaces?: readonly CssDependencyGraphChange[];
|
|
45
55
|
readonly customPropertyDefinitions?: number;
|
|
46
56
|
readonly customPropertyReferences?: number;
|
|
47
57
|
readonly varReferences?: number;
|
|
@@ -58,3 +68,36 @@ export interface CssDependencyGraphEvidence {
|
|
|
58
68
|
readonly browserRenderEquivalenceClaim?: false;
|
|
59
69
|
readonly semanticEquivalenceClaim: false;
|
|
60
70
|
}
|
|
71
|
+
|
|
72
|
+
export interface CssDependencyGraphProof {
|
|
73
|
+
readonly id?: string;
|
|
74
|
+
readonly kind: 'css-dependency-graph-proof' | 'css-source-bound-dependency-graph-proof' | string;
|
|
75
|
+
readonly status: 'passed' | string;
|
|
76
|
+
readonly proofLevel?: string;
|
|
77
|
+
readonly sourcePath?: string;
|
|
78
|
+
readonly reasonCode?: string; readonly reasonCodes?: readonly string[];
|
|
79
|
+
readonly side?: 'worker' | 'head' | string; readonly sides?: readonly string[];
|
|
80
|
+
readonly cascadeKey?: string; readonly cascadeKeys?: readonly string[];
|
|
81
|
+
readonly dependencyKey?: string; readonly dependencyKeys?: readonly string[];
|
|
82
|
+
readonly baseSourceText?: string; readonly workerSourceText?: string; readonly headSourceText?: string; readonly outputSourceText?: string; readonly mergedSourceText?: string;
|
|
83
|
+
readonly baseSourceHash?: string; readonly workerSourceHash?: string; readonly headSourceHash?: string; readonly outputSourceHash?: string; readonly mergedSourceHash?: string;
|
|
84
|
+
readonly sourceTexts?: Readonly<Record<string, string>>; readonly sources?: Readonly<Record<string, string>>;
|
|
85
|
+
readonly sourceHashes?: Readonly<Record<string, string>>; readonly hashes?: Readonly<Record<string, string>>;
|
|
86
|
+
readonly baseDependencyGraphHash?: string; readonly workerDependencyGraphHash?: string; readonly headDependencyGraphHash?: string;
|
|
87
|
+
readonly baseCssDependencyGraphHash?: string; readonly workerCssDependencyGraphHash?: string; readonly headCssDependencyGraphHash?: string;
|
|
88
|
+
readonly dependencyGraphHashes?: Readonly<Record<string, string>>;
|
|
89
|
+
readonly cssDependencyGraphHashes?: Readonly<Record<string, string>>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface CssDependencyGraphProofRecord {
|
|
93
|
+
readonly id?: string;
|
|
94
|
+
readonly kind: string;
|
|
95
|
+
readonly status: 'passed';
|
|
96
|
+
readonly proofLevel: string;
|
|
97
|
+
readonly reasonCode: string;
|
|
98
|
+
readonly side: string;
|
|
99
|
+
readonly cascadeKey: string;
|
|
100
|
+
readonly sourcePath?: string;
|
|
101
|
+
readonly baseSourceHash?: string; readonly workerSourceHash?: string; readonly headSourceHash?: string; readonly outputSourceHash?: string;
|
|
102
|
+
readonly baseDependencyGraphHash?: string; readonly workerDependencyGraphHash?: string; readonly headDependencyGraphHash?: string;
|
|
103
|
+
}
|
package/dist/dependency-graph.js
CHANGED
|
@@ -72,8 +72,7 @@ function mergeCssDependencyGraphEvidence(sheets, changed = {}) {
|
|
|
72
72
|
const sides = Object.fromEntries(Object.entries(sheets).map(([side, sheet]) => [side, sheet.dependencyGraphEvidence ?? emptyGraphEvidence(sheet)]));
|
|
73
73
|
const sideValues = Object.values(sides);
|
|
74
74
|
const hasDependencySurface = sideValues.some((side) => side.hasDependencySurface === true);
|
|
75
|
-
const
|
|
76
|
-
const dependencyKeys = new Set(sideValues.flatMap(dependencySurfaceKeys));
|
|
75
|
+
const changedDependencySurfaces = changedDependencyGraphSurfaces(sides, changed);
|
|
77
76
|
return {
|
|
78
77
|
kind: 'frontier.lang.cssSafeMergeDependencyGraphEvidence',
|
|
79
78
|
version: 1,
|
|
@@ -81,7 +80,8 @@ function mergeCssDependencyGraphEvidence(sheets, changed = {}) {
|
|
|
81
80
|
dependencySurfaceCount: Math.max(0, ...sideValues.map((side) => side.dependencySurfaceCount ?? 0)),
|
|
82
81
|
dependencyGraphHashPresent: hasDependencySurface && sideValues.every((side) => side.hasDependencySurface !== true || side.dependencyGraphHashPresent === true),
|
|
83
82
|
cssDependencyGraphHashPresent: hasDependencySurface && sideValues.every((side) => side.hasDependencySurface !== true || side.cssDependencyGraphHashPresent === true),
|
|
84
|
-
changedDependencySurfaceCount:
|
|
83
|
+
changedDependencySurfaceCount: changedDependencySurfaces.length,
|
|
84
|
+
changedDependencySurfaces,
|
|
85
85
|
sides,
|
|
86
86
|
browserCascadeEquivalenceClaim: false,
|
|
87
87
|
browserRenderEquivalenceClaim: false,
|
|
@@ -89,7 +89,37 @@ function mergeCssDependencyGraphEvidence(sheets, changed = {}) {
|
|
|
89
89
|
};
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
function
|
|
92
|
+
function admitCssDependencyGraphProofs({ id, sourcePath, input, dependencyGraphEvidence, binding, hash }) {
|
|
93
|
+
const proofs = dependencyGraphProofCandidates(input, sourcePath);
|
|
94
|
+
const admitted = [];
|
|
95
|
+
const conflicts = [];
|
|
96
|
+
for (const change of dependencyGraphEvidence.changedDependencySurfaces ?? []) {
|
|
97
|
+
const proof = proofs.find((candidate) => isDependencyGraphProofForChange(candidate, change, sourcePath, dependencyGraphEvidence, binding, hash));
|
|
98
|
+
if (proof) admitted.push(dependencyGraphProofRecord(proof, change, sourcePath, dependencyGraphEvidence, binding, hash));
|
|
99
|
+
else conflicts.push(conflict(id, sourcePath, 'css-dependency-graph-proof-blocked', change.reasonCode, change));
|
|
100
|
+
}
|
|
101
|
+
return { proofs: admitted, conflicts };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function changedDependencyGraphSurfaces(sides, changed) {
|
|
105
|
+
return Object.entries(changed).flatMap(([side, changes]) => (changes ?? []).flatMap((change) => {
|
|
106
|
+
const cascadeKey = change.after?.key ?? change.before?.key;
|
|
107
|
+
if (!cascadeKey) return [];
|
|
108
|
+
const before = dependencyRecordsForKey(sides.base, cascadeKey);
|
|
109
|
+
const after = dependencyRecordsForKey(sides[side], cascadeKey);
|
|
110
|
+
if (!before.length && !after.length) return [];
|
|
111
|
+
return [{
|
|
112
|
+
side,
|
|
113
|
+
cascadeKey,
|
|
114
|
+
reasonCode: 'css-dependency-graph-proof-unproved',
|
|
115
|
+
changeKind: change.kind,
|
|
116
|
+
before: dependencyRecordSummary(before),
|
|
117
|
+
after: dependencyRecordSummary(after)
|
|
118
|
+
}];
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function dependencySurfaceRecords(evidence) {
|
|
93
123
|
const records = evidence.records ?? {};
|
|
94
124
|
return [
|
|
95
125
|
...(records.customPropertyDefinitions ?? []),
|
|
@@ -97,7 +127,99 @@ function dependencySurfaceKeys(evidence) {
|
|
|
97
127
|
...(records.animationNameLinks ?? []),
|
|
98
128
|
...(records.fontFaceLinks ?? []),
|
|
99
129
|
...(records.urlAssetReferences ?? [])
|
|
100
|
-
]
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function dependencyRecordsForKey(evidence, cascadeKey) {
|
|
134
|
+
return dependencySurfaceRecords(evidence).filter((entry) => entry.cascadeKey === cascadeKey);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function dependencyRecordSummary(records) {
|
|
138
|
+
return {
|
|
139
|
+
dependencyKinds: unique(records.map((entry) => entry.kind)),
|
|
140
|
+
names: unique(records.map((entry) => entry.name ?? entry.family ?? entry.url)),
|
|
141
|
+
properties: unique(records.map((entry) => entry.property)),
|
|
142
|
+
declarationHashes: unique(records.map((entry) => entry.declarationHash))
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function dependencyGraphProofCandidates(input = {}, sourcePath) {
|
|
147
|
+
return [
|
|
148
|
+
input.cssDependencyGraphProof,
|
|
149
|
+
input.cssDependencyGraphProofs,
|
|
150
|
+
input.cssDependencyGraphProofsByPath?.[sourcePath],
|
|
151
|
+
input.cssSourceBoundDependencyGraphProof,
|
|
152
|
+
input.cssSourceBoundDependencyGraphProofs,
|
|
153
|
+
input.cssSourceBoundDependencyGraphProofsByPath?.[sourcePath],
|
|
154
|
+
input.dependencyGraphProof,
|
|
155
|
+
input.dependencyGraphProofs,
|
|
156
|
+
input.dependencyGraphProofsByPath?.[sourcePath],
|
|
157
|
+
input.sourceBoundDependencyGraphProof,
|
|
158
|
+
input.sourceBoundDependencyGraphProofs,
|
|
159
|
+
input.sourceBoundDependencyGraphProofsByPath?.[sourcePath]
|
|
160
|
+
].flatMap(asArray).filter(Boolean);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function isDependencyGraphProofForChange(proof, change, sourcePath, evidence, binding, hash) {
|
|
164
|
+
return Boolean(proof && typeof proof === 'object') &&
|
|
165
|
+
DependencyGraphProofKinds.has(proof.kind) &&
|
|
166
|
+
proof.status === 'passed' &&
|
|
167
|
+
proof.sourcePath === sourcePath &&
|
|
168
|
+
proofCoversValue(proof.reasonCode, proof.reasonCodes, change.reasonCode) &&
|
|
169
|
+
proofCoversValue(proof.side, proof.sides, change.side) &&
|
|
170
|
+
proofCoversValue(proof.cascadeKey ?? proof.dependencyKey, proof.cascadeKeys ?? proof.dependencyKeys, change.cascadeKey) &&
|
|
171
|
+
proofSourceMatches(proof, 'base', binding.base, hash) &&
|
|
172
|
+
proofSourceMatches(proof, 'worker', binding.worker, hash) &&
|
|
173
|
+
proofSourceMatches(proof, 'head', binding.head, hash) &&
|
|
174
|
+
proofSourceMatches(proof, 'output', binding.output, hash) &&
|
|
175
|
+
dependencyGraphHashMatches(proof, 'base', evidence.sides?.base) &&
|
|
176
|
+
dependencyGraphHashMatches(proof, 'worker', evidence.sides?.worker) &&
|
|
177
|
+
dependencyGraphHashMatches(proof, 'head', evidence.sides?.head);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function dependencyGraphHashMatches(proof, role, sideEvidence) {
|
|
181
|
+
const graphHash = sideEvidence?.dependencyGraphHash;
|
|
182
|
+
if (!graphHash) return true;
|
|
183
|
+
return proof[`${role}DependencyGraphHash`] === graphHash ||
|
|
184
|
+
proof[`${role}CssDependencyGraphHash`] === graphHash ||
|
|
185
|
+
proof.dependencyGraphHashes?.[role] === graphHash ||
|
|
186
|
+
proof.cssDependencyGraphHashes?.[role] === graphHash;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function proofSourceMatches(proof, role, sourceText, hash) {
|
|
190
|
+
if (typeof sourceText !== 'string') return false;
|
|
191
|
+
const sourceHash = hash?.(sourceText);
|
|
192
|
+
const textFields = role === 'output' ? ['outputSourceText', 'mergedSourceText'] : [`${role}SourceText`];
|
|
193
|
+
const hashFields = role === 'output' ? ['outputSourceHash', 'mergedSourceHash'] : [`${role}SourceHash`];
|
|
194
|
+
const aliases = role === 'output' ? ['output', 'merged'] : [role];
|
|
195
|
+
return textFields.some((field) => proof[field] === sourceText) ||
|
|
196
|
+
aliases.some((alias) => proof.sourceTexts?.[alias] === sourceText || proof.sources?.[alias] === sourceText) ||
|
|
197
|
+
hashFields.some((field) => sourceHash !== undefined && proof[field] === sourceHash) ||
|
|
198
|
+
aliases.some((alias) => sourceHash !== undefined && (proof.sourceHashes?.[alias] === sourceHash || proof.hashes?.[alias] === sourceHash));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function dependencyGraphProofRecord(proof, change, sourcePath, evidence, binding, hash) {
|
|
202
|
+
return {
|
|
203
|
+
id: proof.id,
|
|
204
|
+
kind: proof.kind,
|
|
205
|
+
status: 'passed',
|
|
206
|
+
proofLevel: proof.proofLevel ?? 'css-dependency-graph-source-bound',
|
|
207
|
+
reasonCode: change.reasonCode,
|
|
208
|
+
side: change.side,
|
|
209
|
+
cascadeKey: change.cascadeKey,
|
|
210
|
+
sourcePath,
|
|
211
|
+
baseSourceHash: hash?.(binding.base),
|
|
212
|
+
workerSourceHash: hash?.(binding.worker),
|
|
213
|
+
headSourceHash: hash?.(binding.head),
|
|
214
|
+
outputSourceHash: hash?.(binding.output),
|
|
215
|
+
baseDependencyGraphHash: evidence.sides?.base?.dependencyGraphHash,
|
|
216
|
+
workerDependencyGraphHash: evidence.sides?.worker?.dependencyGraphHash,
|
|
217
|
+
headDependencyGraphHash: evidence.sides?.head?.dependencyGraphHash
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function conflict(id, sourcePath, code, reasonCode, details = {}) {
|
|
222
|
+
return { code, gateId: 'css-semantic-merge', sourcePath, details: { reasonCode, conflictKey: `css#${id}#${reasonCode}#${details.cascadeKey ?? sourcePath ?? 'source'}`, ...details } };
|
|
101
223
|
}
|
|
102
224
|
|
|
103
225
|
function customPropertyDefinition(record, declaration) {
|
|
@@ -184,8 +306,11 @@ function stableTextHash(text) {
|
|
|
184
306
|
|
|
185
307
|
function unique(values) { return [...new Set(values.filter(Boolean))]; }
|
|
186
308
|
function compactRecord(record) { return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined)); }
|
|
309
|
+
function proofCoversValue(value, values, expected) { return value === expected || (Array.isArray(values) && values.includes(expected)); }
|
|
310
|
+
function asArray(value) { return Array.isArray(value) ? value : value === undefined ? [] : [value]; }
|
|
187
311
|
|
|
188
312
|
const AnimationKeywords = new Set(['none', 'initial', 'inherit', 'unset', 'revert', 'linear', 'ease', 'ease-in', 'ease-out', 'ease-in-out', 'infinite', 'alternate', 'forwards', 'backwards', 'both', 'normal', 'reverse', 'running', 'paused']);
|
|
189
313
|
const FontKeywords = new Set(['serif', 'sans-serif', 'monospace', 'cursive', 'fantasy', 'system-ui', 'inherit', 'initial', 'unset', 'revert']);
|
|
314
|
+
const DependencyGraphProofKinds = new Set(['css-dependency-graph-proof', 'css-source-bound-dependency-graph-proof']);
|
|
190
315
|
|
|
191
|
-
export { createCssDependencyGraphEvidence, mergeCssDependencyGraphEvidence };
|
|
316
|
+
export { admitCssDependencyGraphProofs, createCssDependencyGraphEvidence, mergeCssDependencyGraphEvidence };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FrontierLangDocument } from '@shapeshift-labs/frontier-lang-kernel'; import type { CssCascadeRuntimeProof, CssCascadeRuntimeProofRecord } from './cascade-runtime-proof.js'; import type { CssDependencyGraphEvidence } from './dependency-graph.js'; export type { CssCascadeRuntimeProof, CssCascadeRuntimeProofRecord } from './cascade-runtime-proof.js';
|
|
1
|
+
import type { FrontierLangDocument } from '@shapeshift-labs/frontier-lang-kernel'; import type { CssCascadeRuntimeProof, CssCascadeRuntimeProofRecord } from './cascade-runtime-proof.js'; import type { CssDependencyGraphEvidence, CssDependencyGraphProof, CssDependencyGraphProofRecord } from './dependency-graph.js'; export type { CssCascadeRuntimeProof, CssCascadeRuntimeProofRecord } from './cascade-runtime-proof.js'; export type { CssDependencyGraphChange, CssDependencyGraphEvidence, CssDependencyGraphProof, CssDependencyGraphProofRecord } from './dependency-graph.js';
|
|
2
2
|
|
|
3
3
|
export interface CssProjectionOptions {
|
|
4
4
|
readonly banner?: string;
|
|
@@ -125,6 +125,7 @@ export interface CssSemanticRecord {
|
|
|
125
125
|
readonly conditionText?: string;
|
|
126
126
|
readonly scopeKey?: string;
|
|
127
127
|
readonly statementText?: string;
|
|
128
|
+
readonly blockText?: string;
|
|
128
129
|
readonly scopedCascadeGraphHash?: string;
|
|
129
130
|
readonly selectorTargetGraphHash?: string;
|
|
130
131
|
readonly sourceSpan: CssSourceSpan;
|
|
@@ -226,6 +227,7 @@ export interface CssSafeMergeAdmission {
|
|
|
226
227
|
readonly reviewRequired: boolean; readonly reasonCodes: readonly string[];
|
|
227
228
|
readonly browserCascadeEquivalenceClaim?: true;
|
|
228
229
|
readonly cssCascadeRuntimeProofs?: readonly CssCascadeRuntimeProofRecord[];
|
|
230
|
+
readonly cssDependencyGraphProofs?: readonly CssDependencyGraphProofRecord[];
|
|
229
231
|
}
|
|
230
232
|
|
|
231
233
|
export interface CssSafeMergeResult {
|
|
@@ -241,6 +243,7 @@ export interface CssSafeMergeResult {
|
|
|
241
243
|
readonly workerChangedCssModuleContracts?: number; readonly headChangedCssModuleContracts?: number;
|
|
242
244
|
readonly parserEvidence?: CssSafeMergeParserEvidence; readonly selectorTargetEvidence?: CssSafeMergeSelectorTargetEvidence; readonly dependencyGraphEvidence?: CssDependencyGraphEvidence;
|
|
243
245
|
readonly cascadeRuntimeProofs?: readonly CssCascadeRuntimeProofRecord[];
|
|
246
|
+
readonly dependencyGraphProofs?: readonly CssDependencyGraphProofRecord[];
|
|
244
247
|
}
|
|
245
248
|
|
|
246
249
|
export interface CssSafeMergeParserEvidence {
|
|
@@ -294,6 +297,8 @@ export interface CssSafeMergeInput {
|
|
|
294
297
|
readonly cascadeRuntimeProofsByPath?: Readonly<Record<string, CssCascadeRuntimeProof | readonly CssCascadeRuntimeProof[]>>;
|
|
295
298
|
readonly sourceBoundCascadeProof?: CssCascadeRuntimeProof; readonly sourceBoundCascadeProofs?: readonly CssCascadeRuntimeProof[];
|
|
296
299
|
readonly sourceBoundCascadeProofsByPath?: Readonly<Record<string, CssCascadeRuntimeProof | readonly CssCascadeRuntimeProof[]>>;
|
|
300
|
+
readonly cssDependencyGraphProof?: CssDependencyGraphProof; readonly cssDependencyGraphProofs?: readonly CssDependencyGraphProof[]; readonly cssSourceBoundDependencyGraphProof?: CssDependencyGraphProof; readonly cssSourceBoundDependencyGraphProofs?: readonly CssDependencyGraphProof[];
|
|
301
|
+
readonly cssDependencyGraphProofsByPath?: Readonly<Record<string, CssDependencyGraphProof | readonly CssDependencyGraphProof[]>>; readonly cssSourceBoundDependencyGraphProofsByPath?: Readonly<Record<string, CssDependencyGraphProof | readonly CssDependencyGraphProof[]>>;
|
|
297
302
|
readonly selectorTargetGraphHash?: string; readonly selectorTargetEquivalences?: readonly CssSelectorTargetEquivalence[];
|
|
298
303
|
readonly baseGeneratedClassNameMap?: Readonly<Record<string, string>>; readonly workerGeneratedClassNameMap?: Readonly<Record<string, string>>; readonly headGeneratedClassNameMap?: Readonly<Record<string, string>>;
|
|
299
304
|
readonly baseGeneratedClassNameMapHash?: string; readonly workerGeneratedClassNameMapHash?: string; readonly headGeneratedClassNameMapHash?: string;
|
package/dist/index.js
CHANGED
|
@@ -141,7 +141,7 @@ function sourceMapEnvelope(ast, mappings, options) {
|
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
function sourceRef(node, extra = {}) { return { semanticNodeId: node.id, semanticNodeKind: node.kind, semanticNodeName: node.name, ...extra }; }
|
|
144
|
-
function hashableCssRecord(record) { return { kind: record.kind, selectors: record.selectors, specificity: record.specificity, scopes: record.scopes, atRuleName: record.atRuleName, conditionText: record.conditionText, statementText: record.statementText, declarations: record.declarations?.map((item) => ({ property: item.property, value: item.value, important: item.important })), proofGaps: record.proofGaps?.map((gap) => gap.code) }; }
|
|
144
|
+
function hashableCssRecord(record) { return { kind: record.kind, selectors: record.selectors, specificity: record.specificity, scopes: record.scopes, atRuleName: record.atRuleName, conditionText: record.conditionText, statementText: record.statementText, blockTextHash: record.blockText ? record.rawTextHash : undefined, declarations: record.declarations?.map((item) => ({ property: item.property, value: item.value, important: item.important })), proofGaps: record.proofGaps?.map((gap) => gap.code) }; }
|
|
145
145
|
function cssIdentifier(value) { return String(value ?? 'unknown').replace(/[^A-Za-z0-9_-]/g, '-').replace(/^-+/, '') || 'unknown'; }
|
|
146
146
|
function cssString(value) { return String(value ?? '').replace(/\\/g, '\\\\').replace(/"/g, '\\"'); }
|
|
147
147
|
function typeName(type) { return typeof type === 'string' ? type : type?.name ?? type?.kind ?? 'unknown'; }
|
|
@@ -100,6 +100,7 @@ function postcssAtRuleRecord(node, scopes, sourceHash, options) {
|
|
|
100
100
|
atRuleName,
|
|
101
101
|
conditionText,
|
|
102
102
|
statementText: kind === 'at-rule-statement' ? rawText : undefined,
|
|
103
|
+
blockText: kind === 'at-rule' ? rawText : undefined,
|
|
103
104
|
scopeKey: postcssAtRuleScopeKey(node),
|
|
104
105
|
scopes,
|
|
105
106
|
dependencyTokens: atRuleDependencyTokens(node, atRuleName),
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
function atRuleBlockEntry(record, key) {
|
|
2
|
+
if (!shouldRenderOpaqueAtRuleBlock(record)) return undefined;
|
|
3
|
+
return {
|
|
4
|
+
key,
|
|
5
|
+
scopes: record.scopes ?? [],
|
|
6
|
+
atRuleName: record.atRuleName,
|
|
7
|
+
conditionText: record.conditionText,
|
|
8
|
+
blockText: record.blockText,
|
|
9
|
+
rawTextHash: record.rawTextHash,
|
|
10
|
+
atRuleHash: record.atRuleHash
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function shouldRenderOpaqueAtRuleBlock(record) {
|
|
15
|
+
return record.kind === 'at-rule' && !ScopeAtRules.has(record.atRuleName) && typeof record.blockText === 'string';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function changedAtRuleBlocks(baseIndex, currentIndex, side) {
|
|
19
|
+
const baseBlocks = baseIndex.atRuleBlocks ?? new Map();
|
|
20
|
+
const currentBlocks = currentIndex.atRuleBlocks ?? new Map();
|
|
21
|
+
const keys = unique([...baseBlocks.keys(), ...currentBlocks.keys()]);
|
|
22
|
+
return keys.flatMap((key) => {
|
|
23
|
+
const before = baseBlocks.get(key);
|
|
24
|
+
const after = currentBlocks.get(key);
|
|
25
|
+
if ((before?.rawTextHash ?? '') === (after?.rawTextHash ?? '')) return [];
|
|
26
|
+
return [{ side, key, before, after, kind: before && after ? 'update' : before ? 'delete' : 'add' }];
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function atRuleBlockOverlapConflicts(id, sourcePath, workerChanges, headChanges, conflict) {
|
|
31
|
+
const headByKey = new Map(headChanges.map((change) => [change.key, change]));
|
|
32
|
+
return workerChanges.flatMap((workerChange) => {
|
|
33
|
+
const headChange = headByKey.get(workerChange.key);
|
|
34
|
+
if (!headChange || sameAtRuleBlockChange(workerChange, headChange)) return [];
|
|
35
|
+
return [conflict(id, sourcePath, 'css-atrule-block-conflict', 'css-atrule-block-conflict', {
|
|
36
|
+
shapeKey: workerChange.key,
|
|
37
|
+
worker: atRuleBlockChangeDetails(workerChange),
|
|
38
|
+
head: atRuleBlockChangeDetails(headChange)
|
|
39
|
+
})];
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function applyAtRuleBlockChanges(index, changes) {
|
|
44
|
+
const atRuleBlocks = new Map(index.atRuleBlocks);
|
|
45
|
+
const atRuleBlockOrder = [...(index.atRuleBlockOrder ?? [])];
|
|
46
|
+
for (const change of changes) {
|
|
47
|
+
if (!change.after) atRuleBlocks.delete(change.key);
|
|
48
|
+
else {
|
|
49
|
+
atRuleBlocks.set(change.key, change.after);
|
|
50
|
+
if (!atRuleBlockOrder.includes(change.key)) atRuleBlockOrder.push(change.key);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return { ...index, atRuleBlocks, atRuleBlockOrder: atRuleBlockOrder.filter((key) => atRuleBlocks.has(key)) };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function renderAtRuleStatement(chunks, statement) {
|
|
57
|
+
let indent = 0;
|
|
58
|
+
for (const scope of statement.scopes ?? []) {
|
|
59
|
+
chunks.push(`${spaces(indent)}${scope} {`);
|
|
60
|
+
indent += 2;
|
|
61
|
+
}
|
|
62
|
+
chunks.push(`${spaces(indent)}${statement.statementText}`);
|
|
63
|
+
closeScopes(chunks, statement.scopes, indent);
|
|
64
|
+
chunks.push('');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function renderAtRuleBlock(chunks, block) {
|
|
68
|
+
if (!block) return;
|
|
69
|
+
let indent = 0;
|
|
70
|
+
for (const scope of block.scopes ?? []) {
|
|
71
|
+
chunks.push(`${spaces(indent)}${scope} {`);
|
|
72
|
+
indent += 2;
|
|
73
|
+
}
|
|
74
|
+
chunks.push(indentText(block.blockText, indent));
|
|
75
|
+
closeScopes(chunks, block.scopes, indent);
|
|
76
|
+
chunks.push('');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function closeScopes(chunks, scopes = [], indent) {
|
|
80
|
+
for (let index = scopes.length - 1; index >= 0; index -= 1) {
|
|
81
|
+
indent -= 2;
|
|
82
|
+
chunks.push(`${spaces(indent)}}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function atRuleBlockChangeDetails(change) {
|
|
87
|
+
const block = change.after ?? change.before;
|
|
88
|
+
return { kind: change.kind, atRuleName: block?.atRuleName, conditionText: block?.conditionText, rawTextHash: change.after?.rawTextHash };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function sameAtRuleBlockChange(left, right) { return (left.after?.rawTextHash ?? '') === (right.after?.rawTextHash ?? '') && left.kind === right.kind; }
|
|
92
|
+
function atRuleOccurrenceKey(record, counts, prefix = 'at-rule') {
|
|
93
|
+
if (record.kind !== 'at-rule') return undefined;
|
|
94
|
+
const baseKey = `${prefix}:${[...(record.scopes ?? []), record.atRuleName, record.conditionText].join('::')}`;
|
|
95
|
+
const count = (counts.get(baseKey) ?? 0) + 1;
|
|
96
|
+
counts.set(baseKey, count);
|
|
97
|
+
return count === 1 ? baseKey : `${baseKey}#${count}`;
|
|
98
|
+
}
|
|
99
|
+
function unique(values) { return [...new Set(values.filter(Boolean))]; }
|
|
100
|
+
function spaces(count) { return ' '.repeat(Math.max(0, count)); }
|
|
101
|
+
function indentText(text, indent) { return String(text ?? '').split('\n').map((line) => line ? `${spaces(indent)}${line}` : line).join('\n'); }
|
|
102
|
+
|
|
103
|
+
const ScopeAtRules = new Set(['media', 'supports', 'container', 'layer', 'scope']);
|
|
104
|
+
|
|
105
|
+
export { applyAtRuleBlockChanges, atRuleBlockEntry, atRuleBlockOverlapConflicts, atRuleOccurrenceKey, changedAtRuleBlocks, renderAtRuleBlock, renderAtRuleStatement };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { atRuleOccurrenceKey } from './semantic-merge-at-rules.js';
|
|
2
|
+
|
|
1
3
|
function sheetOptions(input, side, sourcePath) {
|
|
2
4
|
const prefix = side === 'base' ? 'base' : side === 'worker' ? 'worker' : 'head';
|
|
3
5
|
return {
|
|
@@ -165,6 +167,7 @@ function unsupportedSourceShapeChangesForSide(baseSheet, currentSheet, declarati
|
|
|
165
167
|
|
|
166
168
|
function sourceShapeIndex(sheet, hash) {
|
|
167
169
|
const result = new Map();
|
|
170
|
+
const atRuleOccurrences = new Map();
|
|
168
171
|
for (const record of sheet.records ?? []) {
|
|
169
172
|
if (record.kind === 'rule') {
|
|
170
173
|
const ruleKey = ruleIdentityKey(record);
|
|
@@ -180,14 +183,16 @@ function sourceShapeIndex(sheet, hash) {
|
|
|
180
183
|
});
|
|
181
184
|
}
|
|
182
185
|
if (record.kind === 'at-rule') {
|
|
183
|
-
const shapeKey =
|
|
186
|
+
const shapeKey = atRuleOccurrenceKey(record, atRuleOccurrences);
|
|
187
|
+
const opaqueHash = isOpaqueAtRuleBlock(record) ? record.rawTextHash : undefined;
|
|
184
188
|
result.set(shapeKey, {
|
|
185
189
|
kind: 'at-rule',
|
|
186
190
|
atRuleName: record.atRuleName,
|
|
187
191
|
conditionText: record.conditionText,
|
|
192
|
+
rawTextHash: opaqueHash,
|
|
188
193
|
representedByDeclarations: false,
|
|
189
194
|
unsupportedReasonCode: atRuleUnsupportedReasonCode(record),
|
|
190
|
-
hash: record.atRuleHash
|
|
195
|
+
hash: opaqueHash ?? record.atRuleHash
|
|
191
196
|
});
|
|
192
197
|
}
|
|
193
198
|
if (record.kind === 'at-rule-statement') {
|
|
@@ -213,15 +218,19 @@ function conflict(id, sourcePath, code, reasonCode, details = {}) {
|
|
|
213
218
|
|
|
214
219
|
function sameContractChange(left, right) { return (left.after?.hash ?? '') === (right.after?.hash ?? '') && left.kind === right.kind; }
|
|
215
220
|
function contractChangeDetails(change) { return { kind: change.kind, contractKind: (change.after ?? change.before)?.contractKind, name: (change.after ?? change.before)?.name, hash: change.after?.hash }; }
|
|
216
|
-
function sourceShapeDetails(shape) { return shape ? { kind: shape.kind, selectors: shape.selectors, atRuleName: shape.atRuleName, conditionText: shape.conditionText, statementText: shape.statementText, representedByDeclarations: shape.representedByDeclarations } : undefined; }
|
|
221
|
+
function sourceShapeDetails(shape) { return shape ? { kind: shape.kind, selectors: shape.selectors, atRuleName: shape.atRuleName, conditionText: shape.conditionText, statementText: shape.statementText, rawTextHash: shape.rawTextHash, representedByDeclarations: shape.representedByDeclarations } : undefined; }
|
|
217
222
|
function sourceShapeChangeReason(before, after) {
|
|
218
223
|
if (!before && after?.kind === 'at-rule') return 'css-atrule-new-scope-unsupported';
|
|
219
224
|
if (before?.kind === 'at-rule' || after?.kind === 'at-rule') return after?.unsupportedReasonCode ?? before?.unsupportedReasonCode ?? 'css-atrule-condition-edit-unsupported';
|
|
220
225
|
if (before?.kind === 'at-rule-statement' || after?.kind === 'at-rule-statement') return after?.unsupportedReasonCode ?? before?.unsupportedReasonCode ?? 'css-atrule-statement-unsupported';
|
|
221
226
|
return 'css-source-shape-unsupported';
|
|
222
227
|
}
|
|
223
|
-
function atRuleUnsupportedReasonCode(record) {
|
|
228
|
+
function atRuleUnsupportedReasonCode(record) {
|
|
229
|
+
if (RuntimeAtRules.has(record.atRuleName)) return `css-${record.atRuleName}-runtime-equivalence-unproved`;
|
|
230
|
+
return record.atRuleName === 'layer' ? 'css-layer-name-edit-unsupported' : 'css-atrule-condition-edit-unsupported';
|
|
231
|
+
}
|
|
224
232
|
function atRuleStatementUnsupportedReasonCode(record) { return record.atRuleName === 'layer' ? 'css-layer-order-statement-unsupported' : 'css-atrule-statement-unsupported'; }
|
|
233
|
+
function isOpaqueAtRuleBlock(record) { return record.kind === 'at-rule' && !ScopeAtRules.has(record.atRuleName) && typeof record.rawTextHash === 'string'; }
|
|
225
234
|
function ruleIdentityKey(record) { return [...(record.scopes ?? []), record.selectors.join(',')].join('::'); }
|
|
226
235
|
function unique(values) { return [...new Set(values.filter(Boolean))]; }
|
|
227
236
|
function uniqueProofGaps(values) {
|
|
@@ -231,3 +240,6 @@ function uniqueProofGaps(values) {
|
|
|
231
240
|
}
|
|
232
241
|
|
|
233
242
|
export { cssModuleContractChanges, cssModuleContractConflicts, sheetOptions, unsupportedSourceShapeChanges, unsupportedSourceShapeConflicts };
|
|
243
|
+
|
|
244
|
+
const RuntimeAtRules = new Set(['keyframes', 'font-face', 'page', 'property']);
|
|
245
|
+
const ScopeAtRules = new Set(['media', 'supports', 'container', 'layer', 'scope']);
|
package/dist/semantic-merge.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { cssModuleContractChanges, cssModuleContractConflicts, sheetOptions, unsupportedSourceShapeChanges } from './semantic-merge-css-modules.js';
|
|
2
2
|
import { admitCascadeRuntimeProofs } from './semantic-merge-cascade-runtime.js';
|
|
3
|
-
import { mergeCssDependencyGraphEvidence } from './dependency-graph.js';
|
|
3
|
+
import { admitCssDependencyGraphProofs, mergeCssDependencyGraphEvidence } from './dependency-graph.js';
|
|
4
4
|
import { mergeSelectorTargetEvidence, planSelectorTargetRebase } from './semantic-merge-selector-targets.js';
|
|
5
|
+
import { applyAtRuleBlockChanges, atRuleBlockEntry, atRuleBlockOverlapConflicts, atRuleOccurrenceKey, changedAtRuleBlocks, renderAtRuleBlock, renderAtRuleStatement } from './semantic-merge-at-rules.js';
|
|
5
6
|
import { declarationsOverlapByCssProperty, shorthandGroupForProperty } from './semantic-merge-shorthand.js';
|
|
6
7
|
|
|
7
8
|
function safeMergeCssSource(input = {}, context = {}) {
|
|
@@ -24,19 +25,24 @@ function safeMergeCssSource(input = {}, context = {}) {
|
|
|
24
25
|
worker: changedDeclarations(indexes.base, indexes.worker, 'worker'),
|
|
25
26
|
head: changedDeclarations(indexes.base, indexes.head, 'head')
|
|
26
27
|
};
|
|
28
|
+
const blockChanges = {
|
|
29
|
+
worker: changedAtRuleBlocks(indexes.base, indexes.worker, 'worker'),
|
|
30
|
+
head: changedAtRuleBlocks(indexes.base, indexes.head, 'head')
|
|
31
|
+
};
|
|
27
32
|
const moduleChanges = cssModuleContractChanges(sheets, hash);
|
|
28
33
|
const proofConflicts = proofGapConflicts(id, sourcePath, changed, indexes);
|
|
29
34
|
const parserConflicts = parserErrorConflicts(id, sourcePath, sheets);
|
|
30
35
|
const overlapConflicts = [
|
|
31
36
|
...overlapDeclarationConflicts(id, sourcePath, changed.worker, changed.head),
|
|
32
|
-
...shorthandOverlapConflicts(id, sourcePath, changed.worker, changed.head)
|
|
37
|
+
...shorthandOverlapConflicts(id, sourcePath, changed.worker, changed.head),
|
|
38
|
+
...atRuleBlockOverlapConflicts(id, sourcePath, blockChanges.worker, blockChanges.head, conflict)
|
|
33
39
|
];
|
|
34
40
|
const moduleConflicts = cssModuleContractConflicts(id, sourcePath, moduleChanges);
|
|
35
41
|
const parserEvidence = mergeParserEvidence(sheets);
|
|
36
42
|
const dependencyGraphEvidence = mergeCssDependencyGraphEvidence(sheets, changed);
|
|
37
43
|
const selectorTargetPlan = planSelectorTargetRebase(id, sourcePath, mergeSelectorTargetEvidence(sheets, changed), changed, input);
|
|
38
44
|
const shapeChanges = unsupportedSourceShapeChanges(sheets, changed, hash);
|
|
39
|
-
const mergedIndex = applyDeclarationChanges(applyDeclarationChanges(indexes.base, selectorTargetPlan.changed.head), selectorTargetPlan.changed.worker);
|
|
45
|
+
const mergedIndex = applyAtRuleBlockChanges(applyAtRuleBlockChanges(applyDeclarationChanges(applyDeclarationChanges(indexes.base, selectorTargetPlan.changed.head), selectorTargetPlan.changed.worker), blockChanges.head), blockChanges.worker);
|
|
40
46
|
const mergedSourceText = renderDeclarationIndex(mergedIndex);
|
|
41
47
|
const cascadeRuntimeAdmission = admitCascadeRuntimeProofs({
|
|
42
48
|
id,
|
|
@@ -46,8 +52,9 @@ function safeMergeCssSource(input = {}, context = {}) {
|
|
|
46
52
|
binding: { base, worker, head, output: mergedSourceText },
|
|
47
53
|
hash
|
|
48
54
|
});
|
|
49
|
-
const
|
|
50
|
-
|
|
55
|
+
const dependencyGraphAdmission = admitCssDependencyGraphProofs({ id, sourcePath, input, dependencyGraphEvidence, binding: { base, worker, head, output: mergedSourceText }, hash });
|
|
56
|
+
const conflicts = [...parserConflicts, ...proofConflicts, ...overlapConflicts, ...moduleConflicts, ...cascadeRuntimeAdmission.conflicts, ...dependencyGraphAdmission.conflicts, ...selectorTargetPlan.conflicts];
|
|
57
|
+
if (conflicts.length) return blocked(id, sourcePath, 'css-semantic-merge-conflict', conflicts, { parserEvidence, dependencyGraphEvidence, selectorTargetEvidence: selectorTargetPlan.evidence, cascadeRuntimeProofs: cascadeRuntimeAdmission.proofs, dependencyGraphProofs: dependencyGraphAdmission.proofs });
|
|
51
58
|
return merged(id, sourcePath, mergedSourceText, 'semantic-declaration-merge', hash, {
|
|
52
59
|
baseSheetHash: sheets.base.sheetHash,
|
|
53
60
|
workerSheetHash: sheets.worker.sheetHash,
|
|
@@ -60,6 +67,7 @@ function safeMergeCssSource(input = {}, context = {}) {
|
|
|
60
67
|
dependencyGraphEvidence,
|
|
61
68
|
selectorTargetEvidence: selectorTargetPlan.evidence,
|
|
62
69
|
cascadeRuntimeProofs: cascadeRuntimeAdmission.proofs,
|
|
70
|
+
dependencyGraphProofs: dependencyGraphAdmission.proofs,
|
|
63
71
|
browserCascadeEquivalenceClaim: cascadeRuntimeAdmission.proofs.length > 0
|
|
64
72
|
});
|
|
65
73
|
}
|
|
@@ -68,7 +76,15 @@ function declarationIndex(sheet) {
|
|
|
68
76
|
const declarations = new Map();
|
|
69
77
|
const order = [];
|
|
70
78
|
const statements = [];
|
|
79
|
+
const atRuleBlocks = new Map();
|
|
80
|
+
const atRuleBlockOrder = [];
|
|
81
|
+
const atRuleOccurrences = new Map();
|
|
71
82
|
for (const record of sheet.records) {
|
|
83
|
+
const block = atRuleBlockEntry(record, atRuleOccurrenceKey(record, atRuleOccurrences));
|
|
84
|
+
if (block) {
|
|
85
|
+
atRuleBlocks.set(block.key, block);
|
|
86
|
+
atRuleBlockOrder.push(block.key);
|
|
87
|
+
}
|
|
72
88
|
if (record.kind === 'at-rule-statement') {
|
|
73
89
|
statements.push({
|
|
74
90
|
key: record.atRuleHash,
|
|
@@ -97,7 +113,7 @@ function declarationIndex(sheet) {
|
|
|
97
113
|
order.push(entry.key);
|
|
98
114
|
}
|
|
99
115
|
}
|
|
100
|
-
return { declarations, order: unique(order), statements };
|
|
116
|
+
return { declarations, order: unique(order), statements, atRuleBlocks, atRuleBlockOrder: unique(atRuleBlockOrder) };
|
|
101
117
|
}
|
|
102
118
|
|
|
103
119
|
function changedDeclarations(baseIndex, currentIndex, side) {
|
|
@@ -210,7 +226,7 @@ function applyDeclarationChanges(index, changes) {
|
|
|
210
226
|
if (!order.includes(change.key)) order.push(change.key);
|
|
211
227
|
}
|
|
212
228
|
}
|
|
213
|
-
return { declarations, order: order.filter((key) => declarations.has(key)), statements: index.statements ?? [] };
|
|
229
|
+
return { ...index, declarations, order: order.filter((key) => declarations.has(key)), statements: index.statements ?? [] };
|
|
214
230
|
}
|
|
215
231
|
|
|
216
232
|
function renderDeclarationIndex(index) {
|
|
@@ -222,26 +238,13 @@ function renderDeclarationIndex(index) {
|
|
|
222
238
|
}
|
|
223
239
|
const chunks = [];
|
|
224
240
|
for (const statement of index.statements ?? []) renderAtRuleStatement(chunks, statement);
|
|
241
|
+
for (const key of index.atRuleBlockOrder ?? []) renderAtRuleBlock(chunks, index.atRuleBlocks.get(key));
|
|
225
242
|
for (const declarations of groups.values()) {
|
|
226
243
|
renderDeclarationGroup(chunks, declarations);
|
|
227
244
|
}
|
|
228
245
|
return `${chunks.join('\n').trimEnd()}\n`;
|
|
229
246
|
}
|
|
230
247
|
|
|
231
|
-
function renderAtRuleStatement(chunks, statement) {
|
|
232
|
-
let indent = 0;
|
|
233
|
-
for (const scope of statement.scopes ?? []) {
|
|
234
|
-
chunks.push(`${spaces(indent)}${scope} {`);
|
|
235
|
-
indent += 2;
|
|
236
|
-
}
|
|
237
|
-
chunks.push(`${spaces(indent)}${statement.statementText}`);
|
|
238
|
-
for (let index = (statement.scopes ?? []).length - 1; index >= 0; index -= 1) {
|
|
239
|
-
indent -= 2;
|
|
240
|
-
chunks.push(`${spaces(indent)}}`);
|
|
241
|
-
}
|
|
242
|
-
chunks.push('');
|
|
243
|
-
}
|
|
244
|
-
|
|
245
248
|
function renderDeclarationGroup(chunks, declarations) {
|
|
246
249
|
const first = declarations[0];
|
|
247
250
|
let indent = 0;
|
|
@@ -296,7 +299,7 @@ function result(id, sourcePath, status, body) {
|
|
|
296
299
|
reviewRequired: status !== 'merged',
|
|
297
300
|
reasonCodes: unique((body.conflicts ?? []).map((item) => item.details.reasonCode)),
|
|
298
301
|
browserCascadeEquivalenceClaim: browserCascadeEquivalenceClaim || undefined,
|
|
299
|
-
cssCascadeRuntimeProofs: body.cascadeRuntimeProofs?.length ? body.cascadeRuntimeProofs : undefined
|
|
302
|
+
cssCascadeRuntimeProofs: body.cascadeRuntimeProofs?.length ? body.cascadeRuntimeProofs : undefined, cssDependencyGraphProofs: body.dependencyGraphProofs?.length ? body.dependencyGraphProofs : undefined
|
|
300
303
|
}
|
|
301
304
|
};
|
|
302
305
|
}
|
package/package.json
CHANGED