@shapeshift-labs/frontier-lang-css 0.1.12 → 0.1.13

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 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 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 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.
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 and non-overlapping shorthand expansion sets; 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; 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`, changed runtime blocks such as `@keyframes`, `@font-face`, `@page`, and `@property`, 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.
package/dist/index.d.ts CHANGED
@@ -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;
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 = `at-rule:${[...(record.scopes ?? []), record.atRuleName, record.conditionText].join('::')}`;
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) { return record.atRuleName === 'layer' ? 'css-layer-name-edit-unsupported' : 'css-atrule-condition-edit-unsupported'; }
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']);
@@ -2,6 +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 { 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,
@@ -68,7 +74,15 @@ function declarationIndex(sheet) {
68
74
  const declarations = new Map();
69
75
  const order = [];
70
76
  const statements = [];
77
+ const atRuleBlocks = new Map();
78
+ const atRuleBlockOrder = [];
79
+ const atRuleOccurrences = new Map();
71
80
  for (const record of sheet.records) {
81
+ const block = atRuleBlockEntry(record, atRuleOccurrenceKey(record, atRuleOccurrences));
82
+ if (block) {
83
+ atRuleBlocks.set(block.key, block);
84
+ atRuleBlockOrder.push(block.key);
85
+ }
72
86
  if (record.kind === 'at-rule-statement') {
73
87
  statements.push({
74
88
  key: record.atRuleHash,
@@ -97,7 +111,7 @@ function declarationIndex(sheet) {
97
111
  order.push(entry.key);
98
112
  }
99
113
  }
100
- return { declarations, order: unique(order), statements };
114
+ return { declarations, order: unique(order), statements, atRuleBlocks, atRuleBlockOrder: unique(atRuleBlockOrder) };
101
115
  }
102
116
 
103
117
  function changedDeclarations(baseIndex, currentIndex, side) {
@@ -210,7 +224,7 @@ function applyDeclarationChanges(index, changes) {
210
224
  if (!order.includes(change.key)) order.push(change.key);
211
225
  }
212
226
  }
213
- return { declarations, order: order.filter((key) => declarations.has(key)), statements: index.statements ?? [] };
227
+ return { ...index, declarations, order: order.filter((key) => declarations.has(key)), statements: index.statements ?? [] };
214
228
  }
215
229
 
216
230
  function renderDeclarationIndex(index) {
@@ -222,26 +236,13 @@ function renderDeclarationIndex(index) {
222
236
  }
223
237
  const chunks = [];
224
238
  for (const statement of index.statements ?? []) renderAtRuleStatement(chunks, statement);
239
+ for (const key of index.atRuleBlockOrder ?? []) renderAtRuleBlock(chunks, index.atRuleBlocks.get(key));
225
240
  for (const declarations of groups.values()) {
226
241
  renderDeclarationGroup(chunks, declarations);
227
242
  }
228
243
  return `${chunks.join('\n').trimEnd()}\n`;
229
244
  }
230
245
 
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
246
  function renderDeclarationGroup(chunks, declarations) {
246
247
  const first = declarations[0];
247
248
  let indent = 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shapeshift-labs/frontier-lang-css",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "CSS semantic merge evidence and projection adapter for Frontier Lang semantic source documents.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",