single-file-core 1.0.8 → 1.0.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.
@@ -212,11 +212,11 @@ async function processFontFaceRule(ruleData, fontInfo, fontDeclarations, fontTes
212
212
  removedNodes.forEach(node => ruleData.block.children.remove(node));
213
213
  const srcDeclaration = ruleData.block.children.filter(node => node.property == "src").tail;
214
214
  if (srcDeclaration) {
215
- await Promise.all(fontInfo.map(async (source, sourceIndex) => {
215
+ await Promise.all(fontInfo.map(async source => {
216
216
  if (fontTests.has(source.src)) {
217
217
  source.valid = fontTests.get(source.src);
218
218
  } else {
219
- if (FontFace) {
219
+ if (FontFace && source.fontUrl) {
220
220
  const fontFace = new FontFace("test-font", source.src);
221
221
  try {
222
222
  let timeout;
@@ -225,9 +225,10 @@ async function processFontFaceRule(ruleData, fontInfo, fontDeclarations, fontTes
225
225
  new Promise(resolve => timeout = globalThis.setTimeout(() => { source.valid = true; resolve(); }, FONT_MAX_LOAD_DELAY))
226
226
  ]);
227
227
  } catch (error) {
228
- const declarationFontURLs = fontDeclarations.get(srcDeclaration.data);
229
- if (declarationFontURLs) {
230
- const fontURL = declarationFontURLs[declarationFontURLs.length - sourceIndex - 1];
228
+ const urlNodes = cssTree.findAll(srcDeclaration.data, node => node.type == "Url");
229
+ const declarationFontURLs = Array.from(fontDeclarations).find(([node]) => urlNodes.includes(node) && node.value == source.fontUrl)[1];
230
+ if (declarationFontURLs.length) {
231
+ const fontURL = declarationFontURLs[0];
231
232
  if (fontURL) {
232
233
  const fontFace = new FontFace("test-font", "url(" + fontURL + ")");
233
234
  try {
@@ -276,7 +277,7 @@ async function processFontFaceRule(ruleData, fontInfo, fontDeclarations, fontTes
276
277
  stats.fonts.discarded -= fontInfo.length;
277
278
  fontInfo.reverse();
278
279
  try {
279
- srcDeclaration.data.value = cssTree.parse(fontInfo.map(fontSource => fontSource.src).join(","), { context: "value" });
280
+ srcDeclaration.data.value = cssTree.parse(fontInfo.map(fontSource => fontSource.src).join(","), { context: "value", parseCustomProperty: true });
280
281
  }
281
282
  catch (error) {
282
283
  // ignored
@@ -333,6 +334,7 @@ function createFontsDetailsInfo() {
333
334
  }
334
335
 
335
336
  function getURL(urlFunction) {
337
+ urlFunction = urlFunction.replace(/url\(-sf-url-original\\\(\\"(.*?)\\"\\\)\\ /g, "");
336
338
  const urlMatch = urlFunction.match(REGEXP_URL_SIMPLE_QUOTES_FN) ||
337
339
  urlFunction.match(REGEXP_URL_DOUBLE_QUOTES_FN) ||
338
340
  urlFunction.match(REGEXP_URL_NO_QUOTES_FN);
@@ -220,13 +220,19 @@ function getFontFamilyNames(declarations) {
220
220
  if (fontFamilyName) {
221
221
  let familyName = "";
222
222
  if (fontFamilyName.data.value.children) {
223
+ let previousType;
223
224
  fontFamilyName.data.value.children.forEach(node => {
224
225
  if (node.type == "Operator" && node.value == "," && familyName) {
225
226
  fontFamilyNames.push(helper.normalizeFontFamily(familyName));
226
227
  familyName = "";
228
+ previousType = null;
227
229
  } else {
230
+ if (previousType == "Identifier" && node.type == "Identifier") {
231
+ familyName += " ";
232
+ }
228
233
  familyName += cssTree.generate(node);
229
234
  }
235
+ previousType = node.type;
230
236
  });
231
237
  } else {
232
238
  fontFamilyName = cssTree.generate(fontFamilyName.data.value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "single-file-core",
3
- "version": "1.0.8",
3
+ "version": "1.0.11",
4
4
  "description": "SingleFile Core",
5
5
  "author": "Gildas Lormeau",
6
6
  "license": "AGPL-3.0-or-later",
@@ -1024,12 +1024,9 @@ class Processor {
1024
1024
 
1025
1025
  resolveStyleAttributeURLs() {
1026
1026
  this.doc.querySelectorAll("[style]").forEach(element => {
1027
- let styleContent = element.getAttribute("style");
1028
- if (this.options.compressCSS) {
1029
- styleContent = util.compressCSS(styleContent);
1030
- }
1031
- styleContent = ProcessorHelper.resolveStylesheetURLs(styleContent, this.baseURI, this.workStyleElement, this.options.saveOriginalURLs);
1032
- const declarationList = cssTree.parse(styleContent, { context: "declarationList" });
1027
+ const styleContent = element.getAttribute("style");
1028
+ const declarationList = cssTree.parse(styleContent, { context: "declarationList", parseCustomProperty: true });
1029
+ ProcessorHelper.resolveStylesheetURLs(declarationList, this.baseURI, this.workStyleElement);
1033
1030
  this.styles.set(element, declarationList);
1034
1031
  });
1035
1032
  }
@@ -1066,16 +1063,7 @@ class Processor {
1066
1063
  let stylesheet;
1067
1064
  stylesheets.set(element, stylesheetInfo);
1068
1065
  if (!options.blockStylesheets) {
1069
- let stylesheetContent = await getStylesheetContent(element, baseURI, options, workStyleElement);
1070
- if (!matchCharsetEquals(stylesheetContent, options.charset)) {
1071
- options = Object.assign({}, options, { charset: getCharset(stylesheetContent) });
1072
- stylesheetContent = await getStylesheetContent(element, baseURI, options, workStyleElement);
1073
- }
1074
- try {
1075
- stylesheet = cssTree.parse(removeCssComments(stylesheetContent));
1076
- } catch (error) {
1077
- // ignored
1078
- }
1066
+ stylesheet = await getStylesheet(element, baseURI, options, workStyleElement);
1079
1067
  }
1080
1068
  if (stylesheet && stylesheet.children) {
1081
1069
  if (options.compressCSS) {
@@ -1087,16 +1075,20 @@ class Processor {
1087
1075
  }
1088
1076
  }
1089
1077
 
1090
- async function getStylesheetContent(element, baseURI, options, workStyleElement) {
1091
- let content;
1078
+ async function getStylesheet(element, baseURI, options, workStyleElement) {
1079
+ let stylesheet;
1092
1080
  if (!options.blockStylesheets) {
1093
1081
  if (element.tagName == "LINK") {
1094
- content = await ProcessorHelper.resolveLinkStylesheetURLs(element.href, baseURI, options, workStyleElement);
1082
+ stylesheet = await ProcessorHelper.resolveLinkStylesheetURLs(element.href, baseURI, options, workStyleElement);
1095
1083
  } else {
1096
- content = await ProcessorHelper.resolveImportURLs(element.textContent, baseURI, options, workStyleElement);
1084
+ stylesheet = cssTree.parse(element.textContentcd, { context: "stylesheet", parseCustomProperty: true });
1085
+ const importFound = await ProcessorHelper.resolveImportURLs(stylesheet, baseURI, options, workStyleElement);
1086
+ if (importFound) {
1087
+ stylesheet = cssTree.parse(cssTree.generate(stylesheet), { context: "stylesheet", parseCustomProperty: true });
1088
+ }
1097
1089
  }
1098
1090
  }
1099
- return content || "";
1091
+ return stylesheet;
1100
1092
  }
1101
1093
  }
1102
1094
 
@@ -1108,7 +1100,7 @@ class Processor {
1108
1100
  frameElement.setAttribute("data", "data:text/html,");
1109
1101
  } else {
1110
1102
  const src = frameElement.getAttribute("src");
1111
- if (this.options.saveOriginalURLs && !isDataURL(src)) {
1103
+ if (this.options.saveOriginalURLs && src && !isDataURL(src)) {
1112
1104
  frameElement.setAttribute("data-sf-original-src", src);
1113
1105
  }
1114
1106
  frameElement.removeAttribute("src");
@@ -1275,8 +1267,8 @@ class Processor {
1275
1267
  }
1276
1268
 
1277
1269
  async processStyleAttributes() {
1278
- return Promise.all([...this.styles].map(([, declarationList]) =>
1279
- ProcessorHelper.processStyle(declarationList.children.toArray(), this.baseURI, this.options, this.cssVariables, this.batchRequest)
1270
+ return Promise.all([...this.styles].map(([, stylesheet]) =>
1271
+ ProcessorHelper.processStyle(stylesheet, this.baseURI, this.options, this.cssVariables, this.batchRequest)
1280
1272
  ));
1281
1273
  }
1282
1274
 
@@ -1426,11 +1418,7 @@ class Processor {
1426
1418
  const stylesheetInfo = this.stylesheets.get(styleElement);
1427
1419
  if (stylesheetInfo) {
1428
1420
  this.stylesheets.delete(styleElement);
1429
- let stylesheetContent = cssTree.generate(stylesheetInfo.stylesheet);
1430
- if (this.options.saveOriginalURLs) {
1431
- stylesheetContent = replaceOriginalURLs(stylesheetContent);
1432
- }
1433
- styleElement.textContent = stylesheetContent;
1421
+ styleElement.textContent = generateStylesheetContent(stylesheetInfo.stylesheet, this.options);
1434
1422
  if (stylesheetInfo.mediaText) {
1435
1423
  styleElement.media = stylesheetInfo.mediaText;
1436
1424
  }
@@ -1446,12 +1434,7 @@ class Processor {
1446
1434
  if (stylesheetInfo.mediaText) {
1447
1435
  styleElement.media = stylesheetInfo.mediaText;
1448
1436
  }
1449
- let stylesheetContent = cssTree.generate(stylesheetInfo.stylesheet);
1450
- if (this.options.saveOriginalURLs) {
1451
- stylesheetContent = replaceOriginalURLs(stylesheetContent);
1452
- styleElement.setAttribute("data-sf-original-href", linkElement.getAttribute("data-sf-original-href"));
1453
- }
1454
- styleElement.textContent = stylesheetContent;
1437
+ styleElement.textContent = generateStylesheetContent(stylesheetInfo.stylesheet, this.options);
1455
1438
  linkElement.parentElement.replaceChild(styleElement, linkElement);
1456
1439
  } else {
1457
1440
  linkElement.remove();
@@ -1461,14 +1444,10 @@ class Processor {
1461
1444
 
1462
1445
  replaceStyleAttributes() {
1463
1446
  this.doc.querySelectorAll("[style]").forEach(element => {
1464
- const declarations = this.styles.get(element);
1465
- if (declarations) {
1447
+ const declarationList = this.styles.get(element);
1448
+ if (declarationList) {
1466
1449
  this.styles.delete(element);
1467
- let styleContent = cssTree.generate(declarations);
1468
- if (this.options.saveOriginalURLs) {
1469
- styleContent = replaceOriginalURLs(styleContent);
1470
- }
1471
- element.setAttribute("style", styleContent);
1450
+ element.setAttribute("style", generateStylesheetContent(declarationList, this.options));
1472
1451
  } else {
1473
1452
  element.setAttribute("style", "");
1474
1453
  }
@@ -1708,52 +1687,43 @@ class ProcessorHelper {
1708
1687
  removedRules.forEach(cssRule => stylesheet.children.remove(cssRule));
1709
1688
  }
1710
1689
 
1711
- static async resolveImportURLs(stylesheetContent, baseURI, options, workStylesheet, importedStyleSheets = new Set()) {
1712
- stylesheetContent = ProcessorHelper.resolveStylesheetURLs(stylesheetContent, baseURI, workStylesheet, options.saveOriginalURLs);
1713
- const imports = getImportFunctions(stylesheetContent);
1714
- await Promise.all(imports.map(async cssImport => {
1715
- const match = matchImport(cssImport);
1716
- if (match) {
1717
- const regExpCssImport = getRegExp(cssImport);
1718
- let resourceURL = normalizeURL(match.resourceURL);
1719
- if (!testIgnoredPath(resourceURL) && testValidPath(resourceURL)) {
1720
- try {
1721
- resourceURL = util.resolveURL(match.resourceURL, baseURI);
1722
- } catch (error) {
1723
- // ignored
1690
+ static async resolveImportURLs(stylesheet, baseURI, options, workStylesheet, importedStyleSheets = new Set()) {
1691
+ let importFound;
1692
+ ProcessorHelper.resolveStylesheetURLs(stylesheet, baseURI, workStylesheet);
1693
+ const imports = getImportFunctions(stylesheet);
1694
+ await Promise.all(imports.map(async node => {
1695
+ const urlNode = cssTree.find(node, node => node.type == "Url") || cssTree.find(node, node => node.type == "String");
1696
+ let resourceURL = normalizeURL(urlNode.value);
1697
+ if (!testIgnoredPath(resourceURL) && testValidPath(resourceURL)) {
1698
+ urlNode.value = util.EMPTY_RESOURCE;
1699
+ try {
1700
+ resourceURL = util.resolveURL(resourceURL, baseURI);
1701
+ } catch (error) {
1702
+ // ignored
1703
+ }
1704
+ if (testValidURL(resourceURL) && !importedStyleSheets.has(resourceURL)) {
1705
+ const content = await getStylesheetContent(resourceURL);
1706
+ resourceURL = content.resourceURL;
1707
+ content.data = getUpdatedResourceContent(resourceURL, content, options);
1708
+ if (content.data && content.data.match(/^<!doctype /i)) {
1709
+ content.data = "";
1724
1710
  }
1725
- if (testValidURL(resourceURL) && !importedStyleSheets.has(resourceURL)) {
1726
- const content = await getStylesheetContent(resourceURL);
1727
- resourceURL = content.resourceURL;
1728
- content.data = getUpdatedResourceContent(resourceURL, content, options);
1729
- if (content.data && content.data.match(/^<!doctype /i)) {
1730
- content.data = "";
1731
- }
1732
- let importedStylesheetContent = removeCssComments(content.data);
1733
- if (options.compressCSS) {
1734
- importedStylesheetContent = util.compressCSS(importedStylesheetContent);
1735
- }
1736
- importedStylesheetContent = wrapMediaQuery(importedStylesheetContent, match.media);
1737
- if (stylesheetContent.includes(cssImport)) {
1738
- const ancestorStyleSheets = new Set(importedStyleSheets);
1739
- ancestorStyleSheets.add(resourceURL);
1740
- importedStylesheetContent = await ProcessorHelper.resolveImportURLs(importedStylesheetContent, resourceURL, options, workStylesheet, ancestorStyleSheets);
1741
- workStylesheet.textContent = importedStylesheetContent;
1742
- if ((workStylesheet.sheet && workStylesheet.sheet.cssRules.length) || (!workStylesheet.sheet && importedStylesheetContent)) {
1743
- stylesheetContent = stylesheetContent.replace(regExpCssImport, importedStylesheetContent);
1744
- } else {
1745
- stylesheetContent = stylesheetContent.replace(regExpCssImport, "");
1746
- }
1747
- }
1748
- } else {
1749
- stylesheetContent = stylesheetContent.replace(regExpCssImport, "");
1711
+ const mediaQueryListNode = cssTree.find(node, node => node.type == "MediaQueryList");
1712
+ if (mediaQueryListNode) {
1713
+ content.data = wrapMediaQuery(content.data, cssTree.generate(mediaQueryListNode));
1750
1714
  }
1751
- } else {
1752
- stylesheetContent = stylesheetContent.replace(regExpCssImport, "");
1715
+ const importedStylesheet = cssTree.parse(content.data, { context: "stylesheet", parseCustomProperty: true });
1716
+ const ancestorStyleSheets = new Set(importedStyleSheets);
1717
+ ancestorStyleSheets.add(resourceURL);
1718
+ await ProcessorHelper.resolveImportURLs(importedStylesheet, resourceURL, options, workStylesheet, ancestorStyleSheets);
1719
+ for (let keyName of Object.keys(importedStylesheet)) {
1720
+ node[keyName] = importedStylesheet[keyName];
1721
+ }
1722
+ importFound = true;
1753
1723
  }
1754
1724
  }
1755
1725
  }));
1756
- return stylesheetContent;
1726
+ return importFound;
1757
1727
 
1758
1728
  async function getStylesheetContent(resourceURL) {
1759
1729
  const content = await util.getContent(resourceURL, {
@@ -1790,13 +1760,10 @@ class ProcessorHelper {
1790
1760
  }
1791
1761
  }
1792
1762
 
1793
- static resolveStylesheetURLs(stylesheetContent, baseURI, workStylesheet, saveOriginalURLs) {
1794
- const urlFunctions = getUrlFunctions(stylesheetContent, true);
1795
- if (saveOriginalURLs) {
1796
- stylesheetContent = addOriginalURLs(stylesheetContent);
1797
- }
1798
- urlFunctions.map(urlFunction => {
1799
- const originalResourceURL = matchURL(urlFunction);
1763
+ static resolveStylesheetURLs(stylesheet, baseURI, workStylesheet) {
1764
+ const urls = getUrlFunctions(stylesheet);
1765
+ urls.map(urlNode => {
1766
+ const originalResourceURL = urlNode.value;
1800
1767
  let resourceURL = normalizeURL(originalResourceURL);
1801
1768
  workStylesheet.textContent = "tmp { content:\"" + resourceURL + "\"}";
1802
1769
  if (workStylesheet.sheet && workStylesheet.sheet.cssRules) {
@@ -1812,25 +1779,14 @@ class ProcessorHelper {
1812
1779
  // ignored
1813
1780
  }
1814
1781
  }
1815
- if (testValidURL(resolvedURL) && originalResourceURL != resolvedURL && stylesheetContent.includes(urlFunction)) {
1816
- try {
1817
- stylesheetContent = stylesheetContent.replace(getRegExp(urlFunction), originalResourceURL ? urlFunction.replace(originalResourceURL, resolvedURL) : "url(" + resolvedURL + ")");
1818
- } catch (error) {
1819
- // ignored
1820
- }
1782
+ if (testValidURL(resolvedURL)) {
1783
+ urlNode.value = resolvedURL;
1821
1784
  }
1822
1785
  } else {
1823
- let newUrlFunction;
1824
- if (originalResourceURL) {
1825
- newUrlFunction = urlFunction.replace(originalResourceURL, util.EMPTY_RESOURCE);
1826
- } else {
1827
- newUrlFunction = "url(" + util.EMPTY_RESOURCE + ")";
1828
- }
1829
- stylesheetContent = stylesheetContent.replace(getRegExp(urlFunction), newUrlFunction);
1786
+ urlNode.value = util.EMPTY_RESOURCE;
1830
1787
  }
1831
1788
  }
1832
1789
  });
1833
- return stylesheetContent;
1834
1790
  }
1835
1791
 
1836
1792
  static async resolveLinkStylesheetURLs(resourceURL, baseURI, options, workStylesheet) {
@@ -1858,12 +1814,12 @@ class ProcessorHelper {
1858
1814
  if (content.data && content.data.match(/^<!doctype /i)) {
1859
1815
  content.data = "";
1860
1816
  }
1861
- let stylesheetContent = removeCssComments(content.data);
1862
- if (options.compressCSS) {
1863
- stylesheetContent = util.compressCSS(stylesheetContent);
1817
+ let stylesheet = cssTree.parse(content.data, { context: "stylesheet", parseCustomProperty: true });
1818
+ const importFound = await ProcessorHelper.resolveImportURLs(stylesheet, resourceURL, options, workStylesheet);
1819
+ if (importFound) {
1820
+ stylesheet = cssTree.parse(cssTree.generate(stylesheet), { context: "stylesheet", parseCustomProperty: true });
1864
1821
  }
1865
- stylesheetContent = await ProcessorHelper.resolveImportURLs(stylesheetContent, resourceURL, options, workStylesheet);
1866
- return stylesheetContent;
1822
+ return stylesheet;
1867
1823
  }
1868
1824
  }
1869
1825
 
@@ -1876,7 +1832,7 @@ class ProcessorHelper {
1876
1832
  removedRules.push(cssRule);
1877
1833
  } else if (ruleData.block && ruleData.block.children) {
1878
1834
  if (ruleData.type == "Rule") {
1879
- promises.push(this.processStyle(ruleData.block.children.toArray(), baseURI, options, cssVariables, batchRequest));
1835
+ promises.push(this.processStyle(ruleData, baseURI, options, cssVariables, batchRequest));
1880
1836
  } else if (ruleData.type == "Atrule" && (ruleData.name == "media" || ruleData.name == "supports")) {
1881
1837
  promises.push(this.processStylesheet(ruleData.block.children, baseURI, options, cssVariables, batchRequest));
1882
1838
  } else if (ruleData.type == "Atrule" && ruleData.name == "font-face") {
@@ -1888,94 +1844,62 @@ class ProcessorHelper {
1888
1844
  await Promise.all(promises);
1889
1845
 
1890
1846
  async function processFontFaceRule(ruleData) {
1891
- await Promise.all(ruleData.block.children.toArray().map(async declaration => {
1892
- if (declaration.type == "Declaration" && declaration.value.children) {
1893
- const urlFunctions = getUrlFunctions(getCSSValue(declaration.value), true);
1894
- await Promise.all(urlFunctions.map(async urlFunction => {
1895
- const originalResourceURL = matchURL(urlFunction);
1896
- if (!options.blockFonts) {
1897
- const resourceURL = normalizeURL(originalResourceURL);
1898
- if (!testIgnoredPath(resourceURL) && testValidURL(resourceURL)) {
1899
- let { content } = await batchRequest.addURL(resourceURL,
1900
- { asBinary: true, expectedType: "font", baseURI, blockMixedContent: options.blockMixedContent });
1901
- let resourceURLs = options.fontDeclarations.get(declaration);
1902
- if (!resourceURLs) {
1903
- resourceURLs = [];
1904
- options.fontDeclarations.set(declaration, resourceURLs);
1905
- }
1906
- resourceURLs.push(resourceURL);
1907
- replaceURLs(declaration, originalResourceURL, content);
1908
- }
1847
+ const urls = getUrlFunctions(ruleData);
1848
+ await Promise.all(urls.map(async urlNode => {
1849
+ const originalResourceURL = urlNode.value;
1850
+ if (!options.blockFonts) {
1851
+ const resourceURL = normalizeURL(originalResourceURL);
1852
+ if (!testIgnoredPath(resourceURL) && testValidURL(resourceURL)) {
1853
+ let { content } = await batchRequest.addURL(resourceURL,
1854
+ { asBinary: true, expectedType: "font", baseURI, blockMixedContent: options.blockMixedContent });
1855
+ let resourceURLs = options.fontDeclarations.get(urlNode);
1856
+ if (!resourceURLs) {
1857
+ resourceURLs = [];
1858
+ options.fontDeclarations.set(urlNode, resourceURLs);
1859
+ }
1860
+ resourceURLs.push(resourceURL);
1861
+ if (!isDataURL(resourceURL) && options.saveOriginalURLs) {
1862
+ urlNode.value = "-sf-url-original(" + JSON.stringify(originalResourceURL) + ") " + content;
1909
1863
  } else {
1910
- replaceURLs(declaration, originalResourceURL, util.EMPTY_RESOURCE);
1864
+ urlNode.value = content;
1911
1865
  }
1912
- }));
1866
+ }
1867
+ } else {
1868
+ urlNode.value = util.EMPTY_RESOURCE;
1913
1869
  }
1914
1870
  }));
1915
-
1916
- function replaceURLs(declaration, oldURL, newURL) {
1917
- declaration.value.children.forEach(token => {
1918
- if (token.type == "Url" && util.removeQuotes(getCSSValue(token.value)) == oldURL) {
1919
- token.value = newURL;
1920
- }
1921
- });
1922
- }
1923
1871
  }
1924
1872
  }
1925
1873
 
1926
- static async processStyle(declarations, baseURI, options, cssVariables, batchRequest) {
1927
- await Promise.all(declarations.map(async declaration => {
1928
- if (declaration.value && !declaration.value.children && declaration.value.type == "Raw") {
1929
- try {
1930
- declaration.value = cssTree.parse(declaration.value.value, { context: "value" });
1931
- } catch (error) {
1932
- // ignored
1933
- }
1934
- }
1935
- if (declaration.type == "Declaration" && declaration.value.children) {
1936
- const urlFunctions = getUrlFunctions(getCSSValue(declaration.value));
1937
- await Promise.all(urlFunctions.map(async urlFunction => {
1938
- const originalResourceURL = matchURL(urlFunction);
1939
- if (!options.blockImages) {
1940
- const resourceURL = normalizeURL(originalResourceURL);
1941
- if (!testIgnoredPath(resourceURL) && testValidURL(resourceURL)) {
1942
- let { content, indexResource, duplicate } = await batchRequest.addURL(resourceURL,
1943
- { asBinary: true, expectedType: "image", groupDuplicates: options.groupDuplicateImages });
1944
- let variableDefined;
1945
- const tokens = [];
1946
- findURLToken(originalResourceURL, declaration.value.children, (token, parent, rootFunction) => {
1947
- if (!originalResourceURL.startsWith("#")) {
1948
- if (duplicate && options.groupDuplicateImages && rootFunction && util.getContentSize(content) < SINGLE_FILE_VARIABLE_MAX_SIZE) {
1949
- const value = cssTree.parse("var(" + SINGLE_FILE_VARIABLE_NAME_PREFIX + indexResource + ")", { context: "value" }).children.head;
1950
- tokens.push({ parent, token, value });
1951
- variableDefined = true;
1952
- } else {
1953
- token.data.value = content;
1954
- }
1955
- }
1956
- });
1957
- if (variableDefined) {
1958
- cssVariables.set(indexResource, { content, url: originalResourceURL });
1959
- tokens.forEach(({ parent, token, value }) => parent.replace(token, value));
1874
+ static async processStyle(ruleData, baseURI, options, cssVariables, batchRequest) {
1875
+ const urls = getUrlFunctions(ruleData);
1876
+ await Promise.all(urls.map(async urlNode => {
1877
+ const originalResourceURL = urlNode.value;
1878
+ if (!options.blockImages) {
1879
+ const resourceURL = normalizeURL(originalResourceURL);
1880
+ if (!testIgnoredPath(resourceURL) && testValidURL(resourceURL)) {
1881
+ let { content, indexResource, duplicate } = await batchRequest.addURL(resourceURL,
1882
+ { asBinary: true, expectedType: "image", groupDuplicates: options.groupDuplicateImages });
1883
+ if (!originalResourceURL.startsWith("#")) {
1884
+ if (duplicate && options.groupDuplicateImages && util.getContentSize(content) < SINGLE_FILE_VARIABLE_MAX_SIZE) {
1885
+ const varNode = cssTree.parse("var(" + SINGLE_FILE_VARIABLE_NAME_PREFIX + indexResource + ")", { context: "value" });
1886
+ for (let keyName of Object.keys(varNode.children.head.data)) {
1887
+ urlNode[keyName] = varNode.children.head.data[keyName];
1888
+ }
1889
+ cssVariables.set(indexResource, { content, url: originalResourceURL });
1890
+ } else {
1891
+ if (!isDataURL(resourceURL) && options.saveOriginalURLs) {
1892
+ urlNode.value = "-sf-url-original(" + JSON.stringify(originalResourceURL) + ") " + content;
1893
+ } else {
1894
+ urlNode.value = content;
1960
1895
  }
1961
1896
  }
1962
- } else {
1963
- findURLToken(originalResourceURL, declaration.value.children, token => token.data.value = util.EMPTY_RESOURCE);
1964
1897
  }
1965
- }));
1966
- }
1967
- }));
1968
-
1969
- function findURLToken(url, children, callback, depth = 0) {
1970
- for (let token = children.head; token; token = token.next) {
1971
- if (token.data.children) {
1972
- findURLToken(url, token.data.children, callback, depth + 1);
1973
- }
1974
- if (token.data.type == "Url" && util.removeQuotes(getCSSValue(token.data.value)) == url) {
1975
- callback(token, children, depth == 0);
1976
1898
  }
1899
+ } else {
1900
+ urlNode.value = util.EMPTY_RESOURCE;
1977
1901
  }
1978
- }
1902
+ }));
1979
1903
  }
1980
1904
 
1981
1905
  static async processAttribute(resourceElements, attributeName, baseURI, options, expectedType, cssVariables, styles, batchRequest, processDuplicates, removeElementIfMissing) {
@@ -2033,7 +1957,7 @@ class ProcessorHelper {
2033
1957
  if (expectedType == "image" && processDuplicates && duplicate && !isSVG && util.getContentSize(content) < SINGLE_FILE_VARIABLE_MAX_SIZE) {
2034
1958
  if (ProcessorHelper.replaceImageSource(resourceElement, SINGLE_FILE_VARIABLE_NAME_PREFIX + indexResource, options)) {
2035
1959
  cssVariables.set(indexResource, { content, url: originURL });
2036
- const declarationList = cssTree.parse(resourceElement.getAttribute("style"), { context: "declarationList" });
1960
+ const declarationList = cssTree.parse(resourceElement.getAttribute("style"), { context: "declarationList", parseCustomProperty: true });
2037
1961
  styles.set(resourceElement, declarationList);
2038
1962
  } else {
2039
1963
  resourceElement.setAttribute(attributeName, content);
@@ -2188,18 +2112,6 @@ const HTTP_URI_PREFIX = /^https?:\/\//;
2188
2112
  const FILE_URI_PREFIX = /^file:\/\//;
2189
2113
  const EMPTY_URL = /^https?:\/\/+\s*$/;
2190
2114
  const NOT_EMPTY_URL = /^(https?:\/\/|file:\/\/|blob:).+/;
2191
- const REGEXP_URL_FN = /(url\s*\(\s*'(.*?)'\s*\))|(url\s*\(\s*"(.*?)"\s*\))|(url\s*\(\s*(.*?)\s*\))/gi;
2192
- const REGEXP_URL_SIMPLE_QUOTES_FN = /^url\s*\(\s*'(.*?)'\s*\)$/i;
2193
- const REGEXP_URL_DOUBLE_QUOTES_FN = /^url\s*\(\s*"(.*?)"\s*\)$/i;
2194
- const REGEXP_URL_NO_QUOTES_FN = /^url\s*\(\s*(.*?)\s*\)$/i;
2195
- const REGEXP_IMPORT_FN = /(@import\s*url\s*\(\s*'(.*?)'\s*\)\s*(.*?)(;|$|}))|(@import\s*url\s*\(\s*"(.*?)"\s*\)\s*(.*?)(;|$|}))|(@import\s*url\s*\(\s*(.*?)\s*\)\s*(.*?)(;|$|}))|(@import\s*'(.*?)'\s*(.*?)(;|$|}))|(@import\s*"(.*?)"\s*(.*?)(;|$|}))|(@import\s*(.*?)\s*(.*?)(;|$|}))/gi;
2196
- const REGEXP_IMPORT_URL_SIMPLE_QUOTES_FN = /@import\s*url\s*\(\s*'(.*?)'\s*\)\s*(.*?)(;|$|})/i;
2197
- const REGEXP_IMPORT_URL_DOUBLE_QUOTES_FN = /@import\s*url\s*\(\s*"(.*?)"\s*\)\s*(.*?)(;|$|})/i;
2198
- const REGEXP_IMPORT_URL_NO_QUOTES_FN = /@import\s*url\s*\(\s*(.*?)\s*\)\s*(.*?)(;|$|})/i;
2199
- const REGEXP_IMPORT_SIMPLE_QUOTES_FN = /@import\s*'(.*?)'\s*(.*?)(;|$|})/i;
2200
- const REGEXP_IMPORT_DOUBLE_QUOTES_FN = /@import\s*"(.*?)"\s*(.*?)(;|$|})/i;
2201
- const REGEXP_IMPORT_NO_QUOTES_FN = /@import\s*(.*?)\s*(.*?)(;|$|})/i;
2202
- const REGEXP_ESCAPE = /([{}()^$&.*?/+|[\\\\]|\]|-)/g;
2203
2115
 
2204
2116
  function getUpdatedResourceContent(resourceURL, content, options) {
2205
2117
  if (options.rootDocument && options.updatedResources[resourceURL]) {
@@ -2218,20 +2130,6 @@ function normalizeURL(url) {
2218
2130
  }
2219
2131
  }
2220
2132
 
2221
- function getCSSValue(value) {
2222
- if (typeof value == "string") {
2223
- return value;
2224
- } else {
2225
- let result = "";
2226
- try {
2227
- result = cssTree.generate(value);
2228
- } catch (error) {
2229
- // ignored
2230
- }
2231
- return result;
2232
- }
2233
- }
2234
-
2235
2133
  function matchCharsetEquals(stylesheetContent, charset = UTF8_CHARSET) {
2236
2134
  const stylesheetCharset = getCharset(stylesheetContent);
2237
2135
  if (stylesheetCharset) {
@@ -2318,21 +2216,23 @@ function getLastSegment(url, replacementCharacter) {
2318
2216
  return lastSegment;
2319
2217
  }
2320
2218
 
2321
- function getRegExp(string) {
2322
- return new RegExp(string.replace(REGEXP_ESCAPE, "\\$1"), "gi");
2219
+ function getUrlFunctions(declarationList) {
2220
+ return cssTree.findAll(declarationList, node => node.type == "Url");
2323
2221
  }
2324
2222
 
2325
- function getUrlFunctions(stylesheetContent, unique) {
2326
- const result = stylesheetContent.match(REGEXP_URL_FN) || [];
2327
- if (unique) {
2328
- return [...new Set(result)];
2329
- } else {
2330
- return result;
2331
- }
2223
+ function getImportFunctions(declarationList) {
2224
+ return cssTree.findAll(declarationList, node => node.type == "Atrule" && node.name == "import");
2332
2225
  }
2333
2226
 
2334
- function getImportFunctions(stylesheetContent) {
2335
- return stylesheetContent.match(REGEXP_IMPORT_FN) || [];
2227
+ function generateStylesheetContent(stylesheet, options) {
2228
+ let stylesheetContent = cssTree.generate(stylesheet);
2229
+ if (options.compressCSS) {
2230
+ stylesheetContent = util.compressCSS(stylesheetContent);
2231
+ }
2232
+ if (options.saveOriginalURLs) {
2233
+ stylesheetContent = replaceOriginalURLs(stylesheetContent);
2234
+ }
2235
+ return stylesheetContent;
2336
2236
  }
2337
2237
 
2338
2238
  function findShortcutIcon(shortcutIcons) {
@@ -2341,30 +2241,12 @@ function findShortcutIcon(shortcutIcons) {
2341
2241
  return shortcutIcons[0];
2342
2242
  }
2343
2243
 
2344
- function matchURL(stylesheetContent) {
2345
- const match = stylesheetContent.match(REGEXP_URL_SIMPLE_QUOTES_FN) ||
2346
- stylesheetContent.match(REGEXP_URL_DOUBLE_QUOTES_FN) ||
2347
- stylesheetContent.match(REGEXP_URL_NO_QUOTES_FN);
2348
- return match && match[1];
2349
- }
2350
-
2351
- function addOriginalURLs(stylesheetContent) {
2352
- return stylesheetContent.replace(REGEXP_URL_FN, function (match, _0, url, _1, url2, _2, url3) {
2353
- url = url || url2 || url3;
2354
- if (isDataURL(url)) {
2355
- return match;
2356
- } else {
2357
- return "-sf-url-original(" + JSON.stringify(url) + ") " + match;
2358
- }
2359
- });
2360
- }
2361
-
2362
2244
  function isDataURL(url) {
2363
2245
  return url && (url.startsWith(DATA_URI_PREFIX) || url.startsWith(BLOB_URI_PREFIX));
2364
2246
  }
2365
2247
 
2366
2248
  function replaceOriginalURLs(stylesheetContent) {
2367
- return stylesheetContent.replace(/-sf-url-original\("(.*?)"\)/g, "/* original URL: $1 */");
2249
+ return stylesheetContent.replace(/url\(-sf-url-original\\\(\\"(.*?)\\"\\\)\\ /g, "/* original URL: $1 */url(");
2368
2250
  }
2369
2251
 
2370
2252
  function testIgnoredPath(resourceURL) {
@@ -2379,35 +2261,6 @@ function testValidURL(resourceURL) {
2379
2261
  return testValidPath(resourceURL) && (resourceURL.match(HTTP_URI_PREFIX) || resourceURL.match(FILE_URI_PREFIX) || resourceURL.startsWith(BLOB_URI_PREFIX)) && resourceURL.match(NOT_EMPTY_URL);
2380
2262
  }
2381
2263
 
2382
- function matchImport(stylesheetContent) {
2383
- const match = stylesheetContent.match(REGEXP_IMPORT_URL_SIMPLE_QUOTES_FN) ||
2384
- stylesheetContent.match(REGEXP_IMPORT_URL_DOUBLE_QUOTES_FN) ||
2385
- stylesheetContent.match(REGEXP_IMPORT_URL_NO_QUOTES_FN) ||
2386
- stylesheetContent.match(REGEXP_IMPORT_SIMPLE_QUOTES_FN) ||
2387
- stylesheetContent.match(REGEXP_IMPORT_DOUBLE_QUOTES_FN) ||
2388
- stylesheetContent.match(REGEXP_IMPORT_NO_QUOTES_FN);
2389
- if (match) {
2390
- const [, resourceURL, media] = match;
2391
- return { resourceURL, media };
2392
- }
2393
- }
2394
-
2395
- function removeCssComments(stylesheetContent) {
2396
- try {
2397
- return stylesheetContent.replace(/\/\*(.|[\r\n])*?\*\//g, "");
2398
- } catch (error) {
2399
- let start, end;
2400
- do {
2401
- start = stylesheetContent.indexOf("/*");
2402
- end = stylesheetContent.indexOf("*/", start + 2);
2403
- if (start != -1 && end != -1) {
2404
- stylesheetContent = stylesheetContent.substring(0, start) + stylesheetContent.substr(end + 2);
2405
- }
2406
- } while (start != -1 && end != -1);
2407
- return stylesheetContent;
2408
- }
2409
- }
2410
-
2411
2264
  function wrapMediaQuery(stylesheetContent, mediaQuery) {
2412
2265
  if (mediaQuery) {
2413
2266
  return "@media " + mediaQuery + "{ " + stylesheetContent + " }";
@@ -273,7 +273,11 @@ function getResourcesInfo(win, doc, element, options, data, elementHidden, compu
273
273
  element.setAttribute(IMAGE_ATTRIBUTE_NAME, data.images.length - 1);
274
274
  data.markedElements.push(element);
275
275
  element.removeAttribute(LAZY_SRC_ATTRIBUTE_NAME);
276
- computedStyle = computedStyle || win.getComputedStyle(element);
276
+ try {
277
+ computedStyle = computedStyle || win.getComputedStyle(element);
278
+ } catch (error) {
279
+ // ignored
280
+ }
277
281
  if (computedStyle) {
278
282
  imageData.size = getSize(win, element, computedStyle);
279
283
  const boxShadow = computedStyle.getPropertyValue("box-shadow");