sheetom 0.1.0-rc.9 → 0.1.1

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/index.js CHANGED
@@ -4,9 +4,9 @@ import { fileURLToPath } from "node:url";
4
4
  //#endregion
5
5
  //#region src/internal/engine-binding.ts
6
6
  const expectedEngineAbiIdentity = Object.freeze({
7
- abiVersion: 1,
8
- sheetomVersion: "0.1.0-rc.9",
9
- syntaxEngineSetSha256: "2af7ec76ba46a3f20f74a90172a708afab5dcb20f45b658a4d975caed76dc855"
7
+ abiVersion: 4,
8
+ sheetomVersion: "0.1.1",
9
+ syntaxEngineSetSha256: "a0f10d0c1f0fe9e5bac9002e8ee548e7b5f6e2c7add1af3d953ae718d25cb37b"
10
10
  });
11
11
  var SheetOMEngineBindingError = class extends Error {
12
12
  code;
@@ -41,7 +41,7 @@ function loadBinding() {
41
41
  validateEngineBindingIdentity(addon);
42
42
  return {
43
43
  ...addon,
44
- createDeclarationState: (context, ...budget) => new addon.NativeDeclarationState(context, ...budget)
44
+ createDeclarationState: (context, ...arguments_) => new addon.NativeDeclarationState(context, ...arguments_)
45
45
  };
46
46
  }
47
47
  //#endregion
@@ -90,18 +90,54 @@ function limitValue(value, name, fallback, maximum = maximumUnsignedLong) {
90
90
  throw new RangeError(`${name} must be an integer between 0 and ${maximum}`);
91
91
  }
92
92
  //#endregion
93
+ //#region src/serialization-error.ts
94
+ /** A reparsable stylesheet cannot faithfully represent the current CSSOM state. */
95
+ var SheetOMSerializationError = class extends Error {
96
+ code;
97
+ shorthand;
98
+ conflictingLonghands;
99
+ constructor(shorthand, conflictingLonghands, options) {
100
+ super(`SheetOM cannot serialize the pending ${shorthand} shorthand without changing its semantics.`, options);
101
+ this.name = "SheetOMSerializationError";
102
+ this.code = "UNREPRESENTABLE_PENDING_SHORTHAND";
103
+ this.shorthand = shorthand;
104
+ this.conflictingLonghands = Object.freeze([...conflictingLonghands]);
105
+ }
106
+ };
107
+ const unrepresentablePendingShorthandPattern = /^SHEETOM_UNREPRESENTABLE_PENDING_SHORTHAND: shorthand=([^;]+); conflicting=([a-z0-9,-]+)$/u;
108
+ function serializationErrorFrom(error) {
109
+ if (!(error instanceof Error)) return null;
110
+ const match = unrepresentablePendingShorthandPattern.exec(error.message);
111
+ if (!match) return null;
112
+ const shorthand = match[1];
113
+ const conflicts = match[2];
114
+ if (!shorthand || !conflicts) return null;
115
+ return new SheetOMSerializationError(shorthand, conflicts.split(","), { cause: error });
116
+ }
117
+ function rethrowSerializationError(error) {
118
+ const serializationError = serializationErrorFrom(error);
119
+ if (serializationError) throw serializationError;
120
+ }
121
+ //#endregion
93
122
  //#region src/internal/native-declaration-block.ts
94
123
  /** Thin JS ownership boundary around the Rust declaration state machine. */
95
124
  var NativeDeclarationBlock = class {
96
125
  #state;
97
126
  #reportDiagnostic;
127
+ #reportSerializationDiagnostic;
98
128
  #reservedNestingDepth;
99
129
  #observableCache;
100
130
  #serializationCache = /* @__PURE__ */ new Map();
101
- constructor(reportDiagnostic, context = "style", resourceBudget = defaultResourceBudget, reservedNestingDepth = () => 0) {
131
+ constructor(reportDiagnostic, reportSerializationDiagnostic, context = "style", resourceBudget = defaultResourceBudget, reservedNestingDepth = () => 0, initialCssText) {
102
132
  this.#reportDiagnostic = reportDiagnostic;
133
+ this.#reportSerializationDiagnostic = reportSerializationDiagnostic;
103
134
  this.#reservedNestingDepth = reservedNestingDepth;
104
- this.#state = engineBinding.createDeclarationState(context, ...nativeBudgetArguments(resourceBudget));
135
+ try {
136
+ this.#state = engineBinding.createDeclarationState(context, ...nativeBudgetArguments(resourceBudget), initialCssText);
137
+ } catch (error) {
138
+ rethrowResourceBudgetError(error);
139
+ throw error;
140
+ }
105
141
  }
106
142
  get cssText() {
107
143
  this.#observableCache ??= this.#state.cssText;
@@ -131,9 +167,59 @@ var NativeDeclarationBlock = class {
131
167
  this.#invalidateSerialization();
132
168
  return;
133
169
  }
134
- const normalizedName = name.startsWith("--") ? name : name.toLowerCase();
135
- const [code, input] = outcome === "invalid-priority" ? ["INVALID_PRIORITY", priority] : outcome === "unsupported-shorthand" ? ["UNSUPPORTED_SHORTHAND_VALUE", value] : ["INVALID_PROPERTY_VALUE", value];
136
- this.#reportDiagnostic(code, normalizedName, input);
170
+ this.#diagnosticForOutcome(outcome, name, value, priority);
171
+ }
172
+ applyMutations(mutations) {
173
+ const kinds = [];
174
+ const properties = [];
175
+ const values = [];
176
+ const priorities = [];
177
+ for (const mutation of mutations) {
178
+ kinds.push(mutation.kind === "set" ? 0 : 1);
179
+ properties.push(mutation.property);
180
+ values.push(mutation.kind === "set" ? mutation.value : "");
181
+ priorities.push(mutation.kind === "set" ? mutation.priority : "");
182
+ }
183
+ let outcomes;
184
+ try {
185
+ outcomes = this.#state.applyMutations(kinds, properties, values, priorities, this.#reservedNestingDepth());
186
+ } catch (error) {
187
+ rethrowResourceBudgetError(error);
188
+ throw error;
189
+ }
190
+ if (outcomes.length !== mutations.length) throw new Error("SheetOM engine returned an incomplete declaration mutation result.");
191
+ const results = [];
192
+ let mutated = false;
193
+ for (let index = 0; index < outcomes.length; index += 1) {
194
+ const mutation = mutations[index];
195
+ const outcome = outcomes[index];
196
+ if (!mutation || outcome === void 0) throw new Error("SheetOM engine returned a mismatched declaration mutation result.");
197
+ if (mutation.kind === "remove") {
198
+ mutated = true;
199
+ results.push({
200
+ kind: "remove",
201
+ value: outcome
202
+ });
203
+ continue;
204
+ }
205
+ if (outcome === "applied") {
206
+ mutated = true;
207
+ results.push({
208
+ kind: "set",
209
+ accepted: true,
210
+ diagnostic: null
211
+ });
212
+ continue;
213
+ }
214
+ if (!isEngineMutationOutcome(outcome)) throw new Error("SheetOM engine returned an invalid declaration mutation outcome.");
215
+ results.push({
216
+ kind: "set",
217
+ accepted: false,
218
+ diagnostic: this.#diagnosticForOutcome(outcome, mutation.property, mutation.value, mutation.priority)
219
+ });
220
+ }
221
+ if (mutated) this.#invalidateSerialization();
222
+ return results;
137
223
  }
138
224
  removeProperty(name) {
139
225
  const removed = this.#state.removeProperty(name);
@@ -149,19 +235,63 @@ var NativeDeclarationBlock = class {
149
235
  }
150
236
  this.#invalidateSerialization();
151
237
  }
152
- serialize(safe, indent, separator) {
153
- const cacheKey = `${safe ? "1" : "0"}\0${indent}\0${separator}`;
238
+ serialize(safe, indent, separator, strict = false) {
239
+ const cacheKey = `${safe ? "1" : "0"}${strict ? "1" : "0"}\0${indent}\0${separator}`;
154
240
  const cached = this.#serializationCache.get(cacheKey);
155
- if (cached !== void 0) return cached;
156
- const serialized = this.#state.serializeFormatted(safe, indent, separator);
157
- this.#serializationCache.set(cacheKey, serialized);
158
- return serialized;
241
+ if (cached !== void 0) {
242
+ if (typeof cached === "string") return cached;
243
+ this.#reportSerializationIssues(cached.issues);
244
+ return cached.serialized;
245
+ }
246
+ let result;
247
+ try {
248
+ if (strict) result = this.#state.serializeFormatted(safe, indent, separator);
249
+ else result = parseResilientSerializationResult(this.#state.serializeFormattedResilient(safe, indent, separator));
250
+ } catch (error) {
251
+ rethrowResourceBudgetError(error);
252
+ rethrowSerializationError(error);
253
+ throw error;
254
+ }
255
+ this.#serializationCache.set(cacheKey, result);
256
+ if (typeof result === "string") return result;
257
+ this.#reportSerializationIssues(result.issues);
258
+ return result.serialized;
159
259
  }
160
260
  #invalidateSerialization() {
161
261
  this.#observableCache = void 0;
162
262
  this.#serializationCache.clear();
163
263
  }
264
+ #diagnosticForOutcome(outcome, name, value, priority) {
265
+ const normalizedName = name.startsWith("--") ? name : name.toLowerCase();
266
+ const [code, input] = outcome === "invalid-priority" ? ["INVALID_PRIORITY", priority] : outcome === "unsupported-shorthand" ? ["UNSUPPORTED_SHORTHAND_VALUE", value] : ["INVALID_PROPERTY_VALUE", value];
267
+ return this.#reportDiagnostic(code, normalizedName, input);
268
+ }
269
+ #reportSerializationIssues(issues) {
270
+ for (const issue of issues) this.#reportSerializationDiagnostic(issue.shorthand, issue.conflictingLonghands);
271
+ }
164
272
  };
273
+ function parseResilientSerializationResult(output) {
274
+ const serialized = output[0];
275
+ if (serialized === void 0 || output.length % 2 === 0) throw new Error("SheetOM engine returned an invalid resilient serialization result.");
276
+ if (output.length === 1) return serialized;
277
+ const issues = [];
278
+ for (let index = 1; index < output.length; index += 2) {
279
+ const shorthand = output[index];
280
+ const conflicts = output[index + 1];
281
+ if (!shorthand || !conflicts) throw new Error("SheetOM engine returned an incomplete resilient serialization issue.");
282
+ issues.push({
283
+ shorthand,
284
+ conflictingLonghands: Object.freeze(conflicts.split(","))
285
+ });
286
+ }
287
+ return {
288
+ serialized,
289
+ issues
290
+ };
291
+ }
292
+ function isEngineMutationOutcome(value) {
293
+ return value === "invalid-name" || value === "invalid-priority" || value === "invalid-value" || value === "unsupported-shorthand";
294
+ }
165
295
  function toUnsignedLong$1(value) {
166
296
  const numeric = Number(value);
167
297
  if (!Number.isFinite(numeric) || numeric === 0) return 0;
@@ -353,13 +483,20 @@ var Serializer = class {
353
483
  this.#describe = describe;
354
484
  }
355
485
  serialize(node) {
486
+ return this.serializeMany([node]);
487
+ }
488
+ serializeMany(nodes) {
356
489
  const chunks = [];
357
- const pending = [{
358
- kind: "node",
359
- node,
360
- depth: 0,
361
- child: false
362
- }];
490
+ const pending = [];
491
+ for (let index = nodes.length - 1; index >= 0; index -= 1) {
492
+ const node = nodes[index];
493
+ if (node) pending.push({
494
+ kind: "node",
495
+ node,
496
+ depth: 0,
497
+ child: false
498
+ });
499
+ }
363
500
  while (pending.length > 0) {
364
501
  const frame = pending.pop();
365
502
  if (!frame) continue;
@@ -466,6 +603,7 @@ const pageMarginRuleNames = /* @__PURE__ */ new Set([
466
603
  "left-top"
467
604
  ]);
468
605
  const ignoreDiagnostic = () => {};
606
+ const normalizedSelectorText = Symbol("normalizedSelectorText");
469
607
  const ruleDiagnostics = /* @__PURE__ */ new WeakMap();
470
608
  const ruleTree = new RuleTree((rule, parentRule, parentStyleSheet) => {
471
609
  ruleParentage.set(rule, {
@@ -473,7 +611,8 @@ const ruleTree = new RuleTree((rule, parentRule, parentStyleSheet) => {
473
611
  parentStyleSheet
474
612
  });
475
613
  }, (rule) => rule.parentStyleSheet);
476
- const safeSerializer = new Serializer(describeRuleSafe);
614
+ const safeSerializer = new Serializer((rule) => describeRuleSafe(rule, false));
615
+ const strictSerializer = new Serializer((rule) => describeRuleSafe(rule, true));
477
616
  let activeConstructionResourceBudget = defaultResourceBudget;
478
617
  function constructWithResourceBudget(factory, resourceBudget) {
479
618
  const previous = activeConstructionResourceBudget;
@@ -1121,24 +1260,37 @@ var CSSViewTransitionRule = class extends CSSRule {
1121
1260
  var CSSStyleDeclaration = class {
1122
1261
  parentRule;
1123
1262
  #block;
1124
- constructor(parentRule) {
1263
+ constructor(parentRule, initialCssText) {
1125
1264
  assertInternalConstructor("CSSStyleDeclaration");
1126
1265
  this.parentRule = parentRule;
1127
1266
  lockOwnProperties(this, "parentRule");
1128
1267
  const reportDeclarationDiagnostic = (code, property, input) => {
1129
- const priority = code === "INVALID_PRIORITY";
1130
- const unsupportedShorthand = code === "UNSUPPORTED_SHORTHAND_VALUE";
1131
- (ruleDiagnostics.get(this.parentRule) ?? ignoreDiagnostic)({
1268
+ const diagnostic = {
1132
1269
  code,
1133
1270
  severity: "warning",
1134
1271
  operation: "setProperty",
1135
- message: priority ? `The mutation was ignored because ${input} is not a valid priority.` : unsupportedShorthand ? `The value was ignored because the shorthand codec for ${property} cannot expand it.` : `The value was ignored because it is invalid for ${property}.`,
1272
+ message: code === "INVALID_PRIORITY" ? `The mutation was ignored because ${input} is not a valid priority.` : code === "UNSUPPORTED_SHORTHAND_VALUE" ? `The value was ignored because the shorthand codec for ${property} cannot expand it.` : `The value was ignored because it is invalid for ${property}.`,
1136
1273
  property,
1137
1274
  input,
1138
1275
  location: null
1139
- });
1276
+ };
1277
+ (ruleDiagnostics.get(this.parentRule) ?? ignoreDiagnostic)(diagnostic);
1278
+ return diagnostic;
1279
+ };
1280
+ const reportSerializationDiagnostic = (shorthand, conflictingLonghands) => {
1281
+ const diagnostic = {
1282
+ code: "UNREPRESENTABLE_PENDING_SHORTHAND",
1283
+ severity: "warning",
1284
+ operation: "serialize",
1285
+ message: `The pending ${shorthand} shorthand required a best-effort recovery during serialization.`,
1286
+ property: shorthand,
1287
+ input: "",
1288
+ location: null,
1289
+ conflictingLonghands: Object.freeze([...conflictingLonghands])
1290
+ };
1291
+ (ruleDiagnostics.get(this.parentRule) ?? ignoreDiagnostic)(diagnostic);
1140
1292
  };
1141
- this.#block = new NativeDeclarationBlock(reportDeclarationDiagnostic, parentRule instanceof CSSFontFaceRule ? "font-face" : parentRule instanceof CSSFunctionDeclarations ? "function" : "style", ruleResourceBudgets.get(parentRule) ?? defaultResourceBudget, () => declarationSerializationDepth(parentRule));
1293
+ this.#block = new NativeDeclarationBlock(reportDeclarationDiagnostic, reportSerializationDiagnostic, parentRule instanceof CSSFontFaceRule ? "font-face" : parentRule instanceof CSSFunctionDeclarations ? "function" : "style", ruleResourceBudgets.get(parentRule) ?? defaultResourceBudget, () => declarationSerializationDepth(parentRule), initialCssText);
1142
1294
  return new Proxy(this, {
1143
1295
  get(target, property) {
1144
1296
  if (typeof property === "string" && arrayIndexPattern.test(property)) return target.item(Number(property)) || void 0;
@@ -1183,21 +1335,49 @@ var CSSStyleDeclaration = class {
1183
1335
  const stringPriority = priority === null ? "" : `${priority}`;
1184
1336
  this.#block.setProperty(stringName, stringValue, stringPriority);
1185
1337
  }
1338
+ /** Apply ordered declaration mutations through one engine boundary crossing. */
1339
+ applyMutations(mutations) {
1340
+ requireArguments(arguments.length, 1, "CSSStyleDeclaration", "applyMutations");
1341
+ if (!Array.isArray(mutations)) throw new TypeError("CSSStyleDeclaration.applyMutations requires an array.");
1342
+ const normalized = [];
1343
+ for (let index = 0; index < mutations.length; index += 1) {
1344
+ const mutation = mutations[index];
1345
+ if (typeof mutation !== "object" || mutation === null) throw new TypeError(`Declaration mutation at index ${index} must be an object.`);
1346
+ if (mutation.kind !== "set" && mutation.kind !== "remove") throw new TypeError(`Declaration mutation at index ${index} has an invalid kind.`);
1347
+ if (typeof mutation.property !== "string") throw new TypeError(`Declaration mutation at index ${index} requires a string property.`);
1348
+ if (mutation.kind === "remove") {
1349
+ normalized.push({
1350
+ kind: "remove",
1351
+ property: mutation.property
1352
+ });
1353
+ continue;
1354
+ }
1355
+ if (typeof mutation.value !== "string" && mutation.value !== null) throw new TypeError(`Declaration mutation at index ${index} requires a string or null value.`);
1356
+ if (mutation.priority !== void 0 && mutation.priority !== null && typeof mutation.priority !== "string") throw new TypeError(`Declaration mutation at index ${index} requires a string priority.`);
1357
+ normalized.push({
1358
+ kind: "set",
1359
+ property: mutation.property,
1360
+ value: mutation.value ?? "",
1361
+ priority: mutation.priority ?? ""
1362
+ });
1363
+ }
1364
+ return this.#block.applyMutations(normalized);
1365
+ }
1186
1366
  removeProperty(name) {
1187
1367
  requireArguments(arguments.length, 1, "CSSStyleDeclaration", "removeProperty");
1188
1368
  return this.#block.removeProperty(`${name}`);
1189
1369
  }
1190
1370
  /** @internal */
1191
- serializeSafe(indent) {
1192
- return this.#block.serialize(true, indent, "\n");
1371
+ serializeSafe(indent, strict = false) {
1372
+ return this.#block.serialize(true, indent, "\n", strict);
1193
1373
  }
1194
1374
  };
1195
1375
  /** A live `@font-face` descriptor rule. */
1196
1376
  var CSSFontFaceRule = class extends CSSRule {
1197
1377
  style;
1198
- constructor() {
1378
+ constructor(initialCssText) {
1199
1379
  super(CSSRule.FONT_FACE_RULE);
1200
- this.style = constructWebIDL(CSSStyleDeclaration, [this]);
1380
+ this.style = constructWebIDL(CSSStyleDeclaration, [this, initialCssText]);
1201
1381
  lockOwnProperties(this, "style");
1202
1382
  }
1203
1383
  get cssText() {
@@ -1208,9 +1388,9 @@ var CSSFontFaceRule = class extends CSSRule {
1208
1388
  /** Declarations ordered after a nested style rule. */
1209
1389
  var CSSNestedDeclarations = class extends CSSRule {
1210
1390
  #style;
1211
- constructor() {
1391
+ constructor(initialCssText) {
1212
1392
  super(0);
1213
- this.#style = constructWebIDL(CSSStyleDeclaration, [this]);
1393
+ this.#style = constructWebIDL(CSSStyleDeclaration, [this, initialCssText]);
1214
1394
  }
1215
1395
  get style() {
1216
1396
  return this.#style;
@@ -1226,9 +1406,9 @@ var CSSNestedDeclarations = class extends CSSRule {
1226
1406
  /** A run of live descriptors inside a custom function. */
1227
1407
  var CSSFunctionDeclarations = class extends CSSRule {
1228
1408
  #style;
1229
- constructor() {
1409
+ constructor(initialCssText) {
1230
1410
  super(0);
1231
- this.#style = constructWebIDL(CSSFunctionDescriptors, [this]);
1411
+ this.#style = constructWebIDL(CSSFunctionDescriptors, [this, initialCssText]);
1232
1412
  }
1233
1413
  get style() {
1234
1414
  return this.#style;
@@ -1252,10 +1432,10 @@ var CSSFunctionDescriptors = class extends CSSStyleDeclaration {
1252
1432
  var CSSMarginRule = class extends CSSRule {
1253
1433
  name;
1254
1434
  style;
1255
- constructor(name) {
1435
+ constructor(name, initialCssText) {
1256
1436
  super(CSSRule.MARGIN_RULE);
1257
1437
  this.name = name;
1258
- this.style = constructWebIDL(CSSStyleDeclaration, [this]);
1438
+ this.style = constructWebIDL(CSSStyleDeclaration, [this, initialCssText]);
1259
1439
  lockOwnProperties(this, "name", "style");
1260
1440
  }
1261
1441
  get cssText() {
@@ -1267,10 +1447,10 @@ var CSSMarginRule = class extends CSSRule {
1267
1447
  var CSSPageRule = class extends CSSGroupingRule {
1268
1448
  style;
1269
1449
  #selectorText;
1270
- constructor(selectorText) {
1450
+ constructor(selectorText, initialCssText) {
1271
1451
  super(CSSRule.PAGE_RULE);
1272
1452
  this.#selectorText = selectorText;
1273
- this.style = constructWebIDL(CSSStyleDeclaration, [this]);
1453
+ this.style = constructWebIDL(CSSStyleDeclaration, [this, initialCssText]);
1274
1454
  lockOwnProperties(this, "style");
1275
1455
  }
1276
1456
  get selectorText() {
@@ -1291,10 +1471,10 @@ var CSSPageRule = class extends CSSGroupingRule {
1291
1471
  var CSSPositionTryRule = class extends CSSRule {
1292
1472
  name;
1293
1473
  style;
1294
- constructor(name) {
1474
+ constructor(name, initialCssText) {
1295
1475
  super(0);
1296
1476
  this.name = name;
1297
- this.style = constructWebIDL(CSSStyleDeclaration, [this]);
1477
+ this.style = constructWebIDL(CSSStyleDeclaration, [this, initialCssText]);
1298
1478
  lockOwnProperties(this, "name", "style");
1299
1479
  }
1300
1480
  get cssText() {
@@ -1328,10 +1508,10 @@ function normalizeKeyText(value) {
1328
1508
  var CSSKeyframeRule = class extends CSSRule {
1329
1509
  style;
1330
1510
  #keyText;
1331
- constructor(keyText) {
1511
+ constructor(keyText, initialCssText) {
1332
1512
  super(CSSRule.KEYFRAME_RULE);
1333
1513
  this.#keyText = normalizeKeyText(keyText) ?? keyText;
1334
- this.style = constructWebIDL(CSSStyleDeclaration, [this]);
1514
+ this.style = constructWebIDL(CSSStyleDeclaration, [this, initialCssText]);
1335
1515
  lockOwnProperties(this, "style");
1336
1516
  }
1337
1517
  get keyText() {
@@ -1642,10 +1822,10 @@ var CSSFontFeatureValuesRule = class extends CSSRule {
1642
1822
  var CSSStyleRule = class extends CSSGroupingRule {
1643
1823
  style;
1644
1824
  #selectorText;
1645
- constructor(selectorText) {
1825
+ constructor(selectorText, normalized, initialCssText) {
1646
1826
  super(CSSRule.STYLE_RULE);
1647
- this.#selectorText = normalizeSelectorText(`${selectorText}`, currentConstructionResourceBudget()) ?? `${selectorText}`;
1648
- this.style = constructWebIDL(CSSStyleDeclaration, [this]);
1827
+ this.#selectorText = normalized === normalizedSelectorText ? selectorText : normalizeSelectorText(`${selectorText}`, currentConstructionResourceBudget()) ?? `${selectorText}`;
1828
+ this.style = constructWebIDL(CSSStyleDeclaration, [this, initialCssText]);
1649
1829
  lockOwnProperties(this, "style");
1650
1830
  }
1651
1831
  get selectorText() {
@@ -1678,26 +1858,23 @@ function replaceGroupingRules(group, rules) {
1678
1858
  function attachRuleTree(rule, parentRule, parentStyleSheet) {
1679
1859
  ruleTree.attach(rule, parentRule, parentStyleSheet);
1680
1860
  }
1681
- function serializeRuleSafe(rule) {
1682
- return safeSerializer.serialize(rule);
1683
- }
1684
- function describeRuleSafe(rule) {
1861
+ function describeRuleSafe(rule, strict) {
1685
1862
  if (rule instanceof CSSStyleRule) return {
1686
1863
  kind: "block",
1687
1864
  header: rule.selectorText,
1688
- declarations: rule.style.serializeSafe(" "),
1865
+ declarations: rule.style.serializeSafe(" ", strict),
1689
1866
  children: ruleTree.children(rule)
1690
1867
  };
1691
1868
  if (rule instanceof CSSNestedDeclarations || rule instanceof CSSFunctionDeclarations) return {
1692
1869
  kind: "declarations",
1693
- declarations: rule.style.serializeSafe("")
1870
+ declarations: rule.style.serializeSafe("", strict)
1694
1871
  };
1695
1872
  if (rule instanceof CSSFontFaceRule || rule instanceof CSSMarginRule || rule instanceof CSSPositionTryRule || rule instanceof CSSKeyframeRule) {
1696
1873
  const blockIndex = rule.cssText.indexOf("{");
1697
1874
  return {
1698
1875
  kind: "block",
1699
1876
  header: rule.cssText.slice(0, blockIndex).trimEnd(),
1700
- declarations: rule.style.serializeSafe(" ")
1877
+ declarations: rule.style.serializeSafe(" ", strict)
1701
1878
  };
1702
1879
  }
1703
1880
  if (rule instanceof CSSKeyframesRule) return {
@@ -1708,7 +1885,7 @@ function describeRuleSafe(rule) {
1708
1885
  if (rule instanceof CSSPageRule) return {
1709
1886
  kind: "block",
1710
1887
  header: `@page${rule.selectorText === "" ? "" : ` ${rule.selectorText}`}`,
1711
- declarations: rule.style.serializeSafe(" "),
1888
+ declarations: rule.style.serializeSafe(" ", strict),
1712
1889
  children: ruleTree.children(rule)
1713
1890
  };
1714
1891
  if (!(rule instanceof CSSGroupingRule)) return {
@@ -1815,24 +1992,19 @@ function isNativeMetadataRule(description) {
1815
1992
  function createRuleShellFromNative(description, reportDiagnostic, preserveImports, resourceBudget) {
1816
1993
  let rule;
1817
1994
  switch (description.kind) {
1818
- case "style": {
1819
- const style = constructWebIDL(CSSStyleRule, [description.prelude]);
1820
- style.style.cssText = description.declarations;
1821
- rule = style;
1995
+ case "style":
1996
+ rule = constructWebIDL(CSSStyleRule, [
1997
+ description.prelude,
1998
+ normalizedSelectorText,
1999
+ description.declarations
2000
+ ]);
1822
2001
  break;
1823
- }
1824
- case "nested-declarations": {
1825
- const nested = constructWebIDL(CSSNestedDeclarations);
1826
- nested.style.cssText = description.declarations;
1827
- rule = nested;
2002
+ case "nested-declarations":
2003
+ rule = constructWebIDL(CSSNestedDeclarations, [description.declarations]);
1828
2004
  break;
1829
- }
1830
- case "function-declarations": {
1831
- const declarations = constructWebIDL(CSSFunctionDeclarations);
1832
- declarations.style.cssText = description.declarations;
1833
- rule = declarations;
2005
+ case "function-declarations":
2006
+ rule = constructWebIDL(CSSFunctionDeclarations, [description.declarations]);
1834
2007
  break;
1835
- }
1836
2008
  case "import": {
1837
2009
  if (!preserveImports) return null;
1838
2010
  const parsed = parseImportPrelude(description.cssText.replace(/^@import\s+/iu, "").replace(/;\s*$/u, ""));
@@ -1845,39 +2017,24 @@ function createRuleShellFromNative(description, reportDiagnostic, preserveImport
1845
2017
  ]);
1846
2018
  break;
1847
2019
  }
1848
- case "font-face": {
1849
- const fontFace = constructWebIDL(CSSFontFaceRule);
1850
- fontFace.style.cssText = description.declarations;
1851
- rule = fontFace;
2020
+ case "font-face":
2021
+ rule = constructWebIDL(CSSFontFaceRule, [description.declarations]);
1852
2022
  break;
1853
- }
1854
- case "page": {
1855
- const page = constructWebIDL(CSSPageRule, [description.prelude]);
1856
- page.style.cssText = description.declarations;
1857
- rule = page;
2023
+ case "page":
2024
+ rule = constructWebIDL(CSSPageRule, [description.prelude, description.declarations]);
1858
2025
  break;
1859
- }
1860
- case "margin": {
1861
- const margin = constructWebIDL(CSSMarginRule, [description.prelude]);
1862
- margin.style.cssText = description.declarations;
1863
- rule = margin;
2026
+ case "margin":
2027
+ rule = constructWebIDL(CSSMarginRule, [description.prelude, description.declarations]);
1864
2028
  break;
1865
- }
1866
- case "position-try": {
1867
- const positionTry = constructWebIDL(CSSPositionTryRule, [description.prelude]);
1868
- positionTry.style.cssText = description.declarations;
1869
- rule = positionTry;
2029
+ case "position-try":
2030
+ rule = constructWebIDL(CSSPositionTryRule, [description.prelude, description.declarations]);
1870
2031
  break;
1871
- }
1872
2032
  case "keyframes":
1873
2033
  rule = constructWebIDL(CSSKeyframesRule, [description.prelude]);
1874
2034
  break;
1875
- case "keyframe": {
1876
- const keyframe = constructWebIDL(CSSKeyframeRule, [description.prelude]);
1877
- keyframe.style.cssText = description.declarations;
1878
- rule = keyframe;
2035
+ case "keyframe":
2036
+ rule = constructWebIDL(CSSKeyframeRule, [description.prelude, description.declarations]);
1879
2037
  break;
1880
- }
1881
2038
  case "counter-style": {
1882
2039
  const counterStyle = constructWebIDL(CSSCounterStyleRule, [description.prelude]);
1883
2040
  hydrateCounterStyleDescriptors(counterStyle, description.declarations, resourceBudget);
@@ -2103,9 +2260,13 @@ var CSSStyleSheet = class {
2103
2260
  }
2104
2261
  /** Serialize current state as reparsable CSS without mutating live objects. */
2105
2262
  serialize() {
2106
- return this.#rules.map(serializeRuleSafe).join("");
2263
+ return safeSerializer.serializeMany(this.#rules);
2264
+ }
2265
+ /** Serialize current state only when its CSS semantics can be preserved exactly. */
2266
+ serializeStrict() {
2267
+ return strictSerializer.serializeMany(this.#rules);
2107
2268
  }
2108
- /** Drain opt-in mutation diagnostics without affecting CSSOM behavior. */
2269
+ /** Drain opt-in mutation and serialization diagnostics without affecting CSSOM behavior. */
2109
2270
  takeDiagnostics() {
2110
2271
  if (!this.#diagnostics) return [];
2111
2272
  return this.#diagnostics.splice(0);
@@ -2123,6 +2284,6 @@ function parseStyleSheet(cssText, options = {}) {
2123
2284
  return sheet;
2124
2285
  }
2125
2286
  //#endregion
2126
- export { CSSConditionRule, CSSContainerRule, CSSCounterStyleRule, CSSFontFaceRule, CSSFontFeatureValuesMap, CSSFontFeatureValuesRule, CSSFontPaletteValuesRule, CSSFunctionDeclarations, CSSFunctionDescriptors, CSSFunctionRule, CSSGroupingRule, CSSImportRule, CSSKeyframeRule, CSSKeyframesRule, CSSLayerBlockRule, CSSLayerStatementRule, CSSMarginRule, CSSMediaRule, CSSNamespaceRule, CSSNestedDeclarations, CSSPageRule, CSSPositionTryRule, CSSPropertyRule, CSSRule, CSSRuleList, CSSScopeRule, CSSStartingStyleRule, CSSStyleDeclaration, CSSStyleRule, CSSStyleSheet, CSSSupportsRule, CSSViewTransitionRule, MediaList, parseStyleSheet };
2287
+ export { CSSConditionRule, CSSContainerRule, CSSCounterStyleRule, CSSFontFaceRule, CSSFontFeatureValuesMap, CSSFontFeatureValuesRule, CSSFontPaletteValuesRule, CSSFunctionDeclarations, CSSFunctionDescriptors, CSSFunctionRule, CSSGroupingRule, CSSImportRule, CSSKeyframeRule, CSSKeyframesRule, CSSLayerBlockRule, CSSLayerStatementRule, CSSMarginRule, CSSMediaRule, CSSNamespaceRule, CSSNestedDeclarations, CSSPageRule, CSSPositionTryRule, CSSPropertyRule, CSSRule, CSSRuleList, CSSScopeRule, CSSStartingStyleRule, CSSStyleDeclaration, CSSStyleRule, CSSStyleSheet, CSSSupportsRule, CSSViewTransitionRule, MediaList, SheetOMSerializationError, parseStyleSheet };
2127
2288
 
2128
2289
  //# sourceMappingURL=index.js.map