single-file-core 1.6.9 → 1.6.11

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.
@@ -25,14 +25,17 @@ import * as cssTree from "./../vendor/css-tree.js";
25
25
  import { computeMaxSpecificity } from "./css-specificity.js";
26
26
  import { parsePrelude } from "./css-scope-prelude-parser.js";
27
27
  import { sanitizeSelector, matchUnqueryableAttributeSelector, matchUnqueryablePseudoClass } from "./css-selector-sanitizer.js";
28
+ import { ESCAPE_CHARACTER, decodeIdentifier, decodeName } from "./css-identifier.js";
28
29
 
29
30
  const DEBUG = false;
30
31
 
31
32
  const PSEUDO_ELEMENT_SYNONYMS = new Set(["after", "before", "first-letter", "first-line"]);
32
- const FUNCTIONAL_PSEUDO_CLASS_NAMES = new Set(["not", "is", "where", "has"]);
33
+ const FUNCTIONAL_PSEUDO_CLASS_NAMES = new Set(["not", "is", "where", "has", "nth-child", "nth-last-child"]);
33
34
  const MEDIA_AT_RULE_NAME = "media";
34
35
  const SUPPORTS_AT_RULE_NAME = "supports";
35
- const CONDITIONAL_AT_RULE_NAMES = new Set([MEDIA_AT_RULE_NAME, SUPPORTS_AT_RULE_NAME, "container"]);
36
+ const STARTING_STYLE_AT_RULE_NAME = "starting-style";
37
+ const CONDITIONAL_AT_RULE_NAMES = new Set([MEDIA_AT_RULE_NAME, SUPPORTS_AT_RULE_NAME, "container", STARTING_STYLE_AT_RULE_NAME]);
38
+ const UNCERTAIN_CONDITIONAL_AT_RULE_NAMES = new Set([MEDIA_AT_RULE_NAME, SUPPORTS_AT_RULE_NAME, STARTING_STYLE_AT_RULE_NAME]);
36
39
  const RULE_TYPE = "Rule";
37
40
  const AT_RULE_TYPE = "Atrule";
38
41
  const NESTING_SELECTOR_TYPE = "NestingSelector";
@@ -41,6 +44,7 @@ const ATTRIBUTE_SELECTOR_TYPE = "AttributeSelector";
41
44
  const DECLARATION_TYPE = "Declaration";
42
45
  const RAW_TYPE = "Raw";
43
46
  const VALUE_TYPE = "Value";
47
+ const IDENTIFIER_TYPE = "Identifier";
44
48
  const PSEUDO_ELEMENT_SELECTOR_TYPE = "PseudoElementSelector";
45
49
  const LAYER_NAME = "layer";
46
50
  const SCOPE_NAME = "scope";
@@ -51,35 +55,58 @@ const COMBINATOR_NAME = "Combinator";
51
55
  const TYPE_SELECTOR_TYPE = "TypeSelector";
52
56
  const STYLE_ATTRIBUTE_NAME = "style";
53
57
  const SELECTOR_LIST_CONTEXT = "selectorList";
58
+ const SCOPE_PRELUDE_OPTIONS = { context: "atrulePrelude", atrule: SCOPE_NAME };
54
59
  const STYLESHEET_CONTEXT = "stylesheet";
55
60
  const SELECTOR_CONTEXT = "selector";
56
61
  const DECLARATION_LIST_CONTEXT = "declarationList";
57
62
  const UNKNOWN_PROPERTY_ERROR_NAME = "SyntaxReferenceError";
63
+ const VALUE_MISMATCH_ERROR_NAME = "SyntaxMatchError";
64
+ const VAR_FUNCTION_NAME = "var";
65
+ const FUNCTION_TYPE = "Function";
66
+ const VALIDITY_VALID = "valid";
67
+ const VALIDITY_UNKNOWN = "unknown";
68
+ const VALIDITY_INVALID = "invalid";
58
69
  const PARSE_CSS_ERROR_MESSAGE = "Failed to parse CSS";
59
70
  const QSA_ERROR_MESSAGE = "Failed to match selector";
60
71
  const PRELUDE_SEPARATOR = ",";
61
- const NESTING_SELECTOR = "&";
72
+ const DECLARATION_SEPARATOR = ":";
73
+ const IS_PSEUDO_CLASS_PREFIX = ":is(";
74
+ const IS_PSEUDO_CLASS_SUFFIX = ")";
75
+ const NO_ELEMENT_SELECTOR = ":not(*)";
62
76
  const SCOPE_PSEUDO_CLASS = ":scope";
77
+ const SCOPE_PSEUDO_CLASS_NAME = "scope";
78
+ const DESCENDANT_COMBINATOR = " ";
63
79
  const NAMESPACE_SEPARATOR = "|";
64
80
  const SELECTOR_SUPPORTS_PREFIX = "selector(";
65
81
  const SELECTOR_SUPPORTS_SUFFIX = ")";
66
82
  const VENDOR_PREFIX = "-";
67
83
  const CUSTOM_PROPERTY_PREFIX = "--";
68
84
  const LAYER_NAME_SEPARATOR = ".";
69
- const DECLARATION_KEY_SEPARATOR = ":";
85
+ const LAYER_NAME_KEY_SEPARATOR = "\u0000";
70
86
  const CONTEXT_KEY_SEPARATOR = "|";
87
+ const REVERT_LAYER_KEYWORD = "revert-layer";
88
+ const REVERT_LAYER_TEST = /(^|[^-\w])revert-layer([^-\w]|$)/i;
89
+ const NAMESPACE_AT_RULE_NAME = "namespace";
90
+ const URL_TYPE = "Url";
91
+ const STRING_TYPE = "String";
92
+ const UNSCOPED_PROXIMITY = Infinity;
71
93
  const BLOCK_OPEN = "{";
72
94
  const BLOCK_CLOSE = "}";
73
95
  const EMPTY_STRING = "";
74
96
  const CSS_IMPORTANCE_NOT_IMPORTANT = 0;
75
97
  const CSS_IMPORTANCE_IMPORTANT = 1;
76
98
  const INVALID_CSS_ESCAPE_TEST = /\\(?![0-9a-fA-F]{1,6}\s|[^0-9a-zA-Z])/;
77
- const ANONYMOUS_LAYER_PLACEHOLDER = "\u0000";
99
+ const ANONYMOUS_LAYER_PREFIX = "\u0000";
100
+ const UNCERTAIN_LAYER_ORDER = null;
101
+ const UNDECLARED_LAYER_POSITION = Infinity;
78
102
 
79
103
  export {
80
104
  process,
81
105
  isUnsupportedPropertyValue,
82
- isUnsupportedVendorValue
106
+ getValueValidity,
107
+ VALIDITY_VALID,
108
+ VALIDITY_UNKNOWN,
109
+ VALIDITY_INVALID
83
110
  };
84
111
 
85
112
  function isUnsupportedPropertyValue(property, value) {
@@ -87,28 +114,41 @@ function isUnsupportedPropertyValue(property, value) {
87
114
  return Boolean(!match.matched && match.error && match.error.name !== UNKNOWN_PROPERTY_ERROR_NAME);
88
115
  }
89
116
 
90
- // A vendor-prefixed VALUE is dropped only when this browser cannot actually use it. Dropping every
91
- // one of them was the overreach: `display:-ms-flexbox` is genuinely dead here and worth removing,
92
- // but `display:-webkit-box` is alive and load-bearing — `-webkit-line-clamp` does nothing without
93
- // it, so a page keeps its clamp rules and silently stops clamping. On anandabazar.com that expanded
94
- // 80 clamped headlines by a line each and moved the page 150px, with nothing wrong-looking left in
95
- // the saved CSS. Four of the five sites in a fifteen-site sweep that use line-clamp were affected.
96
- function isUnsupportedVendorValue(property, name) {
97
- if (!name.startsWith(VENDOR_PREFIX)) {
98
- return false;
117
+ function getValueValidity(property, value) {
118
+ const singleNode = value.children.size === 1 ? value.children.head.data : null;
119
+ const name = singleNode && typeof singleNode.name === "string" ? singleNode.name : null;
120
+ if (name && INVALID_CSS_ESCAPE_TEST.test(name)) {
121
+ return VALIDITY_INVALID;
99
122
  }
100
- // unknown fails open, the same rule the unknown-property branch follows: a dropped valid
101
- // declaration breaks rendering, a kept invalid one is ignored. With no browser to ask, keep.
102
- if (!globalThis.CSS || !globalThis.CSS.supports) {
103
- return false;
123
+ const isVendorValue = Boolean(name && name.startsWith(VENDOR_PREFIX));
124
+ if (globalThis.CSS && globalThis.CSS.supports) {
125
+ let supported;
126
+ try {
127
+ supported = globalThis.CSS.supports(property, cssTree.generate(value));
128
+ } catch {
129
+ return VALIDITY_UNKNOWN;
130
+ }
131
+ if (supported) {
132
+ return VALIDITY_VALID;
133
+ }
134
+ return isVendorValue ? VALIDITY_INVALID : VALIDITY_UNKNOWN;
104
135
  }
136
+ if (cssTree.find(value, node => node.type === FUNCTION_TYPE && decodeName(node.name) === VAR_FUNCTION_NAME)) {
137
+ return VALIDITY_VALID;
138
+ }
139
+ let match;
105
140
  try {
106
- return !globalThis.CSS.supports(property, name);
107
- // eslint-disable-next-line no-unused-vars
108
- } catch (error) {
109
- // a value CSS.supports will not even take as an argument tells us nothing either way
110
- return false;
141
+ match = cssTree.lexer.matchProperty(property, value);
142
+ } catch {
143
+ return VALIDITY_UNKNOWN;
111
144
  }
145
+ if (match.matched) {
146
+ return VALIDITY_VALID;
147
+ }
148
+ if (match.error && match.error.name === VALUE_MISMATCH_ERROR_NAME && !isVendorValue && !property.startsWith(VENDOR_PREFIX)) {
149
+ return VALIDITY_INVALID;
150
+ }
151
+ return VALIDITY_UNKNOWN;
112
152
  }
113
153
 
114
154
  function process(doc, stylesheets) {
@@ -116,20 +156,30 @@ function process(doc, stylesheets) {
116
156
  doc,
117
157
  stats: { processed: 0, discarded: 0 },
118
158
  matchedElements: new Set(),
159
+ revertLayerElements: new Set(),
160
+ revertLayerRules: new WeakMap(),
161
+ revertLayerDeclarations: new WeakMap(),
162
+ hasIndeterminateRevertLayer: false,
119
163
  matchedSelectors: new Map(),
120
164
  matchingSelectors: new Map(),
165
+ scopeProximities: new Map(),
166
+ scopeProximityKeys: new Map(),
121
167
  layerDeclarationCounter: 0,
122
- layerDeclarations: [],
168
+ anonymousLayerCounter: 0,
123
169
  layerOrder: new Map(),
170
+ layerComparisons: new Map(),
124
171
  selectorData: new Map(),
125
172
  selectorTexts: new Map(),
173
+ nestingParentTexts: new Map(),
174
+ nestingParentNodes: new Map(),
175
+ scopedSelectorTexts: new Map(),
126
176
  supportedSelectors: new Map(),
177
+ valueValidities: new Map(),
127
178
  preludeTexts: new Map(),
128
179
  rulesCounter: 0,
129
180
  scopeIdCounter: 0
130
181
  };
131
182
  collectLayerOrder(stylesheets, docContext);
132
- buildEffectiveLayerOrder(docContext);
133
183
  minifyRules(stylesheets, docContext);
134
184
  computeCascade(docContext);
135
185
  removeEmptyRules(stylesheets, docContext);
@@ -140,36 +190,27 @@ function collectLayerOrder(stylesheets, docContext) {
140
190
  stylesheets.forEach((stylesheetInfo, key) => {
141
191
  if (!stylesheetInfo.scoped && stylesheetInfo.stylesheet && !key.urlNode) {
142
192
  if (hasChildNodes(stylesheetInfo.stylesheet)) {
143
- collectStylesheetLayerOrder(stylesheetInfo.stylesheet.children, { layerStack: [], conditionalStack: [] }, docContext);
193
+ collectStylesheetLayerOrder(stylesheetInfo.stylesheet.children, { layerStack: [], conditionalStack: getTopConditionalStack(stylesheetInfo) }, docContext);
144
194
  }
145
195
  }
146
196
  });
147
197
  }
148
198
 
149
- function buildEffectiveLayerOrder(docContext) {
150
- const layerNames = [];
151
- for (let indexDeclaration = 0; indexDeclaration < docContext.layerDeclarations.length; indexDeclaration++) {
152
- const declaration = docContext.layerDeclarations[indexDeclaration];
153
- layerNames.push(declaration.name);
154
- }
155
- for (let indexLayerName = 0; indexLayerName < layerNames.length; indexLayerName++) {
156
- const name = layerNames[indexLayerName];
157
- if (!docContext.layerOrder.has(name)) {
158
- docContext.layerOrder.set(name, docContext.layerOrder.size);
159
- }
160
- }
199
+ function getTopConditionalStack(stylesheetInfo) {
200
+ return stylesheetInfo.mediaText ? [{ name: MEDIA_AT_RULE_NAME, prelude: stylesheetInfo.mediaText }] : [];
161
201
  }
162
202
 
163
203
  function minifyRules(stylesheets, docContext) {
164
204
  stylesheets.forEach((stylesheetInfo, key) => {
165
205
  if (!stylesheetInfo.scoped && stylesheetInfo.stylesheet && !key.urlNode) {
166
206
  if (hasChildNodes(stylesheetInfo.stylesheet)) {
167
- const topConditionalStack = stylesheetInfo.mediaText ? [{ name: MEDIA_AT_RULE_NAME, prelude: stylesheetInfo.mediaText }] : [];
168
207
  minifyStylesheetRules(stylesheetInfo.stylesheet.children, stylesheets, {
169
208
  ancestorsSelectors: [],
170
209
  layerStack: [],
171
210
  scopeStack: [],
172
- conditionalStack: topConditionalStack
211
+ conditionalStack: getTopConditionalStack(stylesheetInfo),
212
+ ownerElement: getStylesheetOwnerElement(key),
213
+ hasDefaultNamespace: hasDefaultNamespace(stylesheetInfo.stylesheet.children)
173
214
  }, docContext);
174
215
  }
175
216
  }
@@ -179,7 +220,9 @@ function minifyRules(stylesheets, docContext) {
179
220
  function computeCascade(docContext) {
180
221
  const winningDeclarations = new Set();
181
222
  docContext.matchedElements.forEach(element => computeCascadedStylesForElement(element, winningDeclarations, docContext));
182
- removeLosingDeclarations(winningDeclarations, docContext);
223
+ if (!docContext.hasIndeterminateRevertLayer) {
224
+ removeLosingDeclarations(winningDeclarations, docContext);
225
+ }
183
226
  }
184
227
 
185
228
  function removeEmptyRules(stylesheets, docContext) {
@@ -196,8 +239,10 @@ function collectStylesheetLayerOrder(cssRules, layerContext, docContext) {
196
239
  const { layerStack, conditionalStack } = layerContext;
197
240
  for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
198
241
  const ruleData = cssRule.data;
199
- if (ruleData.type === AT_RULE_TYPE && ruleData.name === LAYER_NAME) {
242
+ if (ruleData.type === AT_RULE_TYPE && decodeName(ruleData.name) === LAYER_NAME) {
200
243
  collectStylesheetLayerRule(ruleData, layerStack, conditionalStack, docContext);
244
+ } else if (isImportRule(ruleData)) {
245
+ collectImportLayerOrder(ruleData, layerContext, docContext);
201
246
  } else if (ruleData.type === AT_RULE_TYPE && hasChildNodes(ruleData.block)) {
202
247
  const newConditionalStack = buildConditionalStack(conditionalStack, ruleData, docContext);
203
248
  collectStylesheetLayerOrder(ruleData.block.children, { layerStack, conditionalStack: newConditionalStack }, docContext);
@@ -209,48 +254,136 @@ function collectStylesheetLayerOrder(cssRules, layerContext, docContext) {
209
254
 
210
255
  function collectStylesheetLayerRule(ruleData, layerStack, conditionalStack, docContext) {
211
256
  if (ruleData.block) {
212
- const layerName = getPreludeText(ruleData.prelude, docContext);
213
- registerLayerDeclaration(layerStack, layerName, conditionalStack, docContext);
214
- collectStylesheetLayerOrder(ruleData.block.children, { layerStack: [...layerStack, layerName], conditionalStack }, docContext);
257
+ const layerSegments = getLayerSegments(ruleData, docContext);
258
+ registerLayerDeclaration(layerStack, layerSegments, conditionalStack, docContext);
259
+ collectStylesheetLayerOrder(ruleData.block.children, { layerStack: [...layerStack, ...layerSegments], conditionalStack }, docContext);
215
260
  } else if (ruleData.prelude) {
216
- const layerNames = getPreludeText(ruleData.prelude, docContext).split(PRELUDE_SEPARATOR);
217
- layerNames.forEach(layerName => registerLayerDeclaration(layerStack, layerName, conditionalStack, docContext));
261
+ getPreludeText(ruleData.prelude, docContext).split(PRELUDE_SEPARATOR).forEach(layerName => registerLayerDeclaration(layerStack, splitLayerName(layerName), conditionalStack, docContext));
262
+ }
263
+ }
264
+
265
+ function collectImportLayerOrder(ruleData, layerContext, docContext) {
266
+ const urlNode = ruleData.prelude.children.head.data;
267
+ const conditionalStack = buildImportConditionalStack(layerContext.conditionalStack, urlNode);
268
+ const layerSegments = getImportLayerSegments(ruleData, urlNode, docContext);
269
+ let { layerStack } = layerContext;
270
+ if (layerSegments) {
271
+ registerLayerDeclaration(layerStack, layerSegments, conditionalStack, docContext);
272
+ layerStack = [...layerStack, ...layerSegments];
273
+ }
274
+ collectStylesheetLayerOrder(urlNode.importedChildren, { layerStack, conditionalStack }, docContext);
275
+ }
276
+
277
+ function getLayerSegments(ruleData, docContext) {
278
+ if (!ruleData.layerSegments) {
279
+ ruleData.layerSegments = ruleData.prelude ? splitLayerName(getPreludeText(ruleData.prelude, docContext)) : [createAnonymousLayerSegment(docContext)];
218
280
  }
281
+ return ruleData.layerSegments;
282
+ }
283
+
284
+ function splitLayerName(layerName) {
285
+ const name = layerName.trim();
286
+ const segments = [];
287
+ let segment = EMPTY_STRING;
288
+ for (let index = 0; index < name.length; index++) {
289
+ const character = name.charAt(index);
290
+ if (character === ESCAPE_CHARACTER && index + 1 < name.length) {
291
+ segment += character + name.charAt(index + 1);
292
+ index++;
293
+ } else if (character === LAYER_NAME_SEPARATOR) {
294
+ segments.push(decodeIdentifier(segment));
295
+ segment = EMPTY_STRING;
296
+ } else {
297
+ segment += character;
298
+ }
299
+ }
300
+ segments.push(decodeIdentifier(segment));
301
+ return segments;
302
+ }
303
+
304
+ function createAnonymousLayerSegment(docContext) {
305
+ return ANONYMOUS_LAYER_PREFIX + docContext.anonymousLayerCounter++;
219
306
  }
220
307
 
221
308
  function buildConditionalStack(conditionalStack, ruleData, docContext) {
222
- const isConditional = CONDITIONAL_AT_RULE_NAMES.has(ruleData.name);
309
+ const isConditional = CONDITIONAL_AT_RULE_NAMES.has(decodeName(ruleData.name));
223
310
  return isConditional
224
311
  ? [...conditionalStack, { name: ruleData.name, prelude: getPreludeText(ruleData.prelude, docContext) }]
225
312
  : conditionalStack;
226
313
  }
227
314
 
228
- function registerLayerDeclaration(layerStack, layerName, conditionalStack, docContext) {
229
- const fullLayerName = getFullLayerName([...layerStack, layerName]);
230
- docContext.layerDeclarations.push({
231
- name: fullLayerName,
232
- order: docContext.layerDeclarationCounter++,
233
- conditionalStack: conditionalStack.slice()
234
- });
315
+ function buildImportConditionalStack(conditionalStack, urlNode) {
316
+ const importConditionalStack = [...conditionalStack];
317
+ if (urlNode.importedMediaText) {
318
+ importConditionalStack.push({ name: MEDIA_AT_RULE_NAME, prelude: urlNode.importedMediaText });
319
+ }
320
+ if (urlNode.importedSupportsCondition !== undefined) {
321
+ importConditionalStack.push({ name: SUPPORTS_AT_RULE_NAME, prelude: urlNode.importedSupportsCondition });
322
+ }
323
+ return importConditionalStack;
324
+ }
325
+
326
+ function getImportLayerSegments(ruleData, urlNode, docContext) {
327
+ if (urlNode.importedLayerName !== undefined) {
328
+ return splitLayerName(urlNode.importedLayerName);
329
+ }
330
+ if (!urlNode.anonymousLayerSegments) {
331
+ const layerKeyword = cssTree.find(ruleData.prelude, node => node.type === IDENTIFIER_TYPE && decodeName(node.name) === LAYER_NAME);
332
+ urlNode.anonymousLayerSegments = layerKeyword ? [createAnonymousLayerSegment(docContext)] : null;
333
+ }
334
+ return urlNode.anonymousLayerSegments;
335
+ }
336
+
337
+ function isImportRule(ruleData) {
338
+ return ruleData.type === AT_RULE_TYPE && decodeName(ruleData.name) === IMPORT_NAME && hasChildNodes(ruleData.prelude) && Boolean(ruleData.prelude.children.head.data.importedChildren);
339
+ }
340
+
341
+ function registerLayerDeclaration(layerStack, layerSegments, conditionalStack, docContext) {
342
+ const position = docContext.layerDeclarationCounter++;
343
+ const certain = !conditionalStack.some(context => UNCERTAIN_CONDITIONAL_AT_RULE_NAMES.has(decodeName(context.name)));
344
+ const segments = [...layerStack, ...layerSegments];
345
+ for (let length = layerStack.length + 1; length <= segments.length; length++) {
346
+ const fullLayerName = getFullLayerName(segments.slice(0, length));
347
+ const layerPosition = docContext.layerOrder.get(fullLayerName);
348
+ if (!layerPosition) {
349
+ docContext.layerOrder.set(fullLayerName, { first: position, firstCertain: certain ? position : UNDECLARED_LAYER_POSITION });
350
+ } else if (certain && layerPosition.firstCertain === UNDECLARED_LAYER_POSITION) {
351
+ layerPosition.firstCertain = position;
352
+ }
353
+ }
235
354
  }
236
355
 
237
356
  function minifyStylesheetRules(cssRules, stylesheets, processingContext, docContext) {
238
357
  const removedRules = new Set();
358
+ let nestedRuleSeen = false;
359
+ let declarationsOrder;
239
360
  for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
240
361
  docContext.stats.processed++;
241
- minifyRule(cssRule.data, cssRule, stylesheets, processingContext, removedRules, docContext);
362
+ if (cssRule.data.type === DECLARATION_TYPE) {
363
+ if (nestedRuleSeen) {
364
+ if (declarationsOrder === undefined) {
365
+ declarationsOrder = docContext.rulesCounter++;
366
+ }
367
+ cssRule.data.order = declarationsOrder;
368
+ }
369
+ } else {
370
+ minifyRule(cssRule.data, cssRule, stylesheets, processingContext, removedRules, docContext);
371
+ nestedRuleSeen = true;
372
+ declarationsOrder = undefined;
373
+ }
242
374
  }
243
375
  removedRules.forEach(cssRule => cssRules.remove(cssRule));
244
376
  }
245
377
 
246
378
  function minifyRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
247
- if (ruleData.type === AT_RULE_TYPE && ruleData.name === IMPORT_NAME && hasChildNodes(ruleData.prelude) && ruleData.prelude.children.head.data.importedChildren) {
379
+ const atRuleName = decodeName(ruleData.name);
380
+ if (isImportRule(ruleData)) {
248
381
  minifyImportRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
249
- } else if (ruleData.type === AT_RULE_TYPE && ruleData.name === LAYER_NAME && hasChildNodes(ruleData.block)) {
382
+ } else if (ruleData.type === AT_RULE_TYPE && atRuleName === LAYER_NAME && hasChildNodes(ruleData.block)) {
250
383
  minifyLayerRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
251
- } else if (ruleData.type === AT_RULE_TYPE && ruleData.name === SCOPE_NAME && hasChildNodes(ruleData.block)) {
384
+ } else if (ruleData.type === AT_RULE_TYPE && atRuleName === SCOPE_NAME && hasChildNodes(ruleData.block)) {
252
385
  minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
253
- } else if (ruleData.type === AT_RULE_TYPE && ruleData.name !== FONT_FACE_NAME && ruleData.name !== KEYFRAMES_NAME && !ruleData.name.startsWith(VENDOR_PREFIX) && hasChildNodes(ruleData.block)) {
386
+ } else if (ruleData.type === AT_RULE_TYPE && atRuleName !== FONT_FACE_NAME && atRuleName !== KEYFRAMES_NAME && !atRuleName.startsWith(VENDOR_PREFIX) && hasChildNodes(ruleData.block)) {
254
387
  minifyAtRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
255
388
  } else if (ruleData.type === RULE_TYPE && hasChildNodes(ruleData.prelude)) {
256
389
  minifyStylesheetRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
@@ -259,34 +392,52 @@ function minifyRule(ruleData, cssRule, stylesheets, processingContext, removedRu
259
392
 
260
393
  function minifyImportRule(ruleData, _cssRule, stylesheets, processingContext, _removedRules, docContext) {
261
394
  const urlNode = ruleData.prelude.children.head.data;
262
- const topConditionalStack = urlNode.importedMediaText ? [{ name: MEDIA_AT_RULE_NAME, prelude: urlNode.importedMediaText }] : [];
263
- if (urlNode.importedLayerName !== undefined) {
264
- topConditionalStack.push({ name: LAYER_NAME, prelude: urlNode.importedLayerName });
265
- }
266
- if (urlNode.importedSupportsCondition !== undefined) {
267
- topConditionalStack.push({ name: SUPPORTS_AT_RULE_NAME, prelude: urlNode.importedSupportsCondition });
268
- }
395
+ const conditionalStack = buildImportConditionalStack(processingContext.conditionalStack, urlNode);
396
+ const layerSegments = getImportLayerSegments(ruleData, urlNode, docContext);
397
+ const layerStack = layerSegments ? [...processingContext.layerStack, ...layerSegments] : processingContext.layerStack;
269
398
  minifyStylesheetRules(urlNode.importedChildren, stylesheets, {
270
399
  ...processingContext,
271
- conditionalStack: topConditionalStack
400
+ layerStack,
401
+ conditionalStack,
402
+ hasDefaultNamespace: processingContext.hasDefaultNamespace || hasDefaultNamespace(urlNode.importedChildren)
272
403
  }, docContext);
273
404
  }
274
405
 
275
406
  function minifyLayerRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
276
- const layerName = getPreludeText(ruleData.prelude, docContext);
277
- const newProcessingContext = { ...processingContext, layerStack: [...processingContext.layerStack, layerName] };
407
+ const layerSegments = getLayerSegments(ruleData, docContext);
408
+ const layerStack = [...processingContext.layerStack, ...layerSegments];
409
+ if (!docContext.layerOrder.has(getFullLayerName(layerStack))) {
410
+ registerLayerDeclaration(processingContext.layerStack, layerSegments, processingContext.conditionalStack, docContext);
411
+ }
412
+ const newProcessingContext = { ...processingContext, layerStack };
278
413
  expandRawCssRules(ruleData);
279
414
  minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
280
415
  if (!hasChildNodes(ruleData.block)) {
416
+ removeEmptyLayerRule(ruleData, cssRule, removedRules, docContext);
417
+ }
418
+ }
419
+
420
+ function removeEmptyLayerRule(ruleData, cssRule, removedRules, docContext) {
421
+ if (hasChildNodes(ruleData.prelude)) {
422
+ ruleData.block = null;
423
+ } else {
281
424
  docContext.stats.discarded++;
282
425
  removedRules.add(cssRule);
283
426
  }
284
427
  }
285
428
 
429
+ function getScopePrelude(ruleData) {
430
+ const { prelude } = ruleData;
431
+ if (prelude && prelude.type === RAW_TYPE) {
432
+ return cssTree.parse(cssTree.generate(prelude), SCOPE_PRELUDE_OPTIONS);
433
+ }
434
+ return prelude;
435
+ }
436
+
286
437
  function minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
287
438
  let scopeContext;
288
439
  try {
289
- const parsedPrelude = parsePrelude(ruleData.prelude);
440
+ const parsedPrelude = parsePrelude(getScopePrelude(ruleData));
290
441
  scopeContext = buildScopeContext(parsedPrelude, processingContext, docContext);
291
442
  } catch (error) {
292
443
  if (DEBUG) {
@@ -301,7 +452,9 @@ function minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, remo
301
452
  }
302
453
  const newProcessingContext = {
303
454
  ...processingContext,
304
- scopeStack: [...(processingContext.scopeStack || []), scopeContext]
455
+ scopeStack: [...(processingContext.scopeStack || []), scopeContext],
456
+ hasUnqueryableSelector: processingContext.hasUnqueryableSelector || scopeContext.hasUnqueryableSelector,
457
+ hasNestedUnqueryablePseudoClass: processingContext.hasNestedUnqueryablePseudoClass || scopeContext.hasNestedUnqueryablePseudoClass
305
458
  };
306
459
  expandRawCssRules(ruleData);
307
460
  minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
@@ -314,50 +467,102 @@ function minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, remo
314
467
  function buildScopeContext(parsedPrelude, processingContext, docContext) {
315
468
  const scopeStack = processingContext.scopeStack || [];
316
469
  const includeSelectors = parsedPrelude && parsedPrelude.include ? parsedPrelude.include : [];
470
+ const excludeSelectors = parsedPrelude && parsedPrelude.exclude ? parsedPrelude.exclude : [];
471
+ const includeAnalysis = analyzeScopeSelectors(includeSelectors, processingContext.hasDefaultNamespace);
472
+ const excludeAnalysis = analyzeScopeSelectors(excludeSelectors, processingContext.hasDefaultNamespace);
317
473
  let rootElements = [];
318
474
  if (includeSelectors.length) {
319
475
  rootElements = collectScopeRootElements(includeSelectors, scopeStack, docContext);
320
- } else if (scopeStack.length) {
321
- rootElements = Array.from(scopeStack[scopeStack.length - 1].rootElements);
476
+ if (!rootElements.length && includeAnalysis.hasNestedUnqueryablePseudoClass) {
477
+ rootElements = scopeStack.length ? Array.from(scopeStack[scopeStack.length - 1].rootElements) : getDefaultScopeRoots(docContext);
478
+ }
322
479
  } else {
323
- rootElements = getDefaultScopeRoots(docContext);
480
+ rootElements = getImplicitScopeRoots(processingContext.ownerElement, docContext);
324
481
  }
325
482
  const uniqueRoots = Array.from(new Set(rootElements.filter(Boolean)));
326
483
  if (!uniqueRoots.length) {
327
484
  return null;
328
485
  }
329
- const boundaryElements = collectScopeBoundaryElements(parsedPrelude.exclude || [], uniqueRoots, docContext);
486
+ const boundaryElements = collectScopeBoundaryElements(excludeSelectors, uniqueRoots, docContext, processingContext.hasDefaultNamespace);
330
487
  return {
331
488
  id: docContext.scopeIdCounter++,
332
489
  rootElements: new Set(uniqueRoots),
333
490
  stopElements: boundaryElements,
491
+ hasUnqueryableSelector: includeAnalysis.hasUnqueryableSelector || excludeAnalysis.hasUnqueryableSelector,
492
+ hasNestedUnqueryablePseudoClass: includeAnalysis.hasNestedUnqueryablePseudoClass
334
493
  };
335
494
  }
336
495
 
496
+ function analyzeScopeSelectors(selectors, defaultNamespace) {
497
+ const analysis = { hasUnqueryableSelector: false, hasNestedUnqueryablePseudoClass: false };
498
+ selectors.forEach(selectorInfo => {
499
+ const { hasUnqueryableSelector, hasNestedUnqueryablePseudoClass } = analyzeSelector(selectorInfo.data, defaultNamespace);
500
+ analysis.hasUnqueryableSelector ||= hasUnqueryableSelector;
501
+ analysis.hasNestedUnqueryablePseudoClass ||= hasNestedUnqueryablePseudoClass;
502
+ });
503
+ return analysis;
504
+ }
505
+
337
506
  function collectScopeRootElements(includeSelectors, scopeStack, docContext) {
338
507
  const roots = new Set();
339
508
  includeSelectors.forEach(selectorInfo => {
340
- const selectorText = sanitizeSelector(selectorInfo, null, docContext);
341
- const matchedNodes = querySelectorAll(docContext.doc, selectorText);
509
+ const selectorText = sanitizeSelector(selectorInfo, docContext);
510
+ const matchedNodes = scopeStack.length
511
+ ? matchElementsInScope(getScopedSelectorText(selectorText, docContext), scopeStack)
512
+ : querySelectorAll(docContext.doc, selectorText);
342
513
  filterElementsByScopes(matchedNodes, scopeStack).forEach(match => roots.add(match));
343
514
  });
344
515
  return Array.from(roots);
345
516
  }
346
517
 
347
- function collectScopeBoundaryElements(excludeSelectors, rootElements, docContext) {
348
- const boundaries = new Set();
518
+ function collectScopeBoundaryElements(excludeSelectors, rootElements, docContext, defaultNamespace) {
519
+ const boundaries = new Map();
349
520
  if (!excludeSelectors.length || !rootElements.length) {
350
521
  return boundaries;
351
522
  }
352
523
  excludeSelectors.forEach(selectorInfo => {
353
- const selectorText = sanitizeSelector(selectorInfo, null, docContext);
354
- rootElements.forEach(root => {
355
- matchSelectorWithinRoot(root, selectorText).forEach(node => boundaries.add(node));
356
- });
524
+ const { hasUnqueryableSelector, hasNestedUnqueryablePseudoClass } = analyzeSelector(selectorInfo.data, defaultNamespace);
525
+ if (!hasUnqueryableSelector && !hasNestedUnqueryablePseudoClass) {
526
+ const selectorText = getScopedSelectorText(sanitizeSelector(selectorInfo, docContext), docContext);
527
+ rootElements.forEach(root => {
528
+ const rootBoundaries = boundaries.get(root) || new Set();
529
+ matchSelectorWithinRoot(root, selectorText).forEach(node => rootBoundaries.add(node));
530
+ if (rootBoundaries.size) {
531
+ boundaries.set(root, rootBoundaries);
532
+ }
533
+ });
534
+ }
357
535
  });
358
536
  return boundaries;
359
537
  }
360
538
 
539
+ function hasDefaultNamespace(cssRules) {
540
+ for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
541
+ const ruleData = cssRule.data;
542
+ if (ruleData.type === AT_RULE_TYPE && ruleData.name && decodeName(ruleData.name) === NAMESPACE_AT_RULE_NAME) {
543
+ const prelude = ruleData.prelude && ruleData.prelude.children && ruleData.prelude.children.head;
544
+ if (prelude && (prelude.data.type === URL_TYPE || prelude.data.type === STRING_TYPE)) {
545
+ return true;
546
+ }
547
+ }
548
+ }
549
+ return false;
550
+ }
551
+
552
+ function getStylesheetOwnerElement(key) {
553
+ if (key && key.element) {
554
+ return key.element;
555
+ }
556
+ return key && key.nodeType === 1 ? key : null;
557
+ }
558
+
559
+ function getImplicitScopeRoots(ownerElement, docContext) {
560
+ if (ownerElement && ownerElement.parentElement) {
561
+ return [ownerElement.parentElement];
562
+ }
563
+ return getDefaultScopeRoots(docContext);
564
+ }
565
+
361
566
  function getDefaultScopeRoots(docContext) {
362
567
  const roots = [];
363
568
  if (docContext.doc && docContext.doc.documentElement) {
@@ -388,7 +593,8 @@ function minifyStylesheetRule(ruleData, cssRule, stylesheets, processingContext,
388
593
  if (!isPreludeSupported(ruleData.prelude, docContext)) {
389
594
  return;
390
595
  }
391
- const removedSelectors = processSelectors(ruleData, processingContext, docContext);
596
+ expandRawCssRules(ruleData);
597
+ const removedSelectors = getRemovableSelectors(ruleData, processSelectors(ruleData, processingContext, docContext), docContext);
392
598
  const wasDiscarded = removeUnmatchedSelectors(ruleData, removedSelectors, removedRules, cssRule, docContext);
393
599
  if (!wasDiscarded && hasChildNodes(ruleData.block)) {
394
600
  processNestedRules(ruleData, stylesheets, processingContext, docContext);
@@ -399,32 +605,70 @@ function processSelectors(ruleData, processingContext, docContext) {
399
605
  const removedSelectors = [];
400
606
  const { ancestorsSelectors, scopeStack } = processingContext;
401
607
  for (let selector = ruleData.prelude.children.head, selectorIndex = 0; selector; selector = selector.next, selectorIndex++) {
402
- const {
403
- startsWithCombinator,
404
- hasUnqueryableSelector,
405
- hasNestedUnqueryablePseudoClass
406
- } = analyzeSelector(selector.data);
608
+ const analysis = analyzeSelector(selector.data, processingContext.hasDefaultNamespace);
609
+ const { startsWithCombinator } = analysis;
610
+ const hasUnqueryableSelector = analysis.hasUnqueryableSelector || Boolean(processingContext.hasUnqueryableSelector);
611
+ const hasNestedUnqueryablePseudoClass = analysis.hasNestedUnqueryablePseudoClass || Boolean(processingContext.hasNestedUnqueryablePseudoClass);
407
612
  if (hasUnqueryableSelector) {
408
613
  ruleData.hasUnqueryableSelector = true;
409
614
  }
410
- registerSelector(selector, ruleData, processingContext, docContext);
411
- if (!startsWithCombinator || !ancestorsSelectors || !ancestorsSelectors.length) {
412
- const relativeToScope = startsWithCombinator && Boolean(scopeStack && scopeStack.length);
413
- const matchedElements = matchElements(selector, ancestorsSelectors, scopeStack, docContext, relativeToScope);
414
- if (matchedElements.length) {
415
- if (!hasUnqueryableSelector) {
416
- updateMatchingSelectors(matchedElements, selector, docContext);
417
- }
418
- } else if (!hasNestedUnqueryablePseudoClass) {
419
- removedSelectors.push(selector);
615
+ if (hasNestedUnqueryablePseudoClass) {
616
+ ruleData.hasNestedUnqueryablePseudoClass = true;
617
+ }
618
+ const nestedSelector = getNestedSelector(selector, ancestorsSelectors, docContext);
619
+ registerSelector(selector, nestedSelector, ruleData, processingContext, docContext);
620
+ const relativeToScope = startsWithCombinator && !(ancestorsSelectors && ancestorsSelectors.length) && Boolean(scopeStack && scopeStack.length);
621
+ const matchedElements = matchElements(selector, nestedSelector, scopeStack, docContext, relativeToScope);
622
+ if (hasNestedUnqueryablePseudoClass) {
623
+ registerIndeterminateRevertLayer(ruleData, docContext);
624
+ }
625
+ if (matchedElements.length) {
626
+ if (hasUnqueryableSelector) {
627
+ registerRevertLayerElements(ruleData, matchedElements, docContext);
628
+ } else {
629
+ updateMatchingSelectors(matchedElements, selector, docContext);
420
630
  }
631
+ } else if (!hasNestedUnqueryablePseudoClass) {
632
+ removedSelectors.push(selector);
421
633
  }
422
634
  }
423
635
  return removedSelectors;
424
636
  }
425
637
 
426
- function analyzeSelector(selector) {
427
- let hasUnqueryableSelector = false;
638
+ function getRemovableSelectors(ruleData, removedSelectors, docContext) {
639
+ if (!removedSelectors.length || removedSelectors.length === ruleData.prelude.children.size || !hasNestedRules(ruleData)) {
640
+ return removedSelectors;
641
+ }
642
+ const removedSet = new Set(removedSelectors);
643
+ let keptSpecificity = { a: 0, b: 0, c: 0 };
644
+ for (let selector = ruleData.prelude.children.head; selector; selector = selector.next) {
645
+ if (!removedSet.has(selector)) {
646
+ const { specificity } = docContext.selectorData.get(selector);
647
+ if (compareSpecificities(specificity, keptSpecificity) > 0) {
648
+ keptSpecificity = specificity;
649
+ }
650
+ }
651
+ }
652
+ return removedSelectors.filter(selector => compareSpecificities(docContext.selectorData.get(selector).specificity, keptSpecificity) <= 0);
653
+ }
654
+
655
+ function hasNestedRules(ruleData) {
656
+ if (hasChildNodes(ruleData.block)) {
657
+ for (let child = ruleData.block.children.head; child; child = child.next) {
658
+ if (child.data.type !== DECLARATION_TYPE) {
659
+ return true;
660
+ }
661
+ }
662
+ }
663
+ return false;
664
+ }
665
+
666
+ function compareSpecificities(specificityA, specificityB) {
667
+ return (specificityA.a - specificityB.a) || (specificityA.b - specificityB.b) || (specificityA.c - specificityB.c);
668
+ }
669
+
670
+ function analyzeSelector(selector, defaultNamespace) {
671
+ let hasUnqueryableSelector = Boolean(defaultNamespace);
428
672
  let hasNestedUnqueryablePseudoClass = false;
429
673
  let startsWithCombinator = false;
430
674
  let functionalPseudoClassDepth = 0;
@@ -436,7 +680,7 @@ function analyzeSelector(selector) {
436
680
  hasNestedUnqueryablePseudoClass = true;
437
681
  }
438
682
  } else if (node.type === PSEUDO_CLASS_SELECTOR_TYPE) {
439
- if (PSEUDO_ELEMENT_SYNONYMS.has(node.name) || matchUnqueryablePseudoClass(node)) {
683
+ if (PSEUDO_ELEMENT_SYNONYMS.has(decodeName(node.name)) || matchUnqueryablePseudoClass(node)) {
440
684
  hasUnqueryableSelector = true;
441
685
  if (functionalPseudoClassDepth) {
442
686
  hasNestedUnqueryablePseudoClass = true;
@@ -450,6 +694,11 @@ function analyzeSelector(selector) {
450
694
  if (functionalPseudoClassDepth) {
451
695
  hasNestedUnqueryablePseudoClass = true;
452
696
  }
697
+ } else if (node.type === TYPE_SELECTOR_TYPE && typeof node.name === "string" && node.name.includes(NAMESPACE_SEPARATOR)) {
698
+ hasUnqueryableSelector = true;
699
+ if (functionalPseudoClassDepth) {
700
+ hasNestedUnqueryablePseudoClass = true;
701
+ }
453
702
  }
454
703
  },
455
704
  leave(node) {
@@ -516,28 +765,31 @@ function updateMatchingSelectors(matchedElements, selector, docContext) {
516
765
  }
517
766
 
518
767
  function processNestedRules(ruleData, stylesheets, processingContext, docContext) {
519
- expandRawCssRules(ruleData);
520
- const newProcessingContext = { ...processingContext, ancestorsSelectors: [...processingContext.ancestorsSelectors, ruleData.prelude] };
768
+ const newProcessingContext = {
769
+ ...processingContext,
770
+ ancestorsSelectors: [...processingContext.ancestorsSelectors, ruleData.prelude],
771
+ hasUnqueryableSelector: Boolean(ruleData.hasUnqueryableSelector),
772
+ hasNestedUnqueryablePseudoClass: Boolean(ruleData.hasNestedUnqueryablePseudoClass)
773
+ };
521
774
  minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
522
775
  }
523
776
 
524
- function registerSelector(selector, ruleData, processingContext, docContext) {
777
+ function registerSelector(selector, nestedSelector, ruleData, processingContext, docContext) {
525
778
  const {
526
- ancestorsSelectors,
527
779
  layerStack,
528
780
  scopeStack,
529
781
  conditionalStack
530
782
  } = processingContext;
531
783
  docContext.selectorData.set(selector, {
532
- specificity: computeMaxSpecificity(selector.data, ancestorsSelectors, scopeStack),
784
+ specificity: computeMaxSpecificity(nestedSelector === null ? selector.data : nestedSelector.data),
533
785
  rule: ruleData,
534
786
  layerStack,
787
+ scopeStack,
535
788
  conditionalStack
536
789
  });
537
790
  }
538
791
 
539
792
  function computeCascadedStylesForElement(element, winningDeclarations, docContext) {
540
- const cascadedStyles = new Map();
541
793
  const allDeclarations = collectDeclarationItemsForElement(element, docContext);
542
794
  const contextGroups = new Map();
543
795
  allDeclarations.forEach(declarationData => {
@@ -549,18 +801,109 @@ function computeCascadedStylesForElement(element, winningDeclarations, docContex
549
801
  }
550
802
  contextGroups.get(contextKey).push(declarationData);
551
803
  });
804
+ let hasWinningRevertLayer = false;
552
805
  contextGroups.forEach(declarations => {
553
806
  declarations.sort((declarationA, declarationB) => compareDeclarations(declarationA, declarationB, docContext));
807
+ const propertyDeclarations = new Map();
554
808
  declarations.forEach(declarationData => {
555
- const { selector, declaration } = declarationData;
556
- const conditionalStack = getConditionalStackForSelector(selector, docContext);
557
- cascadedStyles.set(declaration.data.property + DECLARATION_KEY_SEPARATOR + createContextKey(conditionalStack), {
558
- selector,
559
- declaration
560
- });
809
+ const { property } = declarationData.declaration.data;
810
+ if (!propertyDeclarations.has(property)) {
811
+ propertyDeclarations.set(property, []);
812
+ }
813
+ propertyDeclarations.get(property).push(declarationData);
561
814
  });
815
+ propertyDeclarations.forEach(candidates => {
816
+ if (hasUncertainLayerOrder(candidates, docContext)) {
817
+ candidates.forEach(candidate => winningDeclarations.add(candidate.declaration));
818
+ return;
819
+ }
820
+ for (let indexCandidate = candidates.length - 1; indexCandidate >= 0; indexCandidate--) {
821
+ const { declaration, validity } = candidates[indexCandidate];
822
+ winningDeclarations.add(declaration);
823
+ if (validity === VALIDITY_VALID) {
824
+ hasWinningRevertLayer ||= hasRevertLayerKeyword(declaration, docContext);
825
+ break;
826
+ }
827
+ }
828
+ });
829
+ });
830
+ if (hasWinningRevertLayer || docContext.revertLayerElements.has(element)) {
831
+ allDeclarations.forEach(({ declaration }) => winningDeclarations.add(declaration));
832
+ }
833
+ }
834
+
835
+ function hasUncertainLayerOrder(candidates, docContext) {
836
+ const layerStacks = [new Map(), new Map()];
837
+ candidates.forEach(({ declaration, selector }) => {
838
+ if (selector) {
839
+ const { layerStack } = docContext.selectorData.get(selector);
840
+ layerStacks[declaration.data.important ? CSS_IMPORTANCE_IMPORTANT : CSS_IMPORTANCE_NOT_IMPORTANT].set(getFullLayerName(layerStack), layerStack);
841
+ }
842
+ });
843
+ return layerStacks.some(importanceStacks => {
844
+ const stacks = Array.from(importanceStacks.values());
845
+ for (let indexA = 0; indexA < stacks.length; indexA++) {
846
+ for (let indexB = indexA + 1; indexB < stacks.length; indexB++) {
847
+ if (compareLayers(stacks[indexA], stacks[indexB], docContext) === UNCERTAIN_LAYER_ORDER) {
848
+ return true;
849
+ }
850
+ }
851
+ }
852
+ return false;
562
853
  });
563
- cascadedStyles.forEach(({ declaration }) => winningDeclarations.add(declaration));
854
+ }
855
+
856
+ function registerRevertLayerElements(ruleData, matchedElements, docContext) {
857
+ if (hasRevertLayerDeclaration(ruleData, docContext)) {
858
+ matchedElements.forEach(element => docContext.revertLayerElements.add(element));
859
+ }
860
+ }
861
+
862
+ function registerIndeterminateRevertLayer(ruleData, docContext) {
863
+ if (hasRevertLayerDeclaration(ruleData, docContext)) {
864
+ docContext.hasIndeterminateRevertLayer = true;
865
+ }
866
+ }
867
+
868
+ function hasRevertLayerDeclaration(ruleData, docContext) {
869
+ let found = docContext.revertLayerRules.get(ruleData);
870
+ if (found === undefined) {
871
+ found = findRevertLayerDeclaration(ruleData, docContext);
872
+ docContext.revertLayerRules.set(ruleData, found);
873
+ }
874
+ return found;
875
+ }
876
+
877
+ function findRevertLayerDeclaration(ruleData, docContext) {
878
+ if (!hasChildNodes(ruleData.block)) {
879
+ return false;
880
+ }
881
+ for (let child = ruleData.block.children.head; child; child = child.next) {
882
+ if (child.data.type === DECLARATION_TYPE && hasRevertLayerKeyword(child, docContext)) {
883
+ return true;
884
+ }
885
+ }
886
+ return false;
887
+ }
888
+
889
+ function hasRevertLayerKeyword(declaration, docContext) {
890
+ let found = docContext.revertLayerDeclarations.get(declaration);
891
+ if (found === undefined) {
892
+ found = findRevertLayerKeyword(declaration);
893
+ docContext.revertLayerDeclarations.set(declaration, found);
894
+ }
895
+ return found;
896
+ }
897
+
898
+ function findRevertLayerKeyword(declaration) {
899
+ const { value } = declaration.data;
900
+ if (!value) {
901
+ return false;
902
+ }
903
+ if (value.type === RAW_TYPE) {
904
+ return REVERT_LAYER_TEST.test(decodeIdentifier(value.value));
905
+ }
906
+ return Boolean(cssTree.find(value, node => node.type === IDENTIFIER_TYPE && decodeIdentifier(node.name).toLowerCase() === REVERT_LAYER_KEYWORD));
564
907
  }
565
908
 
566
909
  function createContextKey(conditionalStack) {
@@ -571,13 +914,15 @@ function collectDeclarationItemsForElement(element, docContext) {
571
914
  const matchingSelectors = docContext.matchingSelectors.get(element);
572
915
  const allDeclarations = [];
573
916
  matchingSelectors.forEach(selector => {
574
- const cssRule = docContext.selectorData.get(selector).rule;
917
+ const selectorData = docContext.selectorData.get(selector);
918
+ const cssRule = selectorData.rule;
575
919
  if (hasChildNodes(cssRule.block)) {
920
+ const proximity = getScopeProximity(element, selector, docContext);
576
921
  const declarations = cssRule.block.children;
577
922
  for (let declaration = declarations.head; declaration; declaration = declaration.next) {
578
- const { type, value } = declaration.data;
923
+ const { type, value, order } = declaration.data;
579
924
  if (type === DECLARATION_TYPE && value) {
580
- addDeclaration(declaration, docContext.selectorData.get(selector).specificity, false, selector);
925
+ addDeclaration(declaration, selectorData.specificity, false, selector, order === undefined ? cssRule.order : order, proximity);
581
926
  }
582
927
  }
583
928
  }
@@ -588,36 +933,41 @@ function collectDeclarationItemsForElement(element, docContext) {
588
933
  }
589
934
  return allDeclarations;
590
935
 
591
- function addDeclaration(declaration, specificity, isInline, selector) {
936
+ function addDeclaration(declaration, specificity, isInline, selector, order, proximity) {
592
937
  const { property, value } = declaration.data;
593
- const isRawValue = value.type === RAW_TYPE;
594
- const hasValueChildNodes = hasChildNodes(value) || value.type === RAW_TYPE;
595
- let isInvalidValue;
596
- if (value.type === VALUE_TYPE &&
597
- hasValueChildNodes &&
598
- value.children.size == 1) {
599
- if (value.children.head.data.name) {
600
- const name = value.children.head.data.name;
601
- isInvalidValue = isUnsupportedVendorValue(property, name) || INVALID_CSS_ESCAPE_TEST.test(name);
602
- } if (!property.startsWith(VENDOR_PREFIX) && value.children.head.data.value) {
603
- try {
604
- isInvalidValue = isUnsupportedPropertyValue(property, value);
605
- } catch {
606
- // ignored
607
- }
938
+ if (value.type === VALUE_TYPE && hasChildNodes(value)) {
939
+ const validity = getCachedValueValidity(property, value, docContext);
940
+ if (validity !== VALIDITY_INVALID) {
941
+ allDeclarations.push({
942
+ declaration,
943
+ selector,
944
+ specificity,
945
+ isInline,
946
+ order,
947
+ proximity,
948
+ validity
949
+ });
608
950
  }
609
951
  }
610
- if (hasValueChildNodes && !isRawValue && !isInvalidValue) {
611
- allDeclarations.push({
612
- declaration,
613
- selector,
614
- specificity,
615
- isInline
616
- });
617
- }
618
952
  }
619
953
  }
620
954
 
955
+ function getCachedValueValidity(property, value, docContext) {
956
+ if (property.startsWith(CUSTOM_PROPERTY_PREFIX)) {
957
+ return VALIDITY_VALID;
958
+ }
959
+ if (!docContext.valueValidities.has(value)) {
960
+ docContext.valueValidities.set(value, getValueValidity(property, value));
961
+ }
962
+ return docContext.valueValidities.get(value);
963
+ }
964
+
965
+ function getScopeProximity(element, selector, docContext) {
966
+ const proximities = docContext.scopeProximities.get(docContext.scopeProximityKeys.get(selector));
967
+ const proximity = proximities && proximities.get(element);
968
+ return proximity === undefined ? UNSCOPED_PROXIMITY : proximity;
969
+ }
970
+
621
971
  function getConditionalStackForSelector(selector, docContext) {
622
972
  let conditionalStack = [];
623
973
  if (selector) {
@@ -629,20 +979,27 @@ function getConditionalStackForSelector(selector, docContext) {
629
979
  return conditionalStack;
630
980
  }
631
981
 
632
- function matchElements(selector, ancestorsSelectors, scopeStack, docContext, relativeToScope) {
633
- let selectorText = createSelectorText(selector, ancestorsSelectors, docContext);
982
+ function matchElements(selector, nestedSelector, scopeStack, docContext, relativeToScope) {
983
+ let selectorText = sanitizeSelector(nestedSelector === null ? selector : nestedSelector, docContext);
634
984
  if (relativeToScope) {
635
985
  selectorText = SCOPE_PSEUDO_CLASS + selectorText;
986
+ } else if (scopeStack && scopeStack.length) {
987
+ selectorText = getScopedSelectorText(selectorText, docContext);
636
988
  }
637
989
  const cacheKey = createScopeCacheKey(selectorText, scopeStack);
990
+ if (scopeStack && scopeStack.length) {
991
+ docContext.scopeProximityKeys.set(selector, cacheKey);
992
+ }
638
993
  const cachedNodes = docContext.matchedSelectors.get(cacheKey);
639
994
  if (cachedNodes) {
640
995
  return cachedNodes;
641
996
  }
642
997
  let nodes;
643
998
  if (scopeStack && scopeStack.length) {
644
- nodes = matchElementsInScope(selectorText, scopeStack);
999
+ const proximities = new Map();
1000
+ nodes = matchElementsInScope(selectorText, scopeStack, proximities);
645
1001
  nodes = filterElementsByScopes(nodes, scopeStack);
1002
+ docContext.scopeProximities.set(cacheKey, proximities);
646
1003
  } else {
647
1004
  nodes = querySelectorAll(docContext.doc, selectorText);
648
1005
  }
@@ -650,6 +1007,26 @@ function matchElements(selector, ancestorsSelectors, scopeStack, docContext, rel
650
1007
  return nodes;
651
1008
  }
652
1009
 
1010
+ function getScopedSelectorText(selectorText, docContext) {
1011
+ if (!docContext.scopedSelectorTexts.has(selectorText)) {
1012
+ let scopedSelectorText = selectorText;
1013
+ try {
1014
+ const selectorList = parseCss(selectorText, SELECTOR_LIST_CONTEXT);
1015
+ scopedSelectorText = selectorList.children.toArray().map(selector => {
1016
+ const hasScopePseudoClass = cssTree.find(selector, node => node.type === PSEUDO_CLASS_SELECTOR_TYPE && decodeName(node.name) === SCOPE_PSEUDO_CLASS_NAME);
1017
+ return hasScopePseudoClass ? cssTree.generate(selector) : SCOPE_PSEUDO_CLASS + DESCENDANT_COMBINATOR + cssTree.generate(selector);
1018
+ }).join(PRELUDE_SEPARATOR);
1019
+ } catch {
1020
+ if (DEBUG) {
1021
+ // eslint-disable-next-line no-console
1022
+ console.warn(PARSE_CSS_ERROR_MESSAGE, selectorText);
1023
+ }
1024
+ }
1025
+ docContext.scopedSelectorTexts.set(selectorText, scopedSelectorText);
1026
+ }
1027
+ return docContext.scopedSelectorTexts.get(selectorText);
1028
+ }
1029
+
653
1030
  function createScopeCacheKey(selectorText, scopeStack) {
654
1031
  if (!scopeStack || !scopeStack.length) {
655
1032
  return selectorText;
@@ -658,7 +1035,7 @@ function createScopeCacheKey(selectorText, scopeStack) {
658
1035
  return `${selectorText}${CONTEXT_KEY_SEPARATOR}${signature}`;
659
1036
  }
660
1037
 
661
- function matchElementsInScope(selectorText, scopeStack) {
1038
+ function matchElementsInScope(selectorText, scopeStack, proximities) {
662
1039
  const currentScope = scopeStack[scopeStack.length - 1];
663
1040
  const roots = Array.from(currentScope.rootElements);
664
1041
  if (!roots.length) {
@@ -666,7 +1043,19 @@ function matchElementsInScope(selectorText, scopeStack) {
666
1043
  }
667
1044
  const matchedNodes = new Set();
668
1045
  roots.forEach(root => {
669
- matchSelectorWithinRoot(root, selectorText).forEach(node => matchedNodes.add(node));
1046
+ const stopElements = currentScope.stopElements && currentScope.stopElements.get(root);
1047
+ matchSelectorWithinRoot(root, selectorText).forEach(node => {
1048
+ const proximity = getRootProximity(node, root, stopElements);
1049
+ if (proximity !== UNSCOPED_PROXIMITY) {
1050
+ matchedNodes.add(node);
1051
+ if (proximities) {
1052
+ const knownProximity = proximities.get(node);
1053
+ if (knownProximity === undefined || proximity < knownProximity) {
1054
+ proximities.set(node, proximity);
1055
+ }
1056
+ }
1057
+ }
1058
+ });
670
1059
  });
671
1060
  return Array.from(matchedNodes);
672
1061
  }
@@ -674,7 +1063,7 @@ function matchElementsInScope(selectorText, scopeStack) {
674
1063
  function matchSelectorWithinRoot(root, selectorText) {
675
1064
  const matchedNodes = new Set();
676
1065
  if (!root || root.nodeType !== 1) {
677
- return matchedNodes;
1066
+ return [];
678
1067
  }
679
1068
  if (matches(root, selectorText)) {
680
1069
  matchedNodes.add(root);
@@ -736,17 +1125,10 @@ function getTraversalRoots(root) {
736
1125
  return [];
737
1126
  }
738
1127
  if (root.nodeType === 9 || root.nodeType === 11) {
739
- const roots = [];
740
1128
  if (root.documentElement) {
741
- roots.push(root.documentElement);
1129
+ return [root.documentElement];
742
1130
  }
743
- if (root.body && (!roots.length || root.body !== roots[0])) {
744
- roots.push(root.body);
745
- }
746
- if (!roots.length && root.children) {
747
- roots.push(...Array.from(root.children).filter(node => node.nodeType === 1));
748
- }
749
- return roots;
1131
+ return root.children ? Array.from(root.children).filter(node => node.nodeType === 1) : [];
750
1132
  }
751
1133
  return [root];
752
1134
  }
@@ -771,30 +1153,81 @@ function isElementWithinScopes(element, scopeStack) {
771
1153
  }
772
1154
 
773
1155
  function isElementWithinScope(element, scopeContext) {
774
- let current = element;
775
- while (current && current.nodeType === 1) {
776
- if (scopeContext.stopElements && scopeContext.stopElements.has(current)) {
777
- return false;
778
- }
779
- if (scopeContext.rootElements.has(current)) {
1156
+ const { rootElements, stopElements } = scopeContext;
1157
+ for (let current = element; current && current.nodeType === 1; current = current.parentElement) {
1158
+ if (rootElements.has(current) && getRootProximity(element, current, stopElements && stopElements.get(current)) !== UNSCOPED_PROXIMITY) {
780
1159
  return true;
781
1160
  }
782
- current = current.parentElement;
783
1161
  }
784
1162
  return false;
785
1163
  }
786
1164
 
787
- function createSelectorText(selector, ancestorsSelectors, docContext) {
788
- let selectorText;
789
- if (ancestorsSelectors && ancestorsSelectors.length) {
790
- selectorText = combineSelectorWithAncestors(selector.data, ancestorsSelectors, docContext);
791
- const combinedAst = parseCss(selectorText, SELECTOR_LIST_CONTEXT);
792
- selectorText = sanitizeSelector({ data: combinedAst }, ancestorsSelectors, docContext);
1165
+ function getRootProximity(element, root, stopElements) {
1166
+ const ancestors = [];
1167
+ for (let current = element; current && current.nodeType === 1; current = current.parentElement) {
1168
+ ancestors.push(current);
1169
+ if (current === root) {
1170
+ return isPathBlocked(ancestors, stopElements) ? UNSCOPED_PROXIMITY : ancestors.length - 1;
1171
+ }
1172
+ }
1173
+ return UNSCOPED_PROXIMITY;
1174
+ }
1175
+
1176
+ function isPathBlocked(ancestors, stopElements) {
1177
+ return Boolean(stopElements) && ancestors.some(ancestor => stopElements.has(ancestor));
1178
+ }
1179
+
1180
+ function getNestedSelector(selector, ancestorsSelectors, docContext) {
1181
+ if (!ancestorsSelectors || !ancestorsSelectors.length) {
1182
+ return null;
793
1183
  }
794
- if (!selectorText) {
795
- selectorText = sanitizeSelector(selector, ancestorsSelectors, docContext);
1184
+ const nestedSelectorText = nestSelectorText(getSelectorText(selector.data, docContext), getNestingParentText(ancestorsSelectors, docContext), docContext);
1185
+ return { data: parseCss(nestedSelectorText, SELECTOR_LIST_CONTEXT) };
1186
+ }
1187
+
1188
+ function getNestingParentText(ancestorsSelectors, docContext) {
1189
+ const prelude = ancestorsSelectors[ancestorsSelectors.length - 1];
1190
+ if (!docContext.nestingParentTexts.has(prelude)) {
1191
+ const grandParentText = ancestorsSelectors.length > 1 ? getNestingParentText(ancestorsSelectors.slice(0, -1), docContext) : null;
1192
+ const selectorTexts = [];
1193
+ for (let parentSelector = prelude.children.head; parentSelector; parentSelector = parentSelector.next) {
1194
+ if (!hasPseudoElement(parentSelector.data)) {
1195
+ const parentText = getSelectorText(parentSelector.data, docContext);
1196
+ selectorTexts.push(grandParentText === null ? parentText : nestSelectorText(parentText, grandParentText, docContext));
1197
+ }
1198
+ }
1199
+ docContext.nestingParentTexts.set(prelude, selectorTexts.length ? IS_PSEUDO_CLASS_PREFIX + selectorTexts.join(PRELUDE_SEPARATOR) + IS_PSEUDO_CLASS_SUFFIX : NO_ELEMENT_SELECTOR);
796
1200
  }
797
- return selectorText;
1201
+ return docContext.nestingParentTexts.get(prelude);
1202
+ }
1203
+
1204
+ function hasPseudoElement(selector) {
1205
+ return Boolean(cssTree.find(selector, node => node.type === PSEUDO_ELEMENT_SELECTOR_TYPE || (node.type === PSEUDO_CLASS_SELECTOR_TYPE && PSEUDO_ELEMENT_SYNONYMS.has(decodeName(node.name)))));
1206
+ }
1207
+
1208
+ function nestSelectorText(selectorText, parentText, docContext) {
1209
+ const selector = parseCss(selectorText);
1210
+ let hasNestingSelector = false;
1211
+ cssTree.walk(selector, {
1212
+ visit: NESTING_SELECTOR_TYPE,
1213
+ enter(_node, item, list) {
1214
+ hasNestingSelector = true;
1215
+ list.insertData(getNestingParentNode(parentText, docContext), item);
1216
+ list.remove(item);
1217
+ }
1218
+ });
1219
+ if (hasNestingSelector) {
1220
+ return cssTree.generate(selector);
1221
+ }
1222
+ const startsWithCombinator = selector.children.head.data.type === COMBINATOR_NAME;
1223
+ return parentText + (startsWithCombinator ? EMPTY_STRING : DESCENDANT_COMBINATOR) + selectorText;
1224
+ }
1225
+
1226
+ function getNestingParentNode(parentText, docContext) {
1227
+ if (!docContext.nestingParentNodes.has(parentText)) {
1228
+ docContext.nestingParentNodes.set(parentText, parseCss(parentText).children.head.data);
1229
+ }
1230
+ return cssTree.clone(docContext.nestingParentNodes.get(parentText));
798
1231
  }
799
1232
 
800
1233
  function compareDeclarations(declarationA, declarationB, docContext) {
@@ -809,7 +1242,7 @@ function compareDeclarations(declarationA, declarationB, docContext) {
809
1242
  const selectorDataB = declarationB.selector ? docContext.selectorData.get(declarationB.selector) : null;
810
1243
  if (selectorDataA && selectorDataB) {
811
1244
  const layerComparison = compareLayers(selectorDataA.layerStack, selectorDataB.layerStack, docContext);
812
- if (layerComparison !== 0) {
1245
+ if (layerComparison) {
813
1246
  return importantA ? -layerComparison : layerComparison;
814
1247
  }
815
1248
  const specificityA = declarationA.specificity;
@@ -823,8 +1256,11 @@ function compareDeclarations(declarationA, declarationB, docContext) {
823
1256
  if (specificityA.c !== specificityB.c) {
824
1257
  return specificityA.c - specificityB.c;
825
1258
  }
826
- if (selectorDataA.rule.order !== selectorDataB.rule.order) {
827
- return selectorDataA.rule.order - selectorDataB.rule.order;
1259
+ if (declarationA.proximity !== declarationB.proximity) {
1260
+ return declarationB.proximity - declarationA.proximity;
1261
+ }
1262
+ if (declarationA.order !== declarationB.order) {
1263
+ return declarationA.order - declarationB.order;
828
1264
  }
829
1265
  return 0;
830
1266
  } else {
@@ -860,27 +1296,32 @@ function compareLayers(layersA, layersB, docContext) {
860
1296
  if (fullLayerNameA === fullLayerNameB) {
861
1297
  return 0;
862
1298
  }
1299
+ const comparisonKey = fullLayerNameA + CONTEXT_KEY_SEPARATOR + fullLayerNameB;
1300
+ if (!docContext.layerComparisons.has(comparisonKey)) {
1301
+ docContext.layerComparisons.set(comparisonKey, compareLayerNames(layersA, layersB, docContext));
1302
+ }
1303
+ return docContext.layerComparisons.get(comparisonKey);
1304
+ }
1305
+
1306
+ function compareLayerNames(layersA, layersB, docContext) {
863
1307
  const minLength = Math.min(layersA.length, layersB.length);
864
- const effectiveMap = docContext.layerOrder;
865
1308
  for (let indexLayer = 0; indexLayer < minLength; indexLayer++) {
866
1309
  if (layersA[indexLayer] !== layersB[indexLayer]) {
867
- const partialLayerA = getFullLayerName(layersA.slice(0, indexLayer + 1));
868
- const partialLayerB = getFullLayerName(layersB.slice(0, indexLayer + 1));
869
- const orderA = effectiveMap.get(partialLayerA);
870
- const orderB = effectiveMap.get(partialLayerB);
871
- if (orderA !== undefined && orderB !== undefined) {
872
- return orderA - orderB;
1310
+ const positionA = docContext.layerOrder.get(getFullLayerName(layersA.slice(0, indexLayer + 1)));
1311
+ const positionB = docContext.layerOrder.get(getFullLayerName(layersB.slice(0, indexLayer + 1)));
1312
+ if (!positionA || !positionB) {
1313
+ return UNCERTAIN_LAYER_ORDER;
873
1314
  }
874
- if (orderA !== undefined) {
1315
+ if (positionA.firstCertain < positionB.first) {
875
1316
  return -1;
876
1317
  }
877
- if (orderB !== undefined) {
1318
+ if (positionB.firstCertain < positionA.first) {
878
1319
  return 1;
879
1320
  }
880
- return 0;
1321
+ return UNCERTAIN_LAYER_ORDER;
881
1322
  }
882
1323
  }
883
- return layersA.length - layersB.length;
1324
+ return layersB.length - layersA.length;
884
1325
  }
885
1326
 
886
1327
  function removeStylesheetEmptyRules(cssRules, docContext) {
@@ -890,15 +1331,22 @@ function removeStylesheetEmptyRules(cssRules, docContext) {
890
1331
  if (ruleData.type === RULE_TYPE) {
891
1332
  if (hasChildNodes(ruleData.block)) {
892
1333
  removeStylesheetEmptyRules(ruleData.block.children, docContext);
893
- } else {
1334
+ }
1335
+ if (!hasChildNodes(ruleData.block)) {
894
1336
  docContext.stats.discarded++;
895
1337
  removedRules.add(cssRule);
896
1338
  }
897
- } else if (ruleData.type === AT_RULE_TYPE && ruleData.block && ruleData.name !== FONT_FACE_NAME && ruleData.name !== KEYFRAMES_NAME) {
1339
+ } else if (isImportRule(ruleData)) {
1340
+ removeStylesheetEmptyRules(ruleData.prelude.children.head.data.importedChildren, docContext);
1341
+ } else if (ruleData.type === AT_RULE_TYPE && ruleData.block && decodeName(ruleData.name) !== FONT_FACE_NAME && decodeName(ruleData.name) !== KEYFRAMES_NAME) {
898
1342
  removeStylesheetEmptyRules(ruleData.block.children, docContext);
899
1343
  if (!hasChildNodes(ruleData.block)) {
900
- docContext.stats.discarded++;
901
- removedRules.add(cssRule);
1344
+ if (decodeName(ruleData.name) === LAYER_NAME) {
1345
+ removeEmptyLayerRule(ruleData, cssRule, removedRules, docContext);
1346
+ } else {
1347
+ docContext.stats.discarded++;
1348
+ removedRules.add(cssRule);
1349
+ }
902
1350
  }
903
1351
  }
904
1352
  }
@@ -951,22 +1399,18 @@ function expandRawCssRules(ruleData) {
951
1399
  const ruleChildren = [];
952
1400
  if (hasChildNodes(ruleData.block)) {
953
1401
  for (let cssRuleNode = ruleData.block.children.head; cssRuleNode; cssRuleNode = cssRuleNode.next) {
954
- if (cssRuleNode.data.type === RAW_TYPE) {
955
- if (cssRuleNode.data.value.indexOf(BLOCK_OPEN) !== -1 &&
956
- cssRuleNode.data.value.indexOf(BLOCK_OPEN) < cssRuleNode.data.value.indexOf(BLOCK_CLOSE)) {
957
- try {
958
- const stylesheet = parseCss(cssRuleNode.data.value, STYLESHEET_CONTEXT);
959
- for (let stylesheetChild = stylesheet.children.head; stylesheetChild; stylesheetChild = stylesheetChild.next) {
960
- ruleChildren.push(stylesheetChild);
961
- }
962
- } catch (error) {
963
- if (DEBUG) {
964
- // eslint-disable-next-line no-console
965
- console.warn(PARSE_CSS_ERROR_MESSAGE, cssRuleNode.data.value, error);
966
- }
1402
+ const rawRuleText = getRawRuleText(cssRuleNode.data);
1403
+ if (rawRuleText !== null) {
1404
+ try {
1405
+ const stylesheet = parseCss(rawRuleText, STYLESHEET_CONTEXT);
1406
+ for (let stylesheetChild = stylesheet.children.head; stylesheetChild; stylesheetChild = stylesheetChild.next) {
1407
+ ruleChildren.push(stylesheetChild);
1408
+ }
1409
+ } catch (error) {
1410
+ if (DEBUG) {
1411
+ // eslint-disable-next-line no-console
1412
+ console.warn(PARSE_CSS_ERROR_MESSAGE, rawRuleText, error);
967
1413
  }
968
- } else {
969
- ruleChildren.push(cssRuleNode);
970
1414
  }
971
1415
  } else {
972
1416
  ruleChildren.push(cssRuleNode);
@@ -977,62 +1421,19 @@ function expandRawCssRules(ruleData) {
977
1421
  ruleChildren.forEach(ruleChild => ruleData.block.children.appendData(ruleChild.data));
978
1422
  }
979
1423
 
980
- function combineSelectorWithAncestors(selector, ancestorsSelectors, docContext) {
981
- const selectorText = getSelectorText(selector, docContext);
982
- if (!ancestorsSelectors || !ancestorsSelectors.length) {
983
- return selectorText;
984
- } else {
985
- let contexts = [EMPTY_STRING];
986
- ancestorsSelectors.forEach(selectorList => {
987
- if (hasChildNodes(selectorList)) {
988
- const parentSelectors = selectorList.children.toArray();
989
- const nextContexts = [];
990
- contexts.forEach(context => parentSelectors.forEach(parentSelector => {
991
- const parentText = getSelectorText(parentSelector, docContext);
992
- const combined = context ? combineSelectors(context, parentText) : parentText;
993
- if (!nextContexts.includes(combined)) {
994
- nextContexts.push(combined);
995
- }
996
- }));
997
- if (nextContexts.length) {
998
- contexts = nextContexts;
999
- }
1000
- }
1001
- });
1002
- const expandedSelectors = new Set();
1003
- contexts.forEach(context => {
1004
- const result = context ? combineSelectors(context, selectorText) : selectorText;
1005
- expandedSelectors.add(result);
1006
- });
1007
- return Array.from(expandedSelectors).join(PRELUDE_SEPARATOR);
1424
+ function getRawRuleText(node) {
1425
+ if (node.type === RAW_TYPE && holdsBlock(node.value)) {
1426
+ return node.value;
1427
+ }
1428
+ if (node.type === DECLARATION_TYPE && !node.property.startsWith(CUSTOM_PROPERTY_PREFIX) && node.value.type === RAW_TYPE && holdsBlock(node.value.value) && node.value.value.trimStart().indexOf(BLOCK_OPEN) > 0) {
1429
+ return node.property + DECLARATION_SEPARATOR + node.value.value;
1008
1430
  }
1431
+ return null;
1009
1432
  }
1010
1433
 
1011
- function combineSelectors(parentSelectorText, childSelectorText) {
1012
- const childSelector = parseCss(childSelectorText || NESTING_SELECTOR);
1013
- const parentSelector = parentSelectorText ? parseCss(parentSelectorText) : null;
1014
- let hasNesting = false;
1015
- cssTree.walk(childSelector, {
1016
- visit: NESTING_SELECTOR_TYPE,
1017
- enter(_node, item, list) {
1018
- hasNesting = true;
1019
- if (!parentSelector) {
1020
- list.remove(item);
1021
- return;
1022
- }
1023
- const nodes = parentSelector.children.toArray().map(parent => cssTree.clone(parent));
1024
- nodes.forEach(node => list.insertData(node, item));
1025
- list.remove(item);
1026
- }
1027
- });
1028
- if (hasNesting) {
1029
- return cssTree.generate(childSelector);
1030
- }
1031
- if (!parentSelector) {
1032
- return cssTree.generate(childSelector);
1033
- }
1034
- const combinedSelector = parseCss(`${parentSelectorText} ${childSelectorText}`);
1035
- return cssTree.generate(combinedSelector);
1434
+ function holdsBlock(text) {
1435
+ const blockOpenIndex = text.indexOf(BLOCK_OPEN);
1436
+ return blockOpenIndex !== -1 && blockOpenIndex < text.indexOf(BLOCK_CLOSE);
1036
1437
  }
1037
1438
 
1038
1439
  function hasChildNodes(node) {
@@ -1058,7 +1459,7 @@ function getPreludeText(prelude, docContext) {
1058
1459
  }
1059
1460
 
1060
1461
  function getFullLayerName(layers) {
1061
- return layers.map(layerName => layerName === EMPTY_STRING ? ANONYMOUS_LAYER_PLACEHOLDER : layerName).join(LAYER_NAME_SEPARATOR);
1462
+ return layers.join(LAYER_NAME_KEY_SEPARATOR);
1062
1463
  }
1063
1464
 
1064
1465
  function parseCss(text, context = SELECTOR_CONTEXT) {