single-file-core 1.5.47 → 1.5.49

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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2010-2022 Gildas Lormeau
2
+ * Copyright 2010-2025 Gildas Lormeau
3
3
  * contact : gildas.lormeau <at> gmail.com
4
4
  *
5
5
  * This file is part of SingleFile.
@@ -22,124 +22,949 @@
22
22
  */
23
23
 
24
24
  import * as cssTree from "./../vendor/css-tree.js";
25
+ import { computeMaxSpecificity } from "./css-specificity.js";
26
+ import { parsePrelude } from "./css-scope-prelude-parser.js";
27
+ import { sanitizeSelector } from "./css-selector-sanitizer.js";
25
28
 
26
29
  const DEBUG = false;
27
30
 
31
+ const CANONICAL_PSEUDO_ELEMENT_NAMES = new Set(["after", "before", "first-letter", "first-line", "placeholder", "selection", "part", "marker"]);
32
+ const DYNAMIC_STATE_PSEUDO_CLASSES = new Set(["hover", "focus", "active", "focus-within", "focus-visible", "target", "visited", "link", "target-current"]);
33
+ const CONDITIONAL_AT_RULE_NAMES = new Set(["media", "supports", "container"]);
34
+ const RULE_TYPE = "Rule";
35
+ const AT_RULE_TYPE = "Atrule";
36
+ const NESTING_SELECTOR_TYPE = "NestingSelector";
37
+ const PSEUDO_CLASS_SELECTOR_TYPE = "PseudoClassSelector";
38
+ const DECLARATION_TYPE = "Declaration";
39
+ const RAW_TYPE = "Raw";
40
+ const PSEUDO_ELEMENT_SELECTOR_TYPE = "PseudoElementSelector";
41
+ const LAYER_NAME = "layer";
42
+ const SCOPE_NAME = "scope";
43
+ const IMPORT_NAME = "import";
44
+ const FONT_FACE_NAME = "font-face";
45
+ const KEYFRAMES_NAME = "keyframes";
46
+ const COMBINATOR_NAME = "Combinator";
47
+ const STYLE_ATTRIBUTE_NAME = "style";
48
+ const SELECTOR_LIST_CONTEXT = "selectorList";
49
+ const STYLESHEET_CONTEXT = "stylesheet";
50
+ const SELECTOR_CONTEXT = "selector";
51
+ const DECLARATION_LIST_CONTEXT = "declarationList";
52
+ const PARSE_CSS_ERROR_MESSAGE = "Failed to parse CSS";
53
+ const QSA_ERROR_MESSAGE = "Failed to match selector";
54
+ const ROOT_PSEUDO_CLASS = ":root";
55
+ const PRELUDE_SEPARATOR = ",";
56
+ const NESTING_SELECTOR = "&";
57
+ const VENDOR_PREFIX = "-";
58
+ const CUSTOM_PROPERTY_PREFIX = "--";
59
+ const LAYER_NAME_SEPARATOR = ".";
60
+ const DECLARATION_KEY_SEPARATOR = ":";
61
+ const CONTEXT_KEY_SEPARATOR = "|";
62
+ const BLOCK_OPEN = "{";
63
+ const BLOCK_CLOSE = "}";
64
+ const EMPTY_STRING = "";
65
+ const CSS_IMPORTANCE_NOT_IMPORTANT = 0;
66
+ const CSS_IMPORTANCE_IMPORTANT = 1;
67
+
28
68
  export {
29
69
  process
30
70
  };
31
71
 
32
- function process(stylesheets, styles, mediaAllInfo) {
33
- const stats = { processed: 0, discarded: 0 };
34
- let sheetIndex = 0;
72
+ function process(doc, stylesheets) {
73
+ const docContext = {
74
+ doc,
75
+ stats: { processed: 0, discarded: 0 },
76
+ matchedElements: new Set(),
77
+ matchedSelectors: new Map(),
78
+ matchingSelectors: new Map(),
79
+ layerDeclarationCounter: 0,
80
+ layerDeclarations: [],
81
+ layerOrder: new Map(),
82
+ selectorData: new Map(),
83
+ selectorTexts: new Map(),
84
+ preludeTexts: new Map(),
85
+ scopeRoots: new Map(),
86
+ scopeSpecificities: new Map(),
87
+ rulesCounter: 0,
88
+ };
89
+ collectLayerOrder(stylesheets, docContext);
90
+ buildEffectiveLayerOrder(docContext);
91
+ minifyRules(stylesheets, docContext);
92
+ computeCascade(docContext);
93
+ removeEmptyRules(stylesheets, docContext);
94
+ return docContext.stats;
95
+ }
96
+
97
+ function collectLayerOrder(stylesheets, docContext) {
35
98
  stylesheets.forEach((stylesheetInfo, key) => {
36
99
  if (!stylesheetInfo.scoped && stylesheetInfo.stylesheet && !key.urlNode) {
37
- const cssRules = stylesheetInfo.stylesheet.children;
38
- if (cssRules) {
39
- stats.processed += cssRules.size;
40
- stats.discarded += cssRules.size;
41
- let mediaInfo;
42
- if (stylesheetInfo.mediaText && stylesheetInfo.mediaText != "all") {
43
- mediaInfo = mediaAllInfo.medias.get("style-" + sheetIndex + "-" + stylesheetInfo.mediaText);
44
- } else {
45
- mediaInfo = mediaAllInfo;
100
+ if (hasChildNodes(stylesheetInfo.stylesheet)) {
101
+ collectStylesheetLayerOrder(stylesheetInfo.stylesheet.children, { layerStack: [], conditionalStack: [] }, docContext);
102
+ }
103
+ }
104
+ });
105
+ }
106
+
107
+ function buildEffectiveLayerOrder(docContext) {
108
+ const layerNames = [];
109
+ for (let indexDeclaration = 0; indexDeclaration < docContext.layerDeclarations.length; indexDeclaration++) {
110
+ const declaration = docContext.layerDeclarations[indexDeclaration];
111
+ layerNames.push(declaration.name);
112
+ }
113
+ for (let indexLayerName = 0; indexLayerName < layerNames.length; indexLayerName++) {
114
+ const name = layerNames[indexLayerName];
115
+ if (!docContext.layerOrder.has(name)) {
116
+ docContext.layerOrder.set(name, docContext.layerOrder.size);
117
+ }
118
+ }
119
+ }
120
+
121
+ function minifyRules(stylesheets, docContext) {
122
+ stylesheets.forEach((stylesheetInfo, key) => {
123
+ if (!stylesheetInfo.scoped && stylesheetInfo.stylesheet && !key.urlNode) {
124
+ if (hasChildNodes(stylesheetInfo.stylesheet)) {
125
+ const topConditionalStack = stylesheetInfo.mediaText ? [{ name: "media", prelude: stylesheetInfo.mediaText }] : [];
126
+ if (stylesheetInfo.layerName !== undefined) {
127
+ topConditionalStack.push({ name: "layer", prelude: stylesheetInfo.layerName });
46
128
  }
47
- processRules(cssRules, sheetIndex, mediaInfo);
48
- stats.discarded -= cssRules.size;
129
+ if (stylesheetInfo.supportsCondition !== undefined) {
130
+ topConditionalStack.push({ name: "supports", prelude: stylesheetInfo.supportsCondition });
131
+ }
132
+ minifyStylesheetRules(stylesheetInfo.stylesheet.children, stylesheets, {
133
+ ancestorsSelectors: [],
134
+ layerStack: [],
135
+ conditionalStack: topConditionalStack
136
+ }, docContext);
137
+ }
138
+ }
139
+ });
140
+ }
141
+
142
+ function computeCascade(docContext) {
143
+ const winningDeclarations = new Set();
144
+ docContext.matchedElements.forEach(element => computeCascadedStylesForElement(element, winningDeclarations, docContext));
145
+ removeLosingDeclarations(winningDeclarations, docContext);
146
+ }
147
+
148
+ function removeEmptyRules(stylesheets, docContext) {
149
+ stylesheets.forEach((stylesheetInfo, key) => {
150
+ if (!stylesheetInfo.scoped && stylesheetInfo.stylesheet && !key.urlNode) {
151
+ if (hasChildNodes(stylesheetInfo.stylesheet)) {
152
+ removeStylesheetEmptyRules(stylesheetInfo.stylesheet.children, docContext);
49
153
  }
50
154
  }
51
- sheetIndex++;
52
155
  });
53
- let startTime;
54
- if (DEBUG) {
55
- startTime = Date.now();
56
- log(" -- STARTED processStyleAttribute");
156
+ }
157
+
158
+ function collectStylesheetLayerOrder(cssRules, layerContext, docContext) {
159
+ const { layerStack, conditionalStack } = layerContext;
160
+ for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
161
+ const ruleData = cssRule.data;
162
+ if (ruleData.type === AT_RULE_TYPE && ruleData.name === LAYER_NAME) {
163
+ collectStylesheetLayerRule(ruleData, layerStack, conditionalStack, docContext);
164
+ } else if (ruleData.type === AT_RULE_TYPE && hasChildNodes(ruleData.block)) {
165
+ const newConditionalStack = buildConditionalStack(conditionalStack, ruleData, docContext);
166
+ collectStylesheetLayerOrder(ruleData.block.children, { layerStack, conditionalStack: newConditionalStack }, docContext);
167
+ } else if (ruleData.type === RULE_TYPE && hasChildNodes(ruleData.block)) {
168
+ collectStylesheetLayerOrder(ruleData.block.children, layerContext, docContext);
169
+ }
57
170
  }
58
- styles.forEach(style => processStyleAttribute(style, mediaAllInfo));
59
- if (DEBUG) {
60
- log(" -- ENDED processStyleAttribute delay =", Date.now() - startTime);
171
+ }
172
+
173
+ function collectStylesheetLayerRule(ruleData, layerStack, conditionalStack, docContext) {
174
+ if (ruleData.block) {
175
+ const layerName = getPreludeText(ruleData.prelude, docContext);
176
+ registerLayerDeclaration(layerStack, layerName, conditionalStack, docContext);
177
+ collectStylesheetLayerOrder(ruleData.block.children, { layerStack: [...layerStack, layerName], conditionalStack }, docContext);
178
+ } else if (ruleData.prelude) {
179
+ const layerNames = getPreludeText(ruleData.prelude, docContext).split(PRELUDE_SEPARATOR);
180
+ layerNames.forEach(layerName => registerLayerDeclaration(layerStack, layerName, conditionalStack, docContext));
61
181
  }
62
- return stats;
63
182
  }
64
183
 
65
- function processRules(cssRules, sheetIndex, mediaInfo, indexes = { mediaRuleIndex: 0 }) {
66
- let startTime;
67
- if (DEBUG && cssRules.size > 1) {
68
- startTime = Date.now();
69
- log(" -- STARTED processRules", "rules.length =", cssRules.size);
184
+ function buildConditionalStack(conditionalStack, ruleData, docContext) {
185
+ const isConditional = CONDITIONAL_AT_RULE_NAMES.has(ruleData.name);
186
+ return isConditional
187
+ ? [...conditionalStack, { name: ruleData.name, prelude: getPreludeText(ruleData.prelude, docContext) }]
188
+ : conditionalStack;
189
+ }
190
+
191
+ function registerLayerDeclaration(layerStack, layerName, conditionalStack, docContext) {
192
+ const fullLayerName = getFullLayerName([...layerStack, layerName]);
193
+ if (fullLayerName) {
194
+ docContext.layerDeclarations.push({
195
+ name: fullLayerName,
196
+ order: docContext.layerDeclarationCounter++,
197
+ conditionalStack: conditionalStack.slice()
198
+ });
70
199
  }
71
- const removedCssRules = [];
200
+ }
201
+
202
+ function minifyStylesheetRules(cssRules, stylesheets, processingContext, docContext) {
203
+ const removedRules = new Set();
72
204
  for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
73
- const ruleData = cssRule.data;
74
- if (ruleData.type == "Atrule" && ruleData.name == "import" && ruleData.prelude && ruleData.prelude.children && ruleData.prelude.children.head.data.importedChildren) {
75
- processRules(ruleData.prelude.children.head.data.importedChildren, sheetIndex, mediaInfo, indexes);
76
- } else if (ruleData.block && ruleData.block.children && ruleData.prelude && ruleData.prelude.children) {
77
- if (ruleData.type == "Atrule" && ruleData.name == "media") {
78
- const mediaText = cssTree.generate(ruleData.prelude);
79
- processRules(ruleData.block.children, sheetIndex, mediaInfo.medias.get("rule-" + sheetIndex + "-" + indexes.mediaRuleIndex + "-" + mediaText));
80
- indexes.mediaRuleIndex++;
81
- } else if (ruleData.type == "Rule") {
82
- const ruleInfo = mediaInfo.rules.get(ruleData);
83
- const pseudoSelectors = mediaInfo.pseudoRules.get(ruleData);
84
- if (!ruleInfo && !pseudoSelectors) {
85
- removedCssRules.push(cssRule);
86
- } else if (ruleInfo) {
87
- processRuleInfo(ruleData, ruleInfo, pseudoSelectors);
88
- if (!ruleData.prelude.children.size || !ruleData.block.children.size) {
89
- removedCssRules.push(cssRule);
90
- }
205
+ docContext.stats.processed++;
206
+ minifyRule(cssRule.data, cssRule, stylesheets, processingContext, removedRules, docContext);
207
+ }
208
+ removedRules.forEach(cssRule => cssRules.remove(cssRule));
209
+ }
210
+
211
+ function minifyRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
212
+ if (ruleData.type === AT_RULE_TYPE && ruleData.name === IMPORT_NAME && hasChildNodes(ruleData.prelude) && ruleData.prelude.children.head.data.importedChildren) {
213
+ minifyImportRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
214
+ } else if (ruleData.type === AT_RULE_TYPE && ruleData.name === LAYER_NAME && hasChildNodes(ruleData.block)) {
215
+ minifyLayerRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
216
+ } else if (ruleData.type === AT_RULE_TYPE && ruleData.name === SCOPE_NAME && hasChildNodes(ruleData.block)) {
217
+ minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
218
+ } else if (ruleData.type === AT_RULE_TYPE && ruleData.name !== FONT_FACE_NAME && ruleData.name !== KEYFRAMES_NAME && !ruleData.name.startsWith(VENDOR_PREFIX) && hasChildNodes(ruleData.block)) {
219
+ minifyAtRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
220
+ } else if (ruleData.type === RULE_TYPE && hasChildNodes(ruleData.prelude)) {
221
+ minifyStylesheetRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext);
222
+ }
223
+ }
224
+
225
+ function minifyImportRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
226
+ const urlNode = ruleData.prelude.children.head.data;
227
+ const topConditionalStack = urlNode.importedMediaText ? [{ name: "media", prelude: urlNode.importedMediaText }] : [];
228
+ if (urlNode.importedLayerName !== undefined) {
229
+ topConditionalStack.push({ name: "layer", prelude: urlNode.importedLayerName });
230
+ }
231
+ if (urlNode.importedSupportsCondition !== undefined) {
232
+ topConditionalStack.push({ name: "supports", prelude: urlNode.importedSupportsCondition });
233
+ }
234
+ minifyStylesheetRules(urlNode.importedChildren, stylesheets, {
235
+ ...processingContext,
236
+ conditionalStack: topConditionalStack
237
+ }, docContext);
238
+ }
239
+
240
+ function minifyLayerRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
241
+ const layerName = getPreludeText(ruleData.prelude, docContext);
242
+ const newProcessingContext = { ...processingContext, layerStack: [...processingContext.layerStack, layerName] };
243
+ expandRawCssRules(ruleData);
244
+ minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
245
+ if (!hasChildNodes(ruleData.block)) {
246
+ docContext.stats.discarded++;
247
+ removedRules.add(cssRule);
248
+ }
249
+ }
250
+
251
+ function minifyScopeRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
252
+ const parsedPrelude = parsePrelude(ruleData.prelude);
253
+ const includeLists = parsedPrelude.include.map(item => item.text);
254
+ const excludeLists = parsedPrelude.exclude.map(item => item.text);
255
+ const newConditionalStack = buildConditionalStack(processingContext.conditionalStack, ruleData, docContext);
256
+ const newProcessingContext = {
257
+ ...processingContext,
258
+ conditionalStack: newConditionalStack,
259
+ scopeIncludeLists: [...(processingContext.scopeIncludeLists || []), includeLists],
260
+ scopeExclusionLists: [...(processingContext.scopeExclusionLists || []), excludeLists],
261
+ scopeNestingLevel: (processingContext.scopeNestingLevel || 0) + 1
262
+ };
263
+ expandRawCssRules(ruleData);
264
+ minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
265
+ if (!hasChildNodes(ruleData.block)) {
266
+ docContext.stats.discarded++;
267
+ removedRules.add(cssRule);
268
+ }
269
+ }
270
+
271
+ function minifyAtRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
272
+ const newConditionalStack = buildConditionalStack(processingContext.conditionalStack, ruleData, docContext);
273
+ const newProcessingContext = { ...processingContext, conditionalStack: newConditionalStack };
274
+ expandRawCssRules(ruleData);
275
+ minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
276
+ if (!hasChildNodes(ruleData.block)) {
277
+ docContext.stats.discarded++;
278
+ removedRules.add(cssRule);
279
+ }
280
+ }
281
+
282
+ function minifyStylesheetRule(ruleData, cssRule, stylesheets, processingContext, removedRules, docContext) {
283
+ ruleData.order = docContext.rulesCounter++;
284
+ const removedSelectors = processSelectors(ruleData, processingContext, docContext);
285
+ const wasDiscarded = removeUnmatchedSelectors(ruleData, removedSelectors, removedRules, cssRule, docContext);
286
+ if (!wasDiscarded && hasChildNodes(ruleData.block)) {
287
+ processNestedRules(ruleData, stylesheets, processingContext, docContext);
288
+ }
289
+ }
290
+
291
+ function processSelectors(ruleData, processingContext, docContext) {
292
+ const removedSelectors = [];
293
+ const { ancestorsSelectors } = processingContext;
294
+ for (let selector = ruleData.prelude.children.head, selectorIndex = 0; selector; selector = selector.next, selectorIndex++) {
295
+ const {
296
+ startsWithCombinator,
297
+ hasPseudoElement,
298
+ hasDynamicStatePseudoClass,
299
+ scopeRelative
300
+ } = analyzeSelector(selector.data);
301
+ registerSelector(selector, ruleData, scopeRelative, processingContext, docContext);
302
+ if (!hasPseudoElement && !hasDynamicStatePseudoClass &&
303
+ (!startsWithCombinator || !ancestorsSelectors || !ancestorsSelectors.length)) {
304
+ const matchedElements = matchElements(selector, ancestorsSelectors, docContext);
305
+ if (matchedElements.length) {
306
+ updateMatchingSelectors(matchedElements, selector, docContext);
307
+ } else {
308
+ removedSelectors.push(selector);
309
+ }
310
+ }
311
+ }
312
+ return removedSelectors;
313
+ }
314
+
315
+ function analyzeSelector(selector) {
316
+ let hasPseudoElement = false;
317
+ let hasDynamicStatePseudoClass = false;
318
+ let hasNestingOrScope = false;
319
+ let startsWithCombinator = false;
320
+ cssTree.walk(selector, {
321
+ enter(node) {
322
+ if (node.type === PSEUDO_ELEMENT_SELECTOR_TYPE) {
323
+ hasPseudoElement = true;
324
+ } else if (node.type === PSEUDO_CLASS_SELECTOR_TYPE) {
325
+ if (CANONICAL_PSEUDO_ELEMENT_NAMES.has(node.name)) {
326
+ hasPseudoElement = true;
327
+ } else if (DYNAMIC_STATE_PSEUDO_CLASSES.has(node.name)) {
328
+ hasDynamicStatePseudoClass = true;
329
+ } else if (node.name === SCOPE_NAME) {
330
+ hasNestingOrScope = true;
331
+ }
332
+ } else if (node.type === NESTING_SELECTOR_TYPE) {
333
+ hasNestingOrScope = true;
334
+ }
335
+ }
336
+ });
337
+ const firstChild = selector.children.head.data;
338
+ startsWithCombinator = firstChild && firstChild.type === COMBINATOR_NAME;
339
+ const scopeRelative = !startsWithCombinator && !hasNestingOrScope;
340
+ return { hasPseudoElement, hasDynamicStatePseudoClass, startsWithCombinator, scopeRelative };
341
+ }
342
+
343
+ function updateMatchingSelectors(matchedElements, selector, docContext) {
344
+ matchedElements.forEach(element => {
345
+ docContext.matchedElements.add(element);
346
+ let matchingSelectors = docContext.matchingSelectors.get(element);
347
+ if (!matchingSelectors) {
348
+ matchingSelectors = [];
349
+ docContext.matchingSelectors.set(element, matchingSelectors);
350
+ element.matchingSelectors = matchingSelectors;
351
+ }
352
+ matchingSelectors.push(selector);
353
+ });
354
+ }
355
+
356
+ function processNestedRules(ruleData, stylesheets, processingContext, docContext) {
357
+ expandRawCssRules(ruleData);
358
+ removeDuplicateDeclarations(ruleData.block);
359
+ const newProcessingContext = { ...processingContext, ancestorsSelectors: [...processingContext.ancestorsSelectors, ruleData.prelude] };
360
+ minifyStylesheetRules(ruleData.block.children, stylesheets, newProcessingContext, docContext);
361
+ }
362
+
363
+ function registerSelector(selector, ruleData, scopeRelative, processingContext, docContext) {
364
+ const {
365
+ ancestorsSelectors,
366
+ layerStack,
367
+ conditionalStack,
368
+ scopeIncludeLists,
369
+ scopeExclusionLists,
370
+ scopeNestingLevel
371
+ } = processingContext;
372
+ docContext.selectorData.set(selector, {
373
+ specificity: computeMaxSpecificity(selector.data, ancestorsSelectors),
374
+ rule: ruleData,
375
+ layerStack,
376
+ conditionalStack,
377
+ scopeIncludeLists,
378
+ scopeExclusionLists,
379
+ scopeNestingLevel,
380
+ scopeRelative
381
+ });
382
+ }
383
+
384
+ function computeCascadedStylesForElement(element, winningDeclarations, docContext) {
385
+ const cascadedStyles = new Map();
386
+ const allDeclarations = collectDeclarationItemsForElement(element, docContext);
387
+ const contextGroups = new Map();
388
+ allDeclarations.forEach(declarationData => {
389
+ const { selector } = declarationData;
390
+ const conditionalStack = getConditionalStackForSelector(selector, docContext);
391
+ const contextKey = createContextKey(conditionalStack);
392
+ if (!contextGroups.has(contextKey)) {
393
+ contextGroups.set(contextKey, []);
394
+ }
395
+ contextGroups.get(contextKey).push(declarationData);
396
+ });
397
+ contextGroups.forEach(declarations => {
398
+ declarations.sort((declarationA, declarationB) => compareDeclarations(declarationA, declarationB, docContext));
399
+ declarations.forEach(declarationData => {
400
+ const { selector, declaration } = declarationData;
401
+ const conditionalStack = getConditionalStackForSelector(selector, docContext);
402
+ cascadedStyles.set(declaration.data.property + DECLARATION_KEY_SEPARATOR + createContextKey(conditionalStack), {
403
+ selector,
404
+ declaration
405
+ });
406
+ });
407
+ });
408
+ cascadedStyles.forEach(({ declaration }) => winningDeclarations.add(declaration));
409
+ }
410
+
411
+ function createContextKey(conditionalStack) {
412
+ return conditionalStack.map(context => `${context.name}:${context.prelude}`).join(CONTEXT_KEY_SEPARATOR);
413
+ }
414
+
415
+ function collectDeclarationItemsForElement(element, docContext) {
416
+ const matchingSelectors = docContext.matchingSelectors.get(element);
417
+ const allDeclarations = [];
418
+ matchingSelectors.forEach(selector => {
419
+ const cssRule = docContext.selectorData.get(selector).rule;
420
+ if (hasChildNodes(cssRule.block)) {
421
+ const declarations = cssRule.block.children;
422
+ for (let declaration = declarations.head; declaration; declaration = declaration.next) {
423
+ const { type, value } = declaration.data;
424
+ if (type === DECLARATION_TYPE && value && value.type !== RAW_TYPE) {
425
+ allDeclarations.push({
426
+ declaration,
427
+ selector,
428
+ effectiveSpecificity: computeEffectiveSpecificity(
429
+ docContext.selectorData.get(selector), element, docContext),
430
+ isInline: false
431
+ });
91
432
  }
92
433
  }
434
+ }
435
+ });
436
+ const inlineDeclarations = getInlineStyleDeclarations(element);
437
+ for (const declaration of inlineDeclarations) {
438
+ allDeclarations.push({
439
+ declaration: declaration.declaration,
440
+ effectiveSpecificity: declaration.effectiveSpecificity,
441
+ isInline: true
442
+ });
443
+ }
444
+ return allDeclarations;
445
+ }
446
+
447
+ function getConditionalStackForSelector(selector, docContext) {
448
+ let conditionalStack = [];
449
+ if (selector) {
450
+ const selectorData = docContext.selectorData.get(selector);
451
+ if (selectorData && selectorData.conditionalStack) {
452
+ conditionalStack = selectorData.conditionalStack;
453
+ }
454
+ }
455
+ return conditionalStack;
456
+ }
457
+
458
+ function matchElements(selector, ancestorsSelectors, docContext) {
459
+ const selectorText = createSelectorText(selector, ancestorsSelectors, docContext);
460
+ const selectorData = docContext.selectorData.get(selector);
461
+ const hasScope = selectorData && ((selectorData.scopeIncludeLists && selectorData.scopeIncludeLists.length) || selectorData.scopeNestingLevel > 0);
462
+ const cacheKey = createMatchCacheKey(hasScope, selectorData, selectorText);
463
+ const cached = docContext.matchedSelectors.get(cacheKey);
464
+ if (cached) {
465
+ return cached;
466
+ } else {
467
+ if (hasScope) {
468
+ return collectScopedMatches(cacheKey, selector, docContext);
93
469
  } else {
94
- if (!ruleData || ruleData.type == "Raw" || (ruleData.type == "Rule" && (!ruleData.prelude || ruleData.prelude.type == "Raw"))) {
95
- removedCssRules.push(cssRule);
470
+ const nodes = querySelectorAll(docContext.doc, selectorText, docContext.scopeRoots);
471
+ docContext.matchedSelectors.set(cacheKey, nodes);
472
+ return nodes;
473
+ }
474
+ }
475
+ }
476
+
477
+ function createSelectorText(selector, ancestorsSelectors, docContext) {
478
+ let selectorText;
479
+ if (ancestorsSelectors && ancestorsSelectors.length) {
480
+ selectorText = combineSelectorWithAncestors(selector.data, ancestorsSelectors, docContext);
481
+ const combinedAst = parseCss(selectorText, SELECTOR_LIST_CONTEXT);
482
+ selectorText = sanitizeSelector({ data: combinedAst }, ancestorsSelectors, docContext);
483
+ }
484
+ if (!selectorText) {
485
+ selectorText = sanitizeSelector(selector, ancestorsSelectors, docContext);
486
+ }
487
+ return selectorText;
488
+ }
489
+
490
+ function createMatchCacheKey(hasScope, selectorData, selectorText) {
491
+ if (hasScope) {
492
+ const include = selectorData.scopeIncludeLists || [];
493
+ const exclude = selectorData.scopeExclusionLists || [];
494
+ const relative = selectorData.scopeRelative ? 1 : 0;
495
+ const nesting = selectorData.scopeNestingLevel || 0;
496
+ return [
497
+ selectorText,
498
+ JSON.stringify(include),
499
+ JSON.stringify(exclude), String(relative), String(nesting)
500
+ ].join(CONTEXT_KEY_SEPARATOR);
501
+ } else {
502
+ return selectorText;
503
+ }
504
+ }
505
+
506
+ function collectScopedMatches(cacheKey, selector, docContext) {
507
+ const selectorData = docContext.selectorData.get(selector);
508
+ const includeLists = selectorData.scopeIncludeLists && selectorData.scopeIncludeLists.length ? selectorData.scopeIncludeLists[selectorData.scopeIncludeLists.length - 1] : [];
509
+ const excludeLists = selectorData.scopeExclusionLists && selectorData.scopeExclusionLists.length ? selectorData.scopeExclusionLists[selectorData.scopeExclusionLists.length - 1] : [];
510
+ const matchedSet = new Set();
511
+ const includes = includeLists.length ? includeLists : [ROOT_PSEUDO_CLASS];
512
+ for (const includeSelector of includes) {
513
+ collectMatchesForInclude(includeSelector, selector, excludeLists, docContext, matchedSet);
514
+ }
515
+ const matchedElements = Array.from(matchedSet);
516
+ docContext.matchedSelectors.set(cacheKey, matchedElements);
517
+ return matchedElements;
518
+ }
519
+
520
+ function collectMatchesForInclude(includeSelector, selector, excludeLists, docContext, matchedSet) {
521
+ const rootsForInclude = getScopeRoots(includeSelector, docContext);
522
+ for (const rootForInclude of rootsForInclude) {
523
+ const roots = querySelectorForRoot(rootForInclude, normalizeForRoot(selector), docContext.scopeRoots);
524
+ if (roots.length) {
525
+ if (excludeLists && excludeLists.length) {
526
+ const filteredRoots = filterExcludedRoots(roots, excludeLists, docContext);
527
+ filteredRoots.forEach(root => matchedSet.add(root));
528
+ } else {
529
+ roots.forEach(root => matchedSet.add(root));
96
530
  }
97
531
  }
98
532
  }
99
- removedCssRules.forEach(cssRule => cssRules.remove(cssRule));
100
- if (DEBUG && cssRules.size > 1) {
101
- log(" -- ENDED processRules delay =", Date.now() - startTime);
533
+ }
534
+
535
+ function querySelectorForRoot(root, selector, cache) {
536
+ const nodes = querySelectorAll(root, selector, cache);
537
+ if (root.matches && root.matches(selector)) {
538
+ if (nodes.indexOf(root) === -1) {
539
+ nodes.unshift(root);
540
+ }
102
541
  }
542
+ return nodes;
103
543
  }
104
544
 
105
- function processRuleInfo(ruleData, ruleInfo, pseudoSelectors) {
106
- const removedDeclarations = [];
107
- const removedSelectors = [];
108
- let pseudoSelectorFound;
109
- for (let selector = ruleData.prelude.children.head; selector; selector = selector.next) {
110
- const selectorText = cssTree.generate(selector.data);
111
- if (pseudoSelectors && pseudoSelectors.has(selectorText)) {
112
- pseudoSelectorFound = true;
545
+ function normalizeForRoot(selector) {
546
+ const selectorData = cssTree.clone(selector.data);
547
+ cssTree.walk(selectorData, {
548
+ visit: NESTING_SELECTOR_TYPE,
549
+ enter(_node, item, list) {
550
+ const scope = { type: PSEUDO_CLASS_SELECTOR_TYPE, name: SCOPE_NAME };
551
+ list.insertData(scope, item);
552
+ list.remove(item);
553
+ }
554
+ });
555
+ for (let selectorChild = selectorData.children.head; selectorChild; selectorChild = selectorChild.next) {
556
+ const childData = selectorChild.data;
557
+ if (hasChildNodes(childData)) {
558
+ const head = childData.children.head;
559
+ const headData = head.data;
560
+ if (headData && headData.type === COMBINATOR_NAME) {
561
+ const scope = { type: PSEUDO_CLASS_SELECTOR_TYPE, name: SCOPE_NAME };
562
+ childData.children.insertData(scope, head);
563
+ }
564
+ }
565
+ }
566
+ return cssTree.generate(selectorData);
567
+ }
568
+
569
+ function getScopeRoots(selector, docContext) {
570
+ let roots = docContext.scopeRoots.get(selector);
571
+ if (!roots) {
572
+ roots = querySelectorAll(docContext.doc, selector, docContext.scopeRoots);
573
+ }
574
+ return roots;
575
+ }
576
+
577
+ function filterExcludedRoots(roots, excludeLists, docContext) {
578
+ const excludeRoots = new Set();
579
+ for (const excludeSelector of excludeLists) {
580
+ const rootsForExclude = getScopeRoots(excludeSelector, docContext);
581
+ rootsForExclude.forEach(root => excludeRoots.add(root));
582
+ }
583
+ return roots.filter(node => !Array.from(excludeRoots).some(excludedRoot => excludedRoot.contains(node)));
584
+ }
585
+
586
+ function compareDeclarations(declarationA, declarationB, docContext) {
587
+ const importantA = declarationA.declaration.data.important ? CSS_IMPORTANCE_IMPORTANT : CSS_IMPORTANCE_NOT_IMPORTANT;
588
+ const importantB = declarationB.declaration.data.important ? CSS_IMPORTANCE_IMPORTANT : CSS_IMPORTANCE_NOT_IMPORTANT;
589
+ if (importantA !== importantB) {
590
+ return importantA - importantB;
591
+ }
592
+ if (declarationA.isInline && !declarationB.isInline) return 1;
593
+ if (!declarationA.isInline && declarationB.isInline) return -1;
594
+ let selectorDataA = declarationA.selector ? docContext.selectorData.get(declarationA.selector) : null;
595
+ let selectorDataB = declarationB.selector ? docContext.selectorData.get(declarationB.selector) : null;
596
+ if (selectorDataA && selectorDataB) {
597
+ const layerComparison = compareLayers(selectorDataA.layerStack, selectorDataB.layerStack, docContext);
598
+ if (layerComparison !== 0) {
599
+ return importantA ? -layerComparison : layerComparison;
600
+ }
601
+ const specificityA = declarationA.effectiveSpecificity;
602
+ const specificityB = declarationB.effectiveSpecificity;
603
+ if (specificityA.a !== specificityB.a) {
604
+ return specificityA.a - specificityB.a;
605
+ }
606
+ if (specificityA.b !== specificityB.b) {
607
+ return specificityA.b - specificityB.b;
113
608
  }
114
- if (!ruleInfo.matchedSelectors.has(selectorText) && (!pseudoSelectors || !pseudoSelectors.has(selectorText))) {
115
- removedSelectors.push(selector);
609
+ if (specificityA.c !== specificityB.c) {
610
+ return specificityA.c - specificityB.c;
611
+ }
612
+ if (selectorDataA.rule.order !== selectorDataB.rule.order) {
613
+ return selectorDataA.rule.order - selectorDataB.rule.order;
614
+ }
615
+ return 0;
616
+ } else {
617
+ const specificityA = declarationA.effectiveSpecificity;
618
+ const specificityB = declarationB.effectiveSpecificity;
619
+ if (specificityA.a !== specificityB.a) {
620
+ return specificityA.a - specificityB.a;
621
+ }
622
+ if (specificityA.b !== specificityB.b) {
623
+ return specificityA.b - specificityB.b;
624
+ }
625
+ if (specificityA.c !== specificityB.c) {
626
+ return specificityA.c - specificityB.c;
627
+ }
628
+ return 0;
629
+ }
630
+ }
631
+
632
+ function compareLayers(layersA, layersB, docContext) {
633
+ const isUnlayeredA = layersA.length === 0 || layersA.every(layerName => layerName === EMPTY_STRING);
634
+ const isUnlayeredB = layersB.length === 0 || layersB.every(layerName => layerName === EMPTY_STRING);
635
+ if (isUnlayeredA && isUnlayeredB) {
636
+ return 0;
637
+ }
638
+ if (isUnlayeredA) {
639
+ return 1;
640
+ }
641
+ if (isUnlayeredB) {
642
+ return -1;
643
+ }
644
+ const fullLayerNameA = getFullLayerName(layersA);
645
+ const fullLayerNameB = getFullLayerName(layersB);
646
+ if (fullLayerNameA === fullLayerNameB) {
647
+ return 0;
648
+ }
649
+ const minLength = Math.min(layersA.length, layersB.length);
650
+ const effectiveMap = docContext.layerOrder;
651
+ for (let indexLayer = 0; indexLayer < minLength; indexLayer++) {
652
+ if (layersA[indexLayer] !== layersB[indexLayer]) {
653
+ const partialLayerA = getFullLayerName(layersA.slice(0, indexLayer + 1));
654
+ const partialLayerB = getFullLayerName(layersB.slice(0, indexLayer + 1));
655
+ const orderA = effectiveMap.get(partialLayerA);
656
+ const orderB = effectiveMap.get(partialLayerB);
657
+ if (orderA !== undefined && orderB !== undefined) {
658
+ return orderA - orderB;
659
+ }
660
+ if (orderA !== undefined) {
661
+ return -1;
662
+ }
663
+ if (orderB !== undefined) {
664
+ return 1;
665
+ }
666
+ return 0;
116
667
  }
117
668
  }
118
- if (!pseudoSelectorFound) {
119
- for (let declaration = ruleData.block.children.tail; declaration; declaration = declaration.prev) {
120
- if (!ruleInfo.declarations.has(declaration.data)) {
121
- removedDeclarations.push(declaration);
669
+ return layersA.length - layersB.length;
670
+ }
671
+
672
+ function removeStylesheetEmptyRules(cssRules, docContext) {
673
+ const removedRules = new Set();
674
+ for (let cssRule = cssRules.head; cssRule; cssRule = cssRule.next) {
675
+ const ruleData = cssRule.data;
676
+ if (ruleData.type === RULE_TYPE) {
677
+ if (hasChildNodes(ruleData.block)) {
678
+ removeStylesheetEmptyRules(ruleData.block.children, docContext);
679
+ } else {
680
+ docContext.stats.discarded++;
681
+ removedRules.add(cssRule);
682
+ }
683
+ } else if (ruleData.type === AT_RULE_TYPE && ruleData.block && ruleData.name !== FONT_FACE_NAME && ruleData.name !== KEYFRAMES_NAME) {
684
+ removeStylesheetEmptyRules(ruleData.block.children, docContext);
685
+ if (!hasChildNodes(ruleData.block)) {
686
+ docContext.stats.discarded++;
687
+ removedRules.add(cssRule);
122
688
  }
123
689
  }
124
690
  }
125
- removedDeclarations.forEach(declaration => ruleData.block.children.remove(declaration));
126
- removedSelectors.forEach(selector => ruleData.prelude.children.remove(selector));
691
+ removedRules.forEach(cssRule => cssRules.remove(cssRule));
692
+ }
693
+
694
+ function removeUnmatchedSelectors(ruleData, removedSelectors, removedRules, cssRule, docContext) {
695
+ if (removedSelectors && removedSelectors.length) {
696
+ removedSelectors.forEach(selector => ruleData.prelude.children.remove(selector));
697
+ }
698
+ if (!hasChildNodes(ruleData.prelude)) {
699
+ docContext.stats.discarded++;
700
+ removedRules.add(cssRule);
701
+ return true;
702
+ }
703
+ return false;
127
704
  }
128
705
 
129
- function processStyleAttribute(styleData, mediaAllInfo) {
706
+ function removeDuplicateDeclarations(block) {
707
+ const propertyMap = new Map();
130
708
  const removedDeclarations = [];
131
- const styleInfo = mediaAllInfo.matchedStyles.get(styleData);
132
- if (styleInfo) {
133
- let propertyFound;
134
- for (let declaration = styleData.children.head; declaration && !propertyFound; declaration = declaration.next) {
135
- if (!styleInfo.declarations.has(declaration.data)) {
136
- removedDeclarations.push(declaration);
709
+ for (let ruleChildNode = block.children.head; ruleChildNode; ruleChildNode = ruleChildNode.next) {
710
+ if (ruleChildNode.data.type === DECLARATION_TYPE) {
711
+ const property = ruleChildNode.data.property;
712
+ const isImportant = ruleChildNode.data.important;
713
+ if (propertyMap.has(property)) {
714
+ const existing = propertyMap.get(property);
715
+ if (existing.isImportant === isImportant) {
716
+ removedDeclarations.push(existing.node);
717
+ propertyMap.set(property, { node: ruleChildNode, isImportant });
718
+ } else if (isImportant && !existing.isImportant) {
719
+ removedDeclarations.push(existing.node);
720
+ propertyMap.set(property, { node: ruleChildNode, isImportant });
721
+ } else {
722
+ removedDeclarations.push(ruleChildNode);
723
+ }
724
+ } else {
725
+ propertyMap.set(property, { node: ruleChildNode, isImportant });
137
726
  }
138
727
  }
139
- removedDeclarations.forEach(declaration => styleData.children.remove(declaration));
140
728
  }
729
+ removedDeclarations.forEach(declaration => block.children.remove(declaration));
730
+ }
731
+
732
+ function removeLosingDeclarations(winningDeclarations, docContext) {
733
+ const allDeclarations = new Map();
734
+ const protectedDeclarations = new Set();
735
+ docContext.matchedElements.forEach(element => {
736
+ const matchingSelectors = docContext.matchingSelectors.get(element);
737
+ if (matchingSelectors) {
738
+ matchingSelectors.forEach(selector => {
739
+ const cssRule = docContext.selectorData.get(selector).rule;
740
+ if (hasChildNodes(cssRule.block)) {
741
+ const declarations = cssRule.block.children;
742
+ for (let declaration = declarations.head; declaration; declaration = declaration.next) {
743
+ if (declaration.data.type === DECLARATION_TYPE) {
744
+ allDeclarations.set(declaration, declarations);
745
+ const { property, value } = declaration.data;
746
+ if (property && property.startsWith(CUSTOM_PROPERTY_PREFIX) || (value && value.type === RAW_TYPE)) {
747
+ protectedDeclarations.add(declaration);
748
+ }
749
+ }
750
+ }
751
+ }
752
+ });
753
+ }
754
+ });
755
+ allDeclarations.forEach((list, node) => {
756
+ if (!winningDeclarations.has(node) && !protectedDeclarations.has(node)) {
757
+ list.remove(node);
758
+ }
759
+ });
141
760
  }
142
761
 
143
- function log(...args) {
144
- console.log("S-File <css-min>", ...args); // eslint-disable-line no-console
762
+ function expandRawCssRules(ruleData) {
763
+ const ruleChildren = [];
764
+ if (hasChildNodes(ruleData.block)) {
765
+ for (let cssRuleNode = ruleData.block.children.head; cssRuleNode; cssRuleNode = cssRuleNode.next) {
766
+ if (cssRuleNode.data.type === RAW_TYPE) {
767
+ if (cssRuleNode.data.value.indexOf(BLOCK_OPEN) !== -1 &&
768
+ cssRuleNode.data.value.indexOf(BLOCK_OPEN) < cssRuleNode.data.value.indexOf(BLOCK_CLOSE)) {
769
+ try {
770
+ const stylesheet = parseCss(cssRuleNode.data.value, STYLESHEET_CONTEXT);
771
+ for (let stylesheetChild = stylesheet.children.head; stylesheetChild; stylesheetChild = stylesheetChild.next) {
772
+ ruleChildren.push(stylesheetChild);
773
+ }
774
+ } catch (error) {
775
+ if (DEBUG) {
776
+ // eslint-disable-next-line no-console
777
+ console.warn(PARSE_CSS_ERROR_MESSAGE, cssRuleNode.data.value, error);
778
+ }
779
+ }
780
+ } else {
781
+ ruleChildren.push(cssRuleNode);
782
+ }
783
+ } else {
784
+ ruleChildren.push(cssRuleNode);
785
+ }
786
+ }
787
+ }
788
+ ruleData.block.children.clear();
789
+ ruleChildren.forEach(ruleChild => ruleData.block.children.appendData(ruleChild.data));
790
+ }
791
+
792
+ function combineSelectorWithAncestors(selector, ancestorsSelectors, docContext) {
793
+ const selectorText = getSelectorText(selector, docContext);
794
+ if (!ancestorsSelectors || !ancestorsSelectors.length) {
795
+ return selectorText;
796
+ } else {
797
+ let contexts = [EMPTY_STRING];
798
+ ancestorsSelectors.forEach(selectorList => {
799
+ if (hasChildNodes(selectorList)) {
800
+ const parentSelectors = selectorList.children.toArray();
801
+ const nextContexts = [];
802
+ contexts.forEach(context => parentSelectors.forEach(parentSelector => {
803
+ const parentText = getSelectorText(parentSelector, docContext);
804
+ const combined = context ? combineSelectors(context, parentText) : parentText;
805
+ if (!nextContexts.includes(combined)) {
806
+ nextContexts.push(combined);
807
+ }
808
+ }));
809
+ if (nextContexts.length) {
810
+ contexts = nextContexts;
811
+ }
812
+ }
813
+ });
814
+ const expandedSelectors = new Set();
815
+ contexts.forEach(context => {
816
+ const result = context ? combineSelectors(context, selectorText) : selectorText;
817
+ expandedSelectors.add(result);
818
+ });
819
+ return Array.from(expandedSelectors).join(PRELUDE_SEPARATOR);
820
+ }
821
+ }
822
+
823
+ function combineSelectors(parentSelectorText, childSelectorText) {
824
+ const childSelector = parseCss(childSelectorText || NESTING_SELECTOR);
825
+ const parentSelector = parentSelectorText ? parseCss(parentSelectorText) : null;
826
+ let hasNesting = false;
827
+ cssTree.walk(childSelector, {
828
+ visit: NESTING_SELECTOR_TYPE,
829
+ enter(_node, item, list) {
830
+ hasNesting = true;
831
+ if (!parentSelector) {
832
+ list.remove(item);
833
+ return;
834
+ }
835
+ const nodes = parentSelector.children.toArray().map(parent => cssTree.clone(parent));
836
+ nodes.forEach(node => list.insertData(node, item));
837
+ list.remove(item);
838
+ }
839
+ });
840
+ if (hasNesting) {
841
+ return cssTree.generate(childSelector);
842
+ }
843
+ if (!parentSelector) {
844
+ return cssTree.generate(childSelector);
845
+ }
846
+ const combinedSelector = parseCss(`${parentSelectorText} ${childSelectorText}`);
847
+ return cssTree.generate(combinedSelector);
848
+ }
849
+
850
+ function computeEffectiveSpecificity(selectorData, element, docContext) {
851
+ const baseSpecificity = selectorData.specificity;
852
+ let effectiveSpecificity = { a: baseSpecificity.a, b: baseSpecificity.b, c: baseSpecificity.c };
853
+ const includeLists = selectorData && selectorData.scopeIncludeLists && selectorData.scopeIncludeLists.length ? selectorData.scopeIncludeLists[selectorData.scopeIncludeLists.length - 1] : [];
854
+ if (includeLists && includeLists.length) {
855
+ for (const includeSelector of includeLists) {
856
+ const roots = getScopeRoots(includeSelector, docContext);
857
+ if (roots.some(root => root.contains(element))) {
858
+ const includeSpecificity = getIncludeSpecificity(includeSelector, docContext);
859
+ effectiveSpecificity = {
860
+ a: effectiveSpecificity.a + includeSpecificity.a,
861
+ b: effectiveSpecificity.b + includeSpecificity.b,
862
+ c: effectiveSpecificity.c + includeSpecificity.c
863
+ };
864
+ }
865
+ }
866
+ }
867
+ return effectiveSpecificity;
868
+ }
869
+
870
+ function getIncludeSpecificity(includeSelector, docContext) {
871
+ let specificity = docContext.scopeSpecificities.get(includeSelector);
872
+ if (!specificity) {
873
+ const selector = parseCss(includeSelector);
874
+ specificity = computeMaxSpecificity(selector, []);
875
+ docContext.scopeSpecificities.set(includeSelector, specificity);
876
+ }
877
+ return specificity;
878
+ }
879
+
880
+ function hasChildNodes(node) {
881
+ return Boolean(node && node.children && node.children.head);
882
+ }
883
+
884
+ function getSelectorText(selector, docContext) {
885
+ if (!docContext.selectorTexts.has(selector)) {
886
+ docContext.selectorTexts.set(selector, cssTree.generate(selector));
887
+ }
888
+ return docContext.selectorTexts.get(selector);
889
+ }
890
+
891
+ function getPreludeText(prelude, docContext) {
892
+ if (prelude) {
893
+ if (!docContext.preludeTexts.has(prelude)) {
894
+ docContext.preludeTexts.set(prelude, cssTree.generate(prelude));
895
+ }
896
+ return docContext.preludeTexts.get(prelude);
897
+ } else {
898
+ return EMPTY_STRING;
899
+ }
900
+ }
901
+
902
+ function getFullLayerName(layers) {
903
+ return layers.filter(layerName => layerName !== EMPTY_STRING).join(LAYER_NAME_SEPARATOR);
904
+ }
905
+
906
+ function parseCss(text, context = SELECTOR_CONTEXT) {
907
+ const options = { context };
908
+ return cssTree.parse(text, options);
909
+ }
910
+
911
+ function querySelectorAll(root, selector, cache) {
912
+ if (cache && cache !== root) {
913
+ let rootCache = cache.get(root);
914
+ if (!rootCache) {
915
+ rootCache = new Map();
916
+ cache.set(root, rootCache);
917
+ }
918
+ if (rootCache.has(selector)) {
919
+ return rootCache.get(selector);
920
+ } else {
921
+ try {
922
+ const nodes = Array.from(root.querySelectorAll(selector));
923
+ rootCache.set(selector, nodes);
924
+ return nodes;
925
+ } catch {
926
+ if (DEBUG) {
927
+ // eslint-disable-next-line no-console
928
+ console.warn(QSA_ERROR_MESSAGE, selector, root.tagName ? root.tagName : EMPTY_STRING);
929
+ }
930
+ rootCache.set(selector, []);
931
+ return [];
932
+ }
933
+ }
934
+ } else {
935
+ try {
936
+ return Array.from(root.querySelectorAll(selector));
937
+ } catch {
938
+ if (DEBUG) {
939
+ // eslint-disable-next-line no-console
940
+ console.warn(QSA_ERROR_MESSAGE, selector);
941
+ }
942
+ return [];
943
+ }
944
+ }
945
+ }
946
+
947
+ function getInlineStyleDeclarations(element) {
948
+ const style = element.getAttribute(STYLE_ATTRIBUTE_NAME);
949
+ if (style) {
950
+ let declarationNodes;
951
+ try {
952
+ declarationNodes = cssTree.parse(style, { context: DECLARATION_LIST_CONTEXT });
953
+ } catch {
954
+ return [];
955
+ }
956
+ const declarations = [];
957
+ for (let node = declarationNodes.children && declarationNodes.children.head; node; node = node.next) {
958
+ if (node.data.type === DECLARATION_TYPE) {
959
+ declarations.push({
960
+ declaration: node,
961
+ effectiveSpecificity: { a: 1, b: 0, c: 0 },
962
+ isInline: true
963
+ });
964
+ }
965
+ }
966
+ return declarations;
967
+ } else {
968
+ return [];
969
+ }
145
970
  }