single-file-core 1.0.7 → 1.0.10
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
|
|
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
|
|
229
|
-
|
|
230
|
-
|
|
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.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "SingleFile Core",
|
|
5
5
|
"author": "Gildas Lormeau",
|
|
6
6
|
"license": "AGPL-3.0-or-later",
|
|
@@ -15,4 +15,4 @@
|
|
|
15
15
|
"url": "https://github.com/gildas-lormeau/single-file-core/issues"
|
|
16
16
|
},
|
|
17
17
|
"homepage": "https://github.com/gildas-lormeau/single-file-core#readme"
|
|
18
|
-
}
|
|
18
|
+
}
|
package/single-file-core.js
CHANGED
|
@@ -1024,12 +1024,9 @@ class Processor {
|
|
|
1024
1024
|
|
|
1025
1025
|
resolveStyleAttributeURLs() {
|
|
1026
1026
|
this.doc.querySelectorAll("[style]").forEach(element => {
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
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
|
-
|
|
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,17 @@ class Processor {
|
|
|
1087
1075
|
}
|
|
1088
1076
|
}
|
|
1089
1077
|
|
|
1090
|
-
async function
|
|
1091
|
-
let
|
|
1078
|
+
async function getStylesheet(element, baseURI, options, workStyleElement) {
|
|
1079
|
+
let stylesheet;
|
|
1092
1080
|
if (!options.blockStylesheets) {
|
|
1093
1081
|
if (element.tagName == "LINK") {
|
|
1094
|
-
|
|
1082
|
+
stylesheet = await ProcessorHelper.resolveLinkStylesheetURLs(element.href, baseURI, options, workStyleElement);
|
|
1095
1083
|
} else {
|
|
1096
|
-
|
|
1084
|
+
stylesheet = cssTree.parse(element.textContent, { context: "stylesheet", parseCustomProperty: true });
|
|
1085
|
+
await ProcessorHelper.resolveImportURLs(stylesheet, baseURI, options, workStyleElement);
|
|
1097
1086
|
}
|
|
1098
1087
|
}
|
|
1099
|
-
return
|
|
1088
|
+
return stylesheet;
|
|
1100
1089
|
}
|
|
1101
1090
|
}
|
|
1102
1091
|
|
|
@@ -1108,7 +1097,7 @@ class Processor {
|
|
|
1108
1097
|
frameElement.setAttribute("data", "data:text/html,");
|
|
1109
1098
|
} else {
|
|
1110
1099
|
const src = frameElement.getAttribute("src");
|
|
1111
|
-
if (this.options.saveOriginalURLs && !isDataURL(src)) {
|
|
1100
|
+
if (this.options.saveOriginalURLs && src && !isDataURL(src)) {
|
|
1112
1101
|
frameElement.setAttribute("data-sf-original-src", src);
|
|
1113
1102
|
}
|
|
1114
1103
|
frameElement.removeAttribute("src");
|
|
@@ -1275,8 +1264,8 @@ class Processor {
|
|
|
1275
1264
|
}
|
|
1276
1265
|
|
|
1277
1266
|
async processStyleAttributes() {
|
|
1278
|
-
return Promise.all([...this.styles].map(([,
|
|
1279
|
-
ProcessorHelper.processStyle(
|
|
1267
|
+
return Promise.all([...this.styles].map(([, stylesheet]) =>
|
|
1268
|
+
ProcessorHelper.processStyle(stylesheet, this.baseURI, this.options, this.cssVariables, this.batchRequest)
|
|
1280
1269
|
));
|
|
1281
1270
|
}
|
|
1282
1271
|
|
|
@@ -1426,11 +1415,7 @@ class Processor {
|
|
|
1426
1415
|
const stylesheetInfo = this.stylesheets.get(styleElement);
|
|
1427
1416
|
if (stylesheetInfo) {
|
|
1428
1417
|
this.stylesheets.delete(styleElement);
|
|
1429
|
-
|
|
1430
|
-
if (this.options.saveOriginalURLs) {
|
|
1431
|
-
stylesheetContent = replaceOriginalURLs(stylesheetContent);
|
|
1432
|
-
}
|
|
1433
|
-
styleElement.textContent = stylesheetContent;
|
|
1418
|
+
styleElement.textContent = generateStylesheetContent(stylesheetInfo.stylesheet, this.options);
|
|
1434
1419
|
if (stylesheetInfo.mediaText) {
|
|
1435
1420
|
styleElement.media = stylesheetInfo.mediaText;
|
|
1436
1421
|
}
|
|
@@ -1446,12 +1431,7 @@ class Processor {
|
|
|
1446
1431
|
if (stylesheetInfo.mediaText) {
|
|
1447
1432
|
styleElement.media = stylesheetInfo.mediaText;
|
|
1448
1433
|
}
|
|
1449
|
-
|
|
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;
|
|
1434
|
+
styleElement.textContent = generateStylesheetContent(stylesheetInfo.stylesheet, this.options);
|
|
1455
1435
|
linkElement.parentElement.replaceChild(styleElement, linkElement);
|
|
1456
1436
|
} else {
|
|
1457
1437
|
linkElement.remove();
|
|
@@ -1461,14 +1441,10 @@ class Processor {
|
|
|
1461
1441
|
|
|
1462
1442
|
replaceStyleAttributes() {
|
|
1463
1443
|
this.doc.querySelectorAll("[style]").forEach(element => {
|
|
1464
|
-
const
|
|
1465
|
-
if (
|
|
1444
|
+
const declarationList = this.styles.get(element);
|
|
1445
|
+
if (declarationList) {
|
|
1466
1446
|
this.styles.delete(element);
|
|
1467
|
-
|
|
1468
|
-
if (this.options.saveOriginalURLs) {
|
|
1469
|
-
styleContent = replaceOriginalURLs(styleContent);
|
|
1470
|
-
}
|
|
1471
|
-
element.setAttribute("style", styleContent);
|
|
1447
|
+
element.setAttribute("style", generateStylesheetContent(declarationList, this.options));
|
|
1472
1448
|
} else {
|
|
1473
1449
|
element.setAttribute("style", "");
|
|
1474
1450
|
}
|
|
@@ -1708,52 +1684,40 @@ class ProcessorHelper {
|
|
|
1708
1684
|
removedRules.forEach(cssRule => stylesheet.children.remove(cssRule));
|
|
1709
1685
|
}
|
|
1710
1686
|
|
|
1711
|
-
static async resolveImportURLs(
|
|
1712
|
-
|
|
1713
|
-
const imports = getImportFunctions(
|
|
1714
|
-
await Promise.all(imports.map(async
|
|
1715
|
-
const
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1687
|
+
static async resolveImportURLs(stylesheet, baseURI, options, workStylesheet, importedStyleSheets = new Set()) {
|
|
1688
|
+
ProcessorHelper.resolveStylesheetURLs(stylesheet, baseURI, workStylesheet);
|
|
1689
|
+
const imports = getImportFunctions(stylesheet);
|
|
1690
|
+
await Promise.all(imports.map(async node => {
|
|
1691
|
+
const urlNode = cssTree.find(node, node => node.type == "Url") || cssTree.find(node, node => node.type == "String");
|
|
1692
|
+
let resourceURL = normalizeURL(urlNode.value);
|
|
1693
|
+
if (!testIgnoredPath(resourceURL) && testValidPath(resourceURL)) {
|
|
1694
|
+
urlNode.value = util.EMPTY_RESOURCE;
|
|
1695
|
+
try {
|
|
1696
|
+
resourceURL = util.resolveURL(resourceURL, baseURI);
|
|
1697
|
+
} catch (error) {
|
|
1698
|
+
// ignored
|
|
1699
|
+
}
|
|
1700
|
+
if (testValidURL(resourceURL) && !importedStyleSheets.has(resourceURL)) {
|
|
1701
|
+
const content = await getStylesheetContent(resourceURL);
|
|
1702
|
+
resourceURL = content.resourceURL;
|
|
1703
|
+
content.data = getUpdatedResourceContent(resourceURL, content, options);
|
|
1704
|
+
if (content.data && content.data.match(/^<!doctype /i)) {
|
|
1705
|
+
content.data = "";
|
|
1724
1706
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
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, "");
|
|
1707
|
+
const mediaQueryListNode = cssTree.find(node, node => node.type == "MediaQueryList");
|
|
1708
|
+
if (mediaQueryListNode) {
|
|
1709
|
+
content.data = wrapMediaQuery(content.data, cssTree.generate(mediaQueryListNode));
|
|
1710
|
+
}
|
|
1711
|
+
const stylesheet = cssTree.parse(content.data, { context: "stylesheet", parseCustomProperty: true });
|
|
1712
|
+
const ancestorStyleSheets = new Set(importedStyleSheets);
|
|
1713
|
+
ancestorStyleSheets.add(resourceURL);
|
|
1714
|
+
await ProcessorHelper.resolveImportURLs(stylesheet, resourceURL, options, workStylesheet, ancestorStyleSheets);
|
|
1715
|
+
for (let keyName of Object.keys(stylesheet.children.head.data)) {
|
|
1716
|
+
node[keyName] = stylesheet.children.head.data[keyName];
|
|
1750
1717
|
}
|
|
1751
|
-
} else {
|
|
1752
|
-
stylesheetContent = stylesheetContent.replace(regExpCssImport, "");
|
|
1753
1718
|
}
|
|
1754
1719
|
}
|
|
1755
1720
|
}));
|
|
1756
|
-
return stylesheetContent;
|
|
1757
1721
|
|
|
1758
1722
|
async function getStylesheetContent(resourceURL) {
|
|
1759
1723
|
const content = await util.getContent(resourceURL, {
|
|
@@ -1790,13 +1754,10 @@ class ProcessorHelper {
|
|
|
1790
1754
|
}
|
|
1791
1755
|
}
|
|
1792
1756
|
|
|
1793
|
-
static resolveStylesheetURLs(
|
|
1794
|
-
const
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
}
|
|
1798
|
-
urlFunctions.map(urlFunction => {
|
|
1799
|
-
const originalResourceURL = matchURL(urlFunction);
|
|
1757
|
+
static resolveStylesheetURLs(stylesheet, baseURI, workStylesheet) {
|
|
1758
|
+
const urls = getUrlFunctions(stylesheet);
|
|
1759
|
+
urls.map(urlNode => {
|
|
1760
|
+
const originalResourceURL = urlNode.value;
|
|
1800
1761
|
let resourceURL = normalizeURL(originalResourceURL);
|
|
1801
1762
|
workStylesheet.textContent = "tmp { content:\"" + resourceURL + "\"}";
|
|
1802
1763
|
if (workStylesheet.sheet && workStylesheet.sheet.cssRules) {
|
|
@@ -1812,25 +1773,14 @@ class ProcessorHelper {
|
|
|
1812
1773
|
// ignored
|
|
1813
1774
|
}
|
|
1814
1775
|
}
|
|
1815
|
-
if (testValidURL(resolvedURL)
|
|
1816
|
-
|
|
1817
|
-
stylesheetContent = stylesheetContent.replace(getRegExp(urlFunction), originalResourceURL ? urlFunction.replace(originalResourceURL, resolvedURL) : "url(" + resolvedURL + ")");
|
|
1818
|
-
} catch (error) {
|
|
1819
|
-
// ignored
|
|
1820
|
-
}
|
|
1776
|
+
if (testValidURL(resolvedURL)) {
|
|
1777
|
+
urlNode.value = resolvedURL;
|
|
1821
1778
|
}
|
|
1822
1779
|
} else {
|
|
1823
|
-
|
|
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);
|
|
1780
|
+
urlNode.value = util.EMPTY_RESOURCE;
|
|
1830
1781
|
}
|
|
1831
1782
|
}
|
|
1832
1783
|
});
|
|
1833
|
-
return stylesheetContent;
|
|
1834
1784
|
}
|
|
1835
1785
|
|
|
1836
1786
|
static async resolveLinkStylesheetURLs(resourceURL, baseURI, options, workStylesheet) {
|
|
@@ -1858,12 +1808,9 @@ class ProcessorHelper {
|
|
|
1858
1808
|
if (content.data && content.data.match(/^<!doctype /i)) {
|
|
1859
1809
|
content.data = "";
|
|
1860
1810
|
}
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
}
|
|
1865
|
-
stylesheetContent = await ProcessorHelper.resolveImportURLs(stylesheetContent, resourceURL, options, workStylesheet);
|
|
1866
|
-
return stylesheetContent;
|
|
1811
|
+
const stylesheet = cssTree.parse(content.data, { context: "stylesheet", parseCustomProperty: true });
|
|
1812
|
+
await ProcessorHelper.resolveImportURLs(stylesheet, resourceURL, options, workStylesheet);
|
|
1813
|
+
return stylesheet;
|
|
1867
1814
|
}
|
|
1868
1815
|
}
|
|
1869
1816
|
|
|
@@ -1876,7 +1823,7 @@ class ProcessorHelper {
|
|
|
1876
1823
|
removedRules.push(cssRule);
|
|
1877
1824
|
} else if (ruleData.block && ruleData.block.children) {
|
|
1878
1825
|
if (ruleData.type == "Rule") {
|
|
1879
|
-
promises.push(this.processStyle(ruleData
|
|
1826
|
+
promises.push(this.processStyle(ruleData, baseURI, options, cssVariables, batchRequest));
|
|
1880
1827
|
} else if (ruleData.type == "Atrule" && (ruleData.name == "media" || ruleData.name == "supports")) {
|
|
1881
1828
|
promises.push(this.processStylesheet(ruleData.block.children, baseURI, options, cssVariables, batchRequest));
|
|
1882
1829
|
} else if (ruleData.type == "Atrule" && ruleData.name == "font-face") {
|
|
@@ -1888,94 +1835,62 @@ class ProcessorHelper {
|
|
|
1888
1835
|
await Promise.all(promises);
|
|
1889
1836
|
|
|
1890
1837
|
async function processFontFaceRule(ruleData) {
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
replaceURLs(declaration, originalResourceURL, content);
|
|
1908
|
-
}
|
|
1838
|
+
const urls = getUrlFunctions(ruleData);
|
|
1839
|
+
await Promise.all(urls.map(async urlNode => {
|
|
1840
|
+
const originalResourceURL = urlNode.value;
|
|
1841
|
+
if (!options.blockFonts) {
|
|
1842
|
+
const resourceURL = normalizeURL(originalResourceURL);
|
|
1843
|
+
if (!testIgnoredPath(resourceURL) && testValidURL(resourceURL)) {
|
|
1844
|
+
let { content } = await batchRequest.addURL(resourceURL,
|
|
1845
|
+
{ asBinary: true, expectedType: "font", baseURI, blockMixedContent: options.blockMixedContent });
|
|
1846
|
+
let resourceURLs = options.fontDeclarations.get(urlNode);
|
|
1847
|
+
if (!resourceURLs) {
|
|
1848
|
+
resourceURLs = [];
|
|
1849
|
+
options.fontDeclarations.set(urlNode, resourceURLs);
|
|
1850
|
+
}
|
|
1851
|
+
resourceURLs.push(resourceURL);
|
|
1852
|
+
if (!isDataURL(resourceURL) && options.saveOriginalURLs) {
|
|
1853
|
+
urlNode.value = "-sf-url-original(" + JSON.stringify(originalResourceURL) + ") " + content;
|
|
1909
1854
|
} else {
|
|
1910
|
-
|
|
1855
|
+
urlNode.value = content;
|
|
1911
1856
|
}
|
|
1912
|
-
}
|
|
1857
|
+
}
|
|
1858
|
+
} else {
|
|
1859
|
+
urlNode.value = util.EMPTY_RESOURCE;
|
|
1913
1860
|
}
|
|
1914
1861
|
}));
|
|
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
1862
|
}
|
|
1924
1863
|
}
|
|
1925
1864
|
|
|
1926
|
-
static async processStyle(
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
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));
|
|
1865
|
+
static async processStyle(ruleData, baseURI, options, cssVariables, batchRequest) {
|
|
1866
|
+
const urls = getUrlFunctions(ruleData);
|
|
1867
|
+
await Promise.all(urls.map(async urlNode => {
|
|
1868
|
+
const originalResourceURL = urlNode.value;
|
|
1869
|
+
if (!options.blockImages) {
|
|
1870
|
+
const resourceURL = normalizeURL(originalResourceURL);
|
|
1871
|
+
if (!testIgnoredPath(resourceURL) && testValidURL(resourceURL)) {
|
|
1872
|
+
let { content, indexResource, duplicate } = await batchRequest.addURL(resourceURL,
|
|
1873
|
+
{ asBinary: true, expectedType: "image", groupDuplicates: options.groupDuplicateImages });
|
|
1874
|
+
if (!originalResourceURL.startsWith("#")) {
|
|
1875
|
+
if (duplicate && options.groupDuplicateImages && util.getContentSize(content) < SINGLE_FILE_VARIABLE_MAX_SIZE) {
|
|
1876
|
+
const varNode = cssTree.parse("var(" + SINGLE_FILE_VARIABLE_NAME_PREFIX + indexResource + ")", { context: "value" });
|
|
1877
|
+
for (let keyName of Object.keys(varNode.children.head.data)) {
|
|
1878
|
+
urlNode[keyName] = varNode.children.head.data[keyName];
|
|
1879
|
+
}
|
|
1880
|
+
cssVariables.set(indexResource, { content, url: originalResourceURL });
|
|
1881
|
+
} else {
|
|
1882
|
+
if (!isDataURL(resourceURL) && options.saveOriginalURLs) {
|
|
1883
|
+
urlNode.value = "-sf-url-original(" + JSON.stringify(originalResourceURL) + ") " + content;
|
|
1884
|
+
} else {
|
|
1885
|
+
urlNode.value = content;
|
|
1960
1886
|
}
|
|
1961
1887
|
}
|
|
1962
|
-
} else {
|
|
1963
|
-
findURLToken(originalResourceURL, declaration.value.children, token => token.data.value = util.EMPTY_RESOURCE);
|
|
1964
1888
|
}
|
|
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
1889
|
}
|
|
1890
|
+
} else {
|
|
1891
|
+
urlNode.value = util.EMPTY_RESOURCE;
|
|
1977
1892
|
}
|
|
1978
|
-
}
|
|
1893
|
+
}));
|
|
1979
1894
|
}
|
|
1980
1895
|
|
|
1981
1896
|
static async processAttribute(resourceElements, attributeName, baseURI, options, expectedType, cssVariables, styles, batchRequest, processDuplicates, removeElementIfMissing) {
|
|
@@ -2033,7 +1948,7 @@ class ProcessorHelper {
|
|
|
2033
1948
|
if (expectedType == "image" && processDuplicates && duplicate && !isSVG && util.getContentSize(content) < SINGLE_FILE_VARIABLE_MAX_SIZE) {
|
|
2034
1949
|
if (ProcessorHelper.replaceImageSource(resourceElement, SINGLE_FILE_VARIABLE_NAME_PREFIX + indexResource, options)) {
|
|
2035
1950
|
cssVariables.set(indexResource, { content, url: originURL });
|
|
2036
|
-
const declarationList = cssTree.parse(resourceElement.getAttribute("style"), { context: "declarationList" });
|
|
1951
|
+
const declarationList = cssTree.parse(resourceElement.getAttribute("style"), { context: "declarationList", parseCustomProperty: true });
|
|
2037
1952
|
styles.set(resourceElement, declarationList);
|
|
2038
1953
|
} else {
|
|
2039
1954
|
resourceElement.setAttribute(attributeName, content);
|
|
@@ -2188,18 +2103,6 @@ const HTTP_URI_PREFIX = /^https?:\/\//;
|
|
|
2188
2103
|
const FILE_URI_PREFIX = /^file:\/\//;
|
|
2189
2104
|
const EMPTY_URL = /^https?:\/\/+\s*$/;
|
|
2190
2105
|
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
2106
|
|
|
2204
2107
|
function getUpdatedResourceContent(resourceURL, content, options) {
|
|
2205
2108
|
if (options.rootDocument && options.updatedResources[resourceURL]) {
|
|
@@ -2218,20 +2121,6 @@ function normalizeURL(url) {
|
|
|
2218
2121
|
}
|
|
2219
2122
|
}
|
|
2220
2123
|
|
|
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
2124
|
function matchCharsetEquals(stylesheetContent, charset = UTF8_CHARSET) {
|
|
2236
2125
|
const stylesheetCharset = getCharset(stylesheetContent);
|
|
2237
2126
|
if (stylesheetCharset) {
|
|
@@ -2318,21 +2207,23 @@ function getLastSegment(url, replacementCharacter) {
|
|
|
2318
2207
|
return lastSegment;
|
|
2319
2208
|
}
|
|
2320
2209
|
|
|
2321
|
-
function
|
|
2322
|
-
return
|
|
2210
|
+
function getUrlFunctions(declarationList) {
|
|
2211
|
+
return cssTree.findAll(declarationList, node => node.type == "Url");
|
|
2323
2212
|
}
|
|
2324
2213
|
|
|
2325
|
-
function
|
|
2326
|
-
|
|
2327
|
-
if (unique) {
|
|
2328
|
-
return [...new Set(result)];
|
|
2329
|
-
} else {
|
|
2330
|
-
return result;
|
|
2331
|
-
}
|
|
2214
|
+
function getImportFunctions(declarationList) {
|
|
2215
|
+
return cssTree.findAll(declarationList, node => node.type == "Atrule" && node.name == "import");
|
|
2332
2216
|
}
|
|
2333
2217
|
|
|
2334
|
-
function
|
|
2335
|
-
|
|
2218
|
+
function generateStylesheetContent(stylesheet, options) {
|
|
2219
|
+
let stylesheetContent = cssTree.generate(stylesheet);
|
|
2220
|
+
if (options.compressCSS) {
|
|
2221
|
+
stylesheetContent = util.compressCSS(stylesheetContent);
|
|
2222
|
+
}
|
|
2223
|
+
if (options.saveOriginalURLs) {
|
|
2224
|
+
stylesheetContent = replaceOriginalURLs(stylesheetContent);
|
|
2225
|
+
}
|
|
2226
|
+
return stylesheetContent;
|
|
2336
2227
|
}
|
|
2337
2228
|
|
|
2338
2229
|
function findShortcutIcon(shortcutIcons) {
|
|
@@ -2341,30 +2232,12 @@ function findShortcutIcon(shortcutIcons) {
|
|
|
2341
2232
|
return shortcutIcons[0];
|
|
2342
2233
|
}
|
|
2343
2234
|
|
|
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
2235
|
function isDataURL(url) {
|
|
2363
2236
|
return url && (url.startsWith(DATA_URI_PREFIX) || url.startsWith(BLOB_URI_PREFIX));
|
|
2364
2237
|
}
|
|
2365
2238
|
|
|
2366
2239
|
function replaceOriginalURLs(stylesheetContent) {
|
|
2367
|
-
return stylesheetContent.replace(
|
|
2240
|
+
return stylesheetContent.replace(/url\(-sf-url-original\\\(\\"(.*?)\\"\\\)\\ /g, "/* original URL: $1 */url(");
|
|
2368
2241
|
}
|
|
2369
2242
|
|
|
2370
2243
|
function testIgnoredPath(resourceURL) {
|
|
@@ -2379,35 +2252,6 @@ function testValidURL(resourceURL) {
|
|
|
2379
2252
|
return testValidPath(resourceURL) && (resourceURL.match(HTTP_URI_PREFIX) || resourceURL.match(FILE_URI_PREFIX) || resourceURL.startsWith(BLOB_URI_PREFIX)) && resourceURL.match(NOT_EMPTY_URL);
|
|
2380
2253
|
}
|
|
2381
2254
|
|
|
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
2255
|
function wrapMediaQuery(stylesheetContent, mediaQuery) {
|
|
2412
2256
|
if (mediaQuery) {
|
|
2413
2257
|
return "@media " + mediaQuery + "{ " + stylesheetContent + " }";
|
package/single-file-helper.js
CHANGED
|
@@ -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
|
-
|
|
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");
|