single-file-core 1.5.48 → 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.
package/core/index.js CHANGED
@@ -1404,10 +1404,7 @@ class Processor {
1404
1404
  }
1405
1405
 
1406
1406
  removeUnusedStyles() {
1407
- if (!this.mediaAllInfo) {
1408
- this.mediaAllInfo = util.getMediaAllInfo(this.doc, this.stylesheets, this.styles);
1409
- }
1410
- const stats = util.minifyCSSRules(this.stylesheets, this.styles, this.mediaAllInfo);
1407
+ const stats = util.minifyCSSRules(this.doc, this.stylesheets);
1411
1408
  this.stats.set("processed", "CSS rules", stats.processed);
1412
1409
  this.stats.set("discarded", "CSS rules", stats.discarded);
1413
1410
  }
@@ -183,6 +183,22 @@ function getProcessorHelperClass(utilInstance) {
183
183
  if (mediaQueryListNode) {
184
184
  content.data = this.wrapMediaQuery(content.data, cssTree.generate(mediaQueryListNode));
185
185
  }
186
+ const layerListNode = cssTree.find(node, node => node.type == "LayerList");
187
+ if (layerListNode) {
188
+ const layerNames = [];
189
+ layerListNode.children.forEach(child => {
190
+ if (child.type == "Identifier") {
191
+ layerNames.push(child.name);
192
+ }
193
+ });
194
+ if (layerNames.length == 1) {
195
+ content.data = this.wrapLayer(content.data, layerNames[0]);
196
+ }
197
+ }
198
+ const supportsNode = cssTree.find(node, node => node.type == "Supports");
199
+ if (supportsNode) {
200
+ content.data = "@supports " + cssTree.generate(supportsNode) + " { " + content.data + " }";
201
+ }
186
202
  const importedStylesheet = cssTree.parse(content.data, { context: "stylesheet", parseCustomProperty: true });
187
203
  const ancestorStyleSheets = new Set(importedStyleSheets);
188
204
  ancestorStyleSheets.add(resourceURL);
@@ -470,6 +486,14 @@ function getProcessorHelperClass(utilInstance) {
470
486
  }
471
487
  }
472
488
 
489
+ wrapLayer(stylesheetContent, layerName) {
490
+ if (layerName) {
491
+ return "@layer " + layerName + " { " + stylesheetContent + " }";
492
+ } else {
493
+ return stylesheetContent;
494
+ }
495
+ }
496
+
473
497
  getAdditionalPageData() {
474
498
  return {};
475
499
  }
@@ -162,10 +162,18 @@ function getProcessorHelperClass(utilInstance) {
162
162
  }
163
163
  if (testValidURL(resourceURL)) {
164
164
  const mediaQueryListNode = cssTree.find(node, node => node.type == "MediaQueryList");
165
- let mediaText;
165
+ let mediaText, layerName, supportsCondition;
166
166
  if (mediaQueryListNode) {
167
167
  mediaText = cssTree.generate(mediaQueryListNode);
168
168
  }
169
+ const layerNode = cssTree.find(node, node => node.type == "Layer");
170
+ if (layerNode) {
171
+ layerName = layerNode.name;
172
+ }
173
+ const supportsNode = cssTree.find(node, node => node.type == "Supports");
174
+ if (supportsNode) {
175
+ supportsCondition = cssTree.generate(supportsNode);
176
+ }
169
177
  const existingStylesheet = Array.from(stylesheets).find(([, stylesheetInfo]) => stylesheetInfo.resourceURL == resourceURL);
170
178
  let stylesheet;
171
179
  if (existingStylesheet) {
@@ -178,7 +186,9 @@ function getProcessorHelperClass(utilInstance) {
178
186
  } else {
179
187
  const stylesheetInfo = {
180
188
  scoped,
181
- mediaText
189
+ mediaText,
190
+ layerName,
191
+ supportsCondition
182
192
  };
183
193
  const content = await this.getStylesheetContent(resourceURL, options);
184
194
  stylesheetInfo.url = resourceURL = content.resourceURL;
@@ -189,6 +199,9 @@ function getProcessorHelperClass(utilInstance) {
189
199
  stylesheets.set({ urlNode }, stylesheetInfo);
190
200
  }
191
201
  urlNode.importedChildren = stylesheet.children;
202
+ urlNode.importedMediaText = mediaText;
203
+ urlNode.importedLayerName = layerName;
204
+ urlNode.importedSupportsCondition = supportsCondition;
192
205
  }
193
206
  }
194
207
  }
package/core/util.js CHANGED
@@ -160,15 +160,12 @@ function getInstance(utilOptions) {
160
160
  minifyHTML(doc, options) {
161
161
  return modules.htmlMinifier.process(doc, options);
162
162
  },
163
- minifyCSSRules(stylesheets, styles, mediaAllInfo) {
164
- return modules.cssRulesMinifier.process(stylesheets, styles, mediaAllInfo);
163
+ minifyCSSRules(doc, stylesheets) {
164
+ return modules.cssRulesMinifier.process(doc, stylesheets);
165
165
  },
166
166
  removeUnusedFonts(doc, stylesheets, styles, options) {
167
167
  return modules.fontsMinifier.process(doc, stylesheets, styles, options);
168
168
  },
169
- getMediaAllInfo(doc, stylesheets, styles) {
170
- return modules.matchedRules.getMediaAllInfo(doc, stylesheets, styles);
171
- },
172
169
  compressCSS(content, options) {
173
170
  return vendor.cssMinifier.processString(content, options);
174
171
  },
@@ -0,0 +1,63 @@
1
+ import * as mediaQueryParser from "../vendor/css-media-query-parser.js";
2
+
3
+ function parseMediaListSafe(mediaText) {
4
+ return mediaQueryParser.parseMediaList(mediaText);
5
+ }
6
+
7
+ function containsNotKeyword(node) {
8
+ if (!node || !node.nodes) return false;
9
+ for (const token of node.nodes) {
10
+ if (token && token.type === "keyword" && token.value && token.value.toLowerCase() === "not") {
11
+ return true;
12
+ }
13
+ }
14
+ return false;
15
+ }
16
+
17
+ function isMediaTypeNegated(parentNode, index) {
18
+ for (let j = index - 1; j >= 0; j--) {
19
+ const prev = parentNode.nodes[j];
20
+ if (!prev) continue;
21
+ if (prev.type === "operator" && prev.value === ",") {
22
+ break;
23
+ }
24
+ if (prev.type === "keyword" && prev.value && prev.value.toLowerCase() === "not") {
25
+ return true;
26
+ }
27
+ }
28
+ return false;
29
+ }
30
+
31
+ function extractMediaTypes(parentNode, mediaTypes = []) {
32
+ for (let index = 0; index < parentNode.nodes.length; index++) {
33
+ const node = parentNode.nodes[index];
34
+ if (node.type == "media-query") {
35
+ extractMediaTypes(node, mediaTypes);
36
+ continue;
37
+ }
38
+ if (node.type == "media-type") {
39
+ const negated = isMediaTypeNegated(parentNode, index);
40
+ mediaTypes.push({ not: negated, value: node.value });
41
+ }
42
+ }
43
+ return mediaTypes;
44
+ }
45
+
46
+ function isFeaturefulOrCompound(node) {
47
+ if (!node || !node.nodes) return false;
48
+ for (const token of node.nodes) {
49
+ if (!token) continue;
50
+ if (token.type === "media-feature-expression") return true;
51
+ if (token.type === "keyword" && token.value && token.value.toLowerCase() === "and") return true;
52
+ }
53
+ if (containsNotKeyword(node)) return true;
54
+ return false;
55
+ }
56
+
57
+ export {
58
+ parseMediaListSafe,
59
+ containsNotKeyword,
60
+ isMediaTypeNegated,
61
+ extractMediaTypes,
62
+ isFeaturefulOrCompound
63
+ };
@@ -22,8 +22,13 @@
22
22
  */
23
23
 
24
24
  import * as cssTree from "./../vendor/css-tree.js";
25
- import * as mediaQueryParser from "./../vendor/css-media-query-parser.js";
26
25
  import { flatten } from "./../core/helper.js";
26
+ import {
27
+ parseMediaListSafe,
28
+ containsNotKeyword as utilContainsNotKeyword,
29
+ extractMediaTypes,
30
+ isFeaturefulOrCompound
31
+ } from "./css-media-query-utils.js";
27
32
 
28
33
  const helper = {
29
34
  flatten
@@ -72,28 +77,26 @@ function processRules(cssRules, stats, keepPrintStyleSheets, removedRules = [])
72
77
  }
73
78
 
74
79
  function matchesMediaType(mediaText, keepPrintStyleSheets) {
75
- const foundMediaTypes = helper.flatten(mediaQueryParser.parseMediaList(mediaText).map(node => getMediaTypes(node)));
76
- return foundMediaTypes.find(mediaTypeInfo =>
77
- (!mediaTypeInfo.not && (mediaTypeInfo.value == MEDIA_SCREEN || mediaTypeInfo.value == MEDIA_ALL || (keepPrintStyleSheets && mediaTypeInfo.value == MEDIA_PRINT))) ||
78
- (mediaTypeInfo.not && (mediaTypeInfo.value != MEDIA_SCREEN || mediaText.includes(" and "))));
79
- }
80
-
81
- function getMediaTypes(parentNode, mediaTypes = []) {
82
- parentNode.nodes.map((node, indexNode) => {
83
- if (node.type == "media-query") {
84
- return getMediaTypes(node);
85
- } else {
86
- if (node.type == "media-type") {
87
- const nodeMediaType = {
88
- not: Boolean(indexNode && parentNode.nodes[0].type == "keyword" && parentNode.nodes[0].value == "not"),
89
- value: node.value
90
- };
91
- mediaTypes.push(nodeMediaType);
92
- }
80
+ let parsed;
81
+ try {
82
+ parsed = parseMediaListSafe(mediaText);
83
+ for (const node of parsed) {
84
+ if (!node || !node.nodes) continue;
85
+ if (isFeaturefulOrCompound(node)) return true;
86
+ if (utilContainsNotKeyword(node)) return true;
93
87
  }
94
- });
95
- if (!mediaTypes.length) {
96
- mediaTypes.push({ not: false, value: MEDIA_ALL });
88
+ } catch {
89
+ return true;
90
+ }
91
+ let foundMediaTypes = helper.flatten(parsed.filter(n => n && n.nodes).map(node => extractMediaTypes(node)));
92
+ if (!foundMediaTypes || !foundMediaTypes.length) {
93
+ foundMediaTypes = [{ not: false, value: MEDIA_ALL }];
97
94
  }
98
- return mediaTypes;
95
+ return foundMediaTypes.some(mediaTypeInfo =>
96
+ !mediaTypeInfo.not && (
97
+ mediaTypeInfo.value == MEDIA_SCREEN ||
98
+ mediaTypeInfo.value == MEDIA_ALL ||
99
+ (keepPrintStyleSheets && mediaTypeInfo.value == MEDIA_PRINT)
100
+ )
101
+ );
99
102
  }