single-file-core 1.2.24 → 1.2.26
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 +1 -1
- package/core/lib/processor-helper-common.js +373 -42
- package/core/lib/processor-helper-inline.js +101 -412
- package/core/lib/processor-helper.js +102 -412
- package/modules/template-formatter.js +27 -12
- package/package.json +1 -1
- package/processors/compression/compression-extract.js +12 -1
- package/processors/compression/compression.js +6 -6
- package/single-file.js +1 -0
|
@@ -24,10 +24,6 @@
|
|
|
24
24
|
/* global globalThis */
|
|
25
25
|
|
|
26
26
|
import * as cssTree from "./../../vendor/css-tree.js";
|
|
27
|
-
import {
|
|
28
|
-
normalizeFontFamily,
|
|
29
|
-
getFontWeight
|
|
30
|
-
} from "./../helper.js";
|
|
31
27
|
|
|
32
28
|
const JSON = globalThis.JSON;
|
|
33
29
|
const FontFace = globalThis.FontFace;
|
|
@@ -45,32 +41,8 @@ const NOSCRIPT_TAG_FOUND = /<noscript/gi;
|
|
|
45
41
|
const CANVAS_TAG_FOUND = /<canvas/gi;
|
|
46
42
|
const SINGLE_FILE_VARIABLE_NAME_PREFIX = "--sf-img-";
|
|
47
43
|
const SINGLE_FILE_VARIABLE_MAX_SIZE = 512 * 1024;
|
|
48
|
-
|
|
49
|
-
const REGEXP_URL_SIMPLE_QUOTES_FN = /url\s*\(\s*'(.*?)'\s*\)/i;
|
|
50
|
-
const REGEXP_URL_DOUBLE_QUOTES_FN = /url\s*\(\s*"(.*?)"\s*\)/i;
|
|
51
|
-
const REGEXP_URL_NO_QUOTES_FN = /url\s*\(\s*(.*?)\s*\)/i;
|
|
52
|
-
const REGEXP_URL_FUNCTION = /(url|local|-sf-url-original)\(.*?\)\s*(,|$)/g;
|
|
53
|
-
const REGEXP_SIMPLE_QUOTES_STRING = /^'(.*?)'$/;
|
|
54
|
-
const REGEXP_DOUBLE_QUOTES_STRING = /^"(.*?)"$/;
|
|
55
|
-
const REGEXP_URL_FUNCTION_WOFF = /^url\(\s*["']?data:font\/(woff2?)/;
|
|
56
|
-
const REGEXP_URL_FUNCTION_WOFF_ALT = /^url\(\s*["']?data:application\/x-font-(woff)/;
|
|
57
|
-
const REGEXP_FONT_FORMAT = /\.([^.?#]+)((\?|#).*?)?$/;
|
|
58
|
-
const REGEXP_FONT_FORMAT_VALUE = /format\((.*?)\)\s*,?$/;
|
|
59
|
-
const REGEXP_FONT_SRC = /(.*?)\s*,?$/;
|
|
60
44
|
const EMPTY_URL_SOURCE = /^url\(["']?data:[^,]*,?["']?\)/;
|
|
61
45
|
const LOCAL_SOURCE = "local(";
|
|
62
|
-
const MEDIA_ALL = "all";
|
|
63
|
-
const FONT_STRETCHES = {
|
|
64
|
-
"ultra-condensed": "50%",
|
|
65
|
-
"extra-condensed": "62.5%",
|
|
66
|
-
"condensed": "75%",
|
|
67
|
-
"semi-condensed": "87.5%",
|
|
68
|
-
"normal": "100%",
|
|
69
|
-
"semi-expanded": "112.5%",
|
|
70
|
-
"expanded": "125%",
|
|
71
|
-
"extra-expanded": "150%",
|
|
72
|
-
"ultra-expanded": "200%"
|
|
73
|
-
};
|
|
74
46
|
const FONT_MAX_LOAD_DELAY = 5000;
|
|
75
47
|
|
|
76
48
|
let util;
|
|
@@ -100,39 +72,6 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
100
72
|
const ProcessorHelperCommon = getProcessorHelperCommonClass(util, cssTree);
|
|
101
73
|
|
|
102
74
|
return class ProcessorHelper extends ProcessorHelperCommon {
|
|
103
|
-
async processPageResources(doc, baseURI, options, resources, styles, batchRequest) {
|
|
104
|
-
const processAttributeArgs = [
|
|
105
|
-
["link[href][rel*=\"icon\"]", "href", false, true],
|
|
106
|
-
["object[type=\"image/svg+xml\"], object[type=\"image/svg-xml\"], object[data*=\".svg\"]", "data"],
|
|
107
|
-
["img[src], input[src][type=image]", "src", true],
|
|
108
|
-
["embed[src*=\".svg\"]", "src"],
|
|
109
|
-
["video[poster]", "poster"],
|
|
110
|
-
["*[background]", "background"],
|
|
111
|
-
["image", "xlink:href"],
|
|
112
|
-
["image", "href"]
|
|
113
|
-
];
|
|
114
|
-
if (options.blockImages) {
|
|
115
|
-
doc.querySelectorAll("svg").forEach(element => element.remove());
|
|
116
|
-
}
|
|
117
|
-
let resourcePromises = processAttributeArgs.map(([selector, attributeName, processDuplicates, removeElementIfMissing]) =>
|
|
118
|
-
this.processAttribute(doc.querySelectorAll(selector), attributeName, baseURI, options, "image", resources, styles, batchRequest, processDuplicates, removeElementIfMissing)
|
|
119
|
-
);
|
|
120
|
-
resourcePromises = resourcePromises.concat([
|
|
121
|
-
this.processXLinks(doc.querySelectorAll("use"), doc, baseURI, options, batchRequest),
|
|
122
|
-
this.processSrcset(doc.querySelectorAll("img[srcset], source[srcset]"), baseURI, options, batchRequest)
|
|
123
|
-
]);
|
|
124
|
-
resourcePromises.push(this.processAttribute(doc.querySelectorAll("object[data*=\".pdf\"]"), "data", baseURI, options, null, resources, styles, batchRequest));
|
|
125
|
-
resourcePromises.push(this.processAttribute(doc.querySelectorAll("embed[src*=\".pdf\"]"), "src", baseURI, options, null, resources, styles, batchRequest));
|
|
126
|
-
resourcePromises.push(this.processAttribute(doc.querySelectorAll("audio[src], audio > source[src]"), "src", baseURI, options, "audio", resources, styles, batchRequest));
|
|
127
|
-
resourcePromises.push(this.processAttribute(doc.querySelectorAll("video[src], video > source[src]"), "src", baseURI, options, "video", resources, styles, batchRequest));
|
|
128
|
-
resourcePromises.push(this.processAttribute(doc.querySelectorAll("audio track[src], video track[src]"), "src", baseURI, options, null, resources, styles, batchRequest));
|
|
129
|
-
resourcePromises.push(this.processAttribute(doc.querySelectorAll("model[src]"), "src", baseURI, options, null, resources, styles, batchRequest));
|
|
130
|
-
await Promise.all(resourcePromises);
|
|
131
|
-
if (options.saveFavicon) {
|
|
132
|
-
this.processShortcutIcons(doc);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
75
|
async processLinkElement(element, stylesheetInfo, stylesheets, baseURI, options, workStyleElement) {
|
|
137
76
|
if (element.tagName.toUpperCase() == "LINK" && element.charset) {
|
|
138
77
|
options.charset = element.charset;
|
|
@@ -165,7 +104,7 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
165
104
|
}
|
|
166
105
|
}
|
|
167
106
|
|
|
168
|
-
replaceStylesheets(doc, stylesheets,
|
|
107
|
+
replaceStylesheets(doc, stylesheets, options) {
|
|
169
108
|
doc.querySelectorAll("style").forEach(styleElement => {
|
|
170
109
|
const stylesheetInfo = stylesheets.get(styleElement);
|
|
171
110
|
if (stylesheetInfo) {
|
|
@@ -295,54 +234,27 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
295
234
|
}
|
|
296
235
|
}
|
|
297
236
|
|
|
298
|
-
async
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
promises.push(this.processStylesheet(ruleData.block.children, baseURI, options, resources, batchRequest));
|
|
310
|
-
} else if (ruleData.type == "Atrule" && ruleData.name == "font-face") {
|
|
311
|
-
promises.push(processFontFaceRule(ruleData));
|
|
312
|
-
}
|
|
313
|
-
}
|
|
237
|
+
async processFont(resourceURL, urlNode, originalResourceURL, baseURI, options, resources, batchRequest) {
|
|
238
|
+
let { content } = await batchRequest.addURL(resourceURL, {
|
|
239
|
+
asBinary: true,
|
|
240
|
+
expectedType: "font",
|
|
241
|
+
baseURI,
|
|
242
|
+
blockMixedContent: options.blockMixedContent
|
|
243
|
+
});
|
|
244
|
+
let resourceURLs = resources.fonts.get(urlNode);
|
|
245
|
+
if (!resourceURLs) {
|
|
246
|
+
resourceURLs = [];
|
|
247
|
+
resources.fonts.set(urlNode, resourceURLs);
|
|
314
248
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
await Promise.all(urls.map(async urlNode => {
|
|
321
|
-
const originalResourceURL = urlNode.value;
|
|
322
|
-
if (!options.blockFonts) {
|
|
323
|
-
const resourceURL = normalizeURL(originalResourceURL);
|
|
324
|
-
if (!testIgnoredPath(resourceURL) && testValidURL(resourceURL)) {
|
|
325
|
-
let { content } = await batchRequest.addURL(resourceURL, { asBinary: true, expectedType: "font", baseURI, blockMixedContent: options.blockMixedContent });
|
|
326
|
-
let resourceURLs = resources.fonts.get(urlNode);
|
|
327
|
-
if (!resourceURLs) {
|
|
328
|
-
resourceURLs = [];
|
|
329
|
-
resources.fonts.set(urlNode, resourceURLs);
|
|
330
|
-
}
|
|
331
|
-
resourceURLs.push(resourceURL);
|
|
332
|
-
if (!isDataURL(resourceURL) && options.saveOriginalURLs) {
|
|
333
|
-
urlNode.value = "-sf-url-original(" + JSON.stringify(originalResourceURL) + ") " + content;
|
|
334
|
-
} else {
|
|
335
|
-
urlNode.value = content;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
} else {
|
|
339
|
-
urlNode.value = util.EMPTY_RESOURCE;
|
|
340
|
-
}
|
|
341
|
-
}));
|
|
249
|
+
resourceURLs.push(resourceURL);
|
|
250
|
+
if (!isDataURL(resourceURL) && options.saveOriginalURLs) {
|
|
251
|
+
urlNode.value = "-sf-url-original(" + JSON.stringify(originalResourceURL) + ") " + content;
|
|
252
|
+
} else {
|
|
253
|
+
urlNode.value = content;
|
|
342
254
|
}
|
|
343
255
|
}
|
|
344
256
|
|
|
345
|
-
async processStyle(ruleData, options,
|
|
257
|
+
async processStyle(ruleData, options, resources, batchRequest) {
|
|
346
258
|
const urls = getUrlFunctions(ruleData);
|
|
347
259
|
await Promise.all(urls.map(async urlNode => {
|
|
348
260
|
const originalResourceURL = urlNode.value;
|
|
@@ -357,7 +269,7 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
357
269
|
for (let keyName of Object.keys(varNode.children.head.data)) {
|
|
358
270
|
urlNode[keyName] = varNode.children.head.data[keyName];
|
|
359
271
|
}
|
|
360
|
-
cssVariables.set(indexResource, { content, url: originalResourceURL });
|
|
272
|
+
resources.cssVariables.set(indexResource, { content, url: originalResourceURL });
|
|
361
273
|
} else {
|
|
362
274
|
if (!isDataURL(resourceURL) && options.saveOriginalURLs) {
|
|
363
275
|
urlNode.value = "-sf-url-original(" + JSON.stringify(originalResourceURL) + ") " + content;
|
|
@@ -373,7 +285,7 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
373
285
|
}));
|
|
374
286
|
}
|
|
375
287
|
|
|
376
|
-
async processAttribute(resourceElements, attributeName, baseURI, options, expectedType,
|
|
288
|
+
async processAttribute(resourceElements, attributeName, baseURI, options, expectedType, resources, removeElementIfMissing, batchRequest, styles, processDuplicates) {
|
|
377
289
|
await Promise.all(Array.from(resourceElements).map(async resourceElement => {
|
|
378
290
|
let resourceURL = resourceElement.getAttribute(attributeName);
|
|
379
291
|
if (resourceURL != null) {
|
|
@@ -445,7 +357,7 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
445
357
|
const maxSizeDuplicateImages = options.maxSizeDuplicateImages || SINGLE_FILE_VARIABLE_MAX_SIZE;
|
|
446
358
|
if (processDuplicates && duplicate && !isSVG && util.getContentSize(content) < maxSizeDuplicateImages) {
|
|
447
359
|
if (this.replaceImageSource(resourceElement, SINGLE_FILE_VARIABLE_NAME_PREFIX + indexResource, options)) {
|
|
448
|
-
cssVariables.set(indexResource, { content, url: originURL });
|
|
360
|
+
resources.cssVariables.set(indexResource, { content, url: originURL });
|
|
449
361
|
const declarationList = cssTree.parse(resourceElement.getAttribute("style"), { context: "declarationList", parseCustomProperty: true });
|
|
450
362
|
styles.set(resourceElement, declarationList);
|
|
451
363
|
} else {
|
|
@@ -477,45 +389,13 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
477
389
|
}
|
|
478
390
|
}
|
|
479
391
|
|
|
480
|
-
async
|
|
481
|
-
await
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
if (!options.blockImages) {
|
|
488
|
-
const srcsetValues = await Promise.all(srcset.map(async srcsetValue => {
|
|
489
|
-
let resourceURL = normalizeURL(srcsetValue.url);
|
|
490
|
-
if (!testIgnoredPath(resourceURL)) {
|
|
491
|
-
if (testValidPath(resourceURL)) {
|
|
492
|
-
try {
|
|
493
|
-
resourceURL = util.resolveURL(resourceURL, baseURI);
|
|
494
|
-
} catch (error) {
|
|
495
|
-
// ignored
|
|
496
|
-
}
|
|
497
|
-
if (testValidURL(resourceURL)) {
|
|
498
|
-
const { content } = await batchRequest.addURL(resourceURL, { asBinary: true, expectedType: "image" });
|
|
499
|
-
const forbiddenPrefixFound = PREFIXES_FORBIDDEN_DATA_URI.filter(prefixDataURI => content.startsWith(prefixDataURI)).length;
|
|
500
|
-
if (forbiddenPrefixFound) {
|
|
501
|
-
return "";
|
|
502
|
-
}
|
|
503
|
-
return content + (srcsetValue.w ? " " + srcsetValue.w + "w" : srcsetValue.d ? " " + srcsetValue.d + "x" : "");
|
|
504
|
-
} else {
|
|
505
|
-
return "";
|
|
506
|
-
}
|
|
507
|
-
} else {
|
|
508
|
-
return "";
|
|
509
|
-
}
|
|
510
|
-
} else {
|
|
511
|
-
return resourceURL + (srcsetValue.w ? " " + srcsetValue.w + "w" : srcsetValue.d ? " " + srcsetValue.d + "x" : "");
|
|
512
|
-
}
|
|
513
|
-
}));
|
|
514
|
-
resourceElement.setAttribute("srcset", srcsetValues.join(", "));
|
|
515
|
-
} else {
|
|
516
|
-
resourceElement.setAttribute("srcset", "");
|
|
517
|
-
}
|
|
518
|
-
}));
|
|
392
|
+
async processImageSrcset(resourceURL, srcsetValue, resources, batchRequest) {
|
|
393
|
+
const { content } = await batchRequest.addURL(resourceURL, { asBinary: true, expectedType: "image" });
|
|
394
|
+
const forbiddenPrefixFound = PREFIXES_FORBIDDEN_DATA_URI.filter(prefixDataURI => content.startsWith(prefixDataURI)).length;
|
|
395
|
+
if (forbiddenPrefixFound) {
|
|
396
|
+
return "";
|
|
397
|
+
}
|
|
398
|
+
return content + (srcsetValue.w ? " " + srcsetValue.w + "w" : srcsetValue.d ? " " + srcsetValue.d + "x" : "");
|
|
519
399
|
}
|
|
520
400
|
|
|
521
401
|
testEmptyResource(resource) {
|
|
@@ -569,10 +449,6 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
569
449
|
return {};
|
|
570
450
|
}
|
|
571
451
|
|
|
572
|
-
removeAlternativeFonts(doc, stylesheets, fonts, fontTests) {
|
|
573
|
-
return removeAlternativeFonts(doc, stylesheets, fonts, fontTests);
|
|
574
|
-
}
|
|
575
|
-
|
|
576
452
|
async processScript(element, resourceURL, options, charset) {
|
|
577
453
|
const content = await util.getContent(resourceURL, {
|
|
578
454
|
asBinary: true,
|
|
@@ -599,279 +475,92 @@ function getProcessorHelperClass(utilInstance) {
|
|
|
599
475
|
removeUnusedStylesheets(doc) {
|
|
600
476
|
doc.querySelectorAll("link[rel*=stylesheet][rel*=alternate][title]").forEach(element => element.remove());
|
|
601
477
|
}
|
|
602
|
-
};
|
|
603
|
-
}
|
|
604
478
|
|
|
605
|
-
async
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
};
|
|
611
|
-
const stats = { rules: { processed: 0, discarded: 0 }, fonts: { processed: 0, discarded: 0 } };
|
|
612
|
-
let sheetIndex = 0;
|
|
613
|
-
stylesheets.forEach(stylesheetInfo => {
|
|
614
|
-
const cssRules = stylesheetInfo.stylesheet.children;
|
|
615
|
-
if (cssRules) {
|
|
616
|
-
stats.rules.processed += cssRules.size;
|
|
617
|
-
stats.rules.discarded += cssRules.size;
|
|
618
|
-
if (stylesheetInfo.mediaText && stylesheetInfo.mediaText != MEDIA_ALL) {
|
|
619
|
-
const mediaFontsDetails = createFontsDetailsInfo();
|
|
620
|
-
fontsDetails.medias.set("media-" + sheetIndex + "-" + stylesheetInfo.mediaText, mediaFontsDetails);
|
|
621
|
-
getFontsDetails(doc, cssRules, sheetIndex, mediaFontsDetails);
|
|
622
|
-
} else {
|
|
623
|
-
getFontsDetails(doc, cssRules, sheetIndex, fontsDetails);
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
sheetIndex++;
|
|
627
|
-
});
|
|
628
|
-
processFontDetails(fontsDetails);
|
|
629
|
-
await Promise.all([...stylesheets].map(async ([, stylesheetInfo], sheetIndex) => {
|
|
630
|
-
const cssRules = stylesheetInfo.stylesheet.children;
|
|
631
|
-
const media = stylesheetInfo.mediaText;
|
|
632
|
-
if (cssRules) {
|
|
633
|
-
if (media && media != MEDIA_ALL) {
|
|
634
|
-
await processFontFaceRules(cssRules, sheetIndex, fontsDetails.medias.get("media-" + sheetIndex + "-" + media), fontDeclarations, fontTests, stats);
|
|
635
|
-
} else {
|
|
636
|
-
await processFontFaceRules(cssRules, sheetIndex, fontsDetails, fontDeclarations, fontTests, stats);
|
|
637
|
-
}
|
|
638
|
-
stats.rules.discarded -= cssRules.size;
|
|
639
|
-
}
|
|
640
|
-
}));
|
|
641
|
-
return stats;
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
function getFontsDetails(doc, cssRules, sheetIndex, mediaFontsDetails) {
|
|
645
|
-
let mediaIndex = 0, supportsIndex = 0;
|
|
646
|
-
cssRules.forEach(ruleData => {
|
|
647
|
-
if (ruleData.type == "Atrule" && ruleData.name == "media" && ruleData.block && ruleData.block.children && ruleData.prelude) {
|
|
648
|
-
const mediaText = cssTree.generate(ruleData.prelude);
|
|
649
|
-
const fontsDetails = createFontsDetailsInfo();
|
|
650
|
-
mediaFontsDetails.medias.set("media-" + sheetIndex + "-" + mediaIndex + "-" + mediaText, fontsDetails);
|
|
651
|
-
mediaIndex++;
|
|
652
|
-
getFontsDetails(doc, ruleData.block.children, sheetIndex, fontsDetails);
|
|
653
|
-
} else if (ruleData.type == "Atrule" && ruleData.name == "supports" && ruleData.block && ruleData.block.children && ruleData.prelude) {
|
|
654
|
-
const supportsText = cssTree.generate(ruleData.prelude);
|
|
655
|
-
const fontsDetails = createFontsDetailsInfo();
|
|
656
|
-
mediaFontsDetails.supports.set("supports-" + sheetIndex + "-" + supportsIndex + "-" + supportsText, fontsDetails);
|
|
657
|
-
supportsIndex++;
|
|
658
|
-
getFontsDetails(doc, ruleData.block.children, sheetIndex, fontsDetails);
|
|
659
|
-
} else if (ruleData.type == "Atrule" && ruleData.name == "font-face" && ruleData.block && ruleData.block.children) {
|
|
660
|
-
const fontKey = getFontKey(ruleData);
|
|
661
|
-
let fontInfo = mediaFontsDetails.fonts.get(fontKey);
|
|
662
|
-
if (!fontInfo) {
|
|
663
|
-
fontInfo = [];
|
|
664
|
-
mediaFontsDetails.fonts.set(fontKey, fontInfo);
|
|
665
|
-
}
|
|
666
|
-
const src = getPropertyValue(ruleData, "src");
|
|
667
|
-
if (src) {
|
|
668
|
-
const fontSources = src.match(REGEXP_URL_FUNCTION);
|
|
669
|
-
if (fontSources) {
|
|
670
|
-
fontSources.forEach(source => fontInfo.unshift(source));
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
function processFontDetails(fontsDetails) {
|
|
678
|
-
fontsDetails.fonts.forEach((fontInfo, fontKey) => {
|
|
679
|
-
fontsDetails.fonts.set(fontKey, fontInfo.map(fontSource => {
|
|
680
|
-
const fontFormatMatch = fontSource.match(REGEXP_FONT_FORMAT_VALUE);
|
|
681
|
-
let fontFormat;
|
|
682
|
-
const fontUrl = getURL(fontSource);
|
|
683
|
-
if (fontFormatMatch && fontFormatMatch[1]) {
|
|
684
|
-
fontFormat = fontFormatMatch[1].replace(REGEXP_SIMPLE_QUOTES_STRING, "$1").replace(REGEXP_DOUBLE_QUOTES_STRING, "$1").toLowerCase();
|
|
685
|
-
}
|
|
686
|
-
if (!fontFormat) {
|
|
687
|
-
const fontFormatMatch = fontSource.match(REGEXP_URL_FUNCTION_WOFF);
|
|
688
|
-
if (fontFormatMatch && fontFormatMatch[1]) {
|
|
689
|
-
fontFormat = fontFormatMatch[1];
|
|
690
|
-
} else {
|
|
691
|
-
const fontFormatMatch = fontSource.match(REGEXP_URL_FUNCTION_WOFF_ALT);
|
|
692
|
-
if (fontFormatMatch && fontFormatMatch[1]) {
|
|
693
|
-
fontFormat = fontFormatMatch[1];
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
if (!fontFormat && fontUrl) {
|
|
698
|
-
const fontFormatMatch = fontUrl.match(REGEXP_FONT_FORMAT);
|
|
699
|
-
if (fontFormatMatch && fontFormatMatch[1]) {
|
|
700
|
-
fontFormat = fontFormatMatch[1];
|
|
479
|
+
async processFontFaceRule(ruleData, fontInfo, fontDeclarations, fontTests, stats) {
|
|
480
|
+
const removedNodes = [];
|
|
481
|
+
for (let node = ruleData.block.children.head; node; node = node.next) {
|
|
482
|
+
if (node.data.property == "src") {
|
|
483
|
+
removedNodes.push(node);
|
|
701
484
|
}
|
|
702
485
|
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
async function processFontFaceRule(ruleData, fontInfo, fontDeclarations, fontTests, stats) {
|
|
740
|
-
const removedNodes = [];
|
|
741
|
-
for (let node = ruleData.block.children.head; node; node = node.next) {
|
|
742
|
-
if (node.data.property == "src") {
|
|
743
|
-
removedNodes.push(node);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
removedNodes.pop();
|
|
747
|
-
removedNodes.forEach(node => ruleData.block.children.remove(node));
|
|
748
|
-
const srcDeclaration = ruleData.block.children.filter(node => node.property == "src").tail;
|
|
749
|
-
if (srcDeclaration) {
|
|
750
|
-
await Promise.all(fontInfo.map(async source => {
|
|
751
|
-
if (fontTests.has(source.src)) {
|
|
752
|
-
source.valid = fontTests.get(source.src);
|
|
753
|
-
} else {
|
|
754
|
-
if (FontFace && source.fontUrl) {
|
|
755
|
-
const fontFace = new FontFace("test-font", source.src);
|
|
756
|
-
try {
|
|
757
|
-
let timeout;
|
|
758
|
-
await Promise.race([
|
|
759
|
-
fontFace.load().then(() => fontFace.loaded).then(() => { source.valid = true; globalThis.clearTimeout(timeout); }),
|
|
760
|
-
new Promise(resolve => timeout = globalThis.setTimeout(() => { source.valid = true; resolve(); }, FONT_MAX_LOAD_DELAY))
|
|
761
|
-
]);
|
|
762
|
-
} catch (error) {
|
|
763
|
-
const urlNodes = cssTree.findAll(srcDeclaration.data, node => node.type == "Url");
|
|
764
|
-
const declarationFontURLs = Array.from(fontDeclarations).find(([node]) => urlNodes.includes(node) && node.value == source.fontUrl);
|
|
765
|
-
if (declarationFontURLs && declarationFontURLs[1].length) {
|
|
766
|
-
const fontURL = declarationFontURLs[1][0];
|
|
767
|
-
if (fontURL) {
|
|
768
|
-
const fontFace = new FontFace("test-font", "url(" + fontURL + ")");
|
|
769
|
-
try {
|
|
770
|
-
let timeout;
|
|
771
|
-
await Promise.race([
|
|
772
|
-
fontFace.load().then(() => fontFace.loaded).then(() => { source.valid = true; globalThis.clearTimeout(timeout); }),
|
|
773
|
-
new Promise(resolve => timeout = globalThis.setTimeout(() => { source.valid = true; resolve(); }, FONT_MAX_LOAD_DELAY))
|
|
774
|
-
]);
|
|
775
|
-
} catch (error) {
|
|
776
|
-
// ignored
|
|
486
|
+
removedNodes.pop();
|
|
487
|
+
removedNodes.forEach(node => ruleData.block.children.remove(node));
|
|
488
|
+
const srcDeclaration = ruleData.block.children.filter(node => node.property == "src").tail;
|
|
489
|
+
if (srcDeclaration) {
|
|
490
|
+
await Promise.all(fontInfo.map(async source => {
|
|
491
|
+
if (fontTests.has(source.src)) {
|
|
492
|
+
source.valid = fontTests.get(source.src);
|
|
493
|
+
} else {
|
|
494
|
+
if (FontFace && source.fontUrl) {
|
|
495
|
+
const fontFace = new FontFace("test-font", source.src);
|
|
496
|
+
try {
|
|
497
|
+
let timeout;
|
|
498
|
+
await Promise.race([
|
|
499
|
+
fontFace.load().then(() => fontFace.loaded).then(() => { source.valid = true; globalThis.clearTimeout(timeout); }),
|
|
500
|
+
new Promise(resolve => timeout = globalThis.setTimeout(() => { source.valid = true; resolve(); }, FONT_MAX_LOAD_DELAY))
|
|
501
|
+
]);
|
|
502
|
+
} catch (error) {
|
|
503
|
+
const urlNodes = cssTree.findAll(srcDeclaration.data, node => node.type == "Url");
|
|
504
|
+
const declarationFontURLs = Array.from(fontDeclarations).find(([node]) => urlNodes.includes(node) && node.value == source.fontUrl);
|
|
505
|
+
if (declarationFontURLs && declarationFontURLs[1].length) {
|
|
506
|
+
const fontURL = declarationFontURLs[1][0];
|
|
507
|
+
if (fontURL) {
|
|
508
|
+
const fontFace = new FontFace("test-font", "url(" + fontURL + ")");
|
|
509
|
+
try {
|
|
510
|
+
let timeout;
|
|
511
|
+
await Promise.race([
|
|
512
|
+
fontFace.load().then(() => fontFace.loaded).then(() => { source.valid = true; globalThis.clearTimeout(timeout); }),
|
|
513
|
+
new Promise(resolve => timeout = globalThis.setTimeout(() => { source.valid = true; resolve(); }, FONT_MAX_LOAD_DELAY))
|
|
514
|
+
]);
|
|
515
|
+
} catch (error) {
|
|
516
|
+
// ignored
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
} else {
|
|
520
|
+
source.valid = true;
|
|
777
521
|
}
|
|
778
522
|
}
|
|
779
523
|
} else {
|
|
780
524
|
source.valid = true;
|
|
781
525
|
}
|
|
526
|
+
fontTests.set(source.src, source.valid);
|
|
782
527
|
}
|
|
528
|
+
}));
|
|
529
|
+
const findSourceByFormat = (fontFormat, testValidity) => fontInfo.find(source => !source.src.match(EMPTY_URL_SOURCE) && source.format == fontFormat && (!testValidity || source.valid));
|
|
530
|
+
const filterSources = fontSource => fontInfo.filter(source => source == fontSource || source.src.startsWith(LOCAL_SOURCE));
|
|
531
|
+
stats.fonts.processed += fontInfo.length;
|
|
532
|
+
stats.fonts.discarded += fontInfo.length;
|
|
533
|
+
const woffFontFound =
|
|
534
|
+
findSourceByFormat("woff2-variations", true) || findSourceByFormat("woff2", true) || findSourceByFormat("woff", true);
|
|
535
|
+
if (woffFontFound) {
|
|
536
|
+
fontInfo = filterSources(woffFontFound);
|
|
783
537
|
} else {
|
|
784
|
-
|
|
538
|
+
const ttfFontFound =
|
|
539
|
+
findSourceByFormat("truetype-variations", true) || findSourceByFormat("truetype", true);
|
|
540
|
+
if (ttfFontFound) {
|
|
541
|
+
fontInfo = filterSources(ttfFontFound);
|
|
542
|
+
} else {
|
|
543
|
+
const otfFontFound =
|
|
544
|
+
findSourceByFormat("opentype") || findSourceByFormat("embedded-opentype");
|
|
545
|
+
if (otfFontFound) {
|
|
546
|
+
fontInfo = filterSources(otfFontFound);
|
|
547
|
+
} else {
|
|
548
|
+
fontInfo = fontInfo.filter(source => !source.src.match(EMPTY_URL_SOURCE) && (source.valid) || source.src.startsWith(LOCAL_SOURCE));
|
|
549
|
+
}
|
|
550
|
+
}
|
|
785
551
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
const filterSources = fontSource => fontInfo.filter(source => source == fontSource || source.src.startsWith(LOCAL_SOURCE));
|
|
791
|
-
stats.fonts.processed += fontInfo.length;
|
|
792
|
-
stats.fonts.discarded += fontInfo.length;
|
|
793
|
-
const woffFontFound =
|
|
794
|
-
findSourceByFormat("woff2-variations", true) || findSourceByFormat("woff2", true) || findSourceByFormat("woff", true);
|
|
795
|
-
if (woffFontFound) {
|
|
796
|
-
fontInfo = filterSources(woffFontFound);
|
|
797
|
-
} else {
|
|
798
|
-
const ttfFontFound =
|
|
799
|
-
findSourceByFormat("truetype-variations", true) || findSourceByFormat("truetype", true);
|
|
800
|
-
if (ttfFontFound) {
|
|
801
|
-
fontInfo = filterSources(ttfFontFound);
|
|
802
|
-
} else {
|
|
803
|
-
const otfFontFound =
|
|
804
|
-
findSourceByFormat("opentype") || findSourceByFormat("embedded-opentype");
|
|
805
|
-
if (otfFontFound) {
|
|
806
|
-
fontInfo = filterSources(otfFontFound);
|
|
807
|
-
} else {
|
|
808
|
-
fontInfo = fontInfo.filter(source => !source.src.match(EMPTY_URL_SOURCE) && (source.valid) || source.src.startsWith(LOCAL_SOURCE));
|
|
552
|
+
stats.fonts.discarded -= fontInfo.length;
|
|
553
|
+
fontInfo.reverse();
|
|
554
|
+
try {
|
|
555
|
+
srcDeclaration.data.value = cssTree.parse(fontInfo.map(fontSource => fontSource.src).join(","), { context: "value", parseCustomProperty: true });
|
|
809
556
|
}
|
|
557
|
+
catch (error) {
|
|
558
|
+
// ignored
|
|
559
|
+
}
|
|
560
|
+
return true;
|
|
561
|
+
} else {
|
|
562
|
+
return false;
|
|
810
563
|
}
|
|
811
564
|
}
|
|
812
|
-
stats.fonts.discarded -= fontInfo.length;
|
|
813
|
-
fontInfo.reverse();
|
|
814
|
-
try {
|
|
815
|
-
srcDeclaration.data.value = cssTree.parse(fontInfo.map(fontSource => fontSource.src).join(","), { context: "value", parseCustomProperty: true });
|
|
816
|
-
}
|
|
817
|
-
catch (error) {
|
|
818
|
-
// ignored
|
|
819
|
-
}
|
|
820
|
-
return true;
|
|
821
|
-
} else {
|
|
822
|
-
return false;
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
function getPropertyValue(ruleData, propertyName) {
|
|
827
|
-
let property;
|
|
828
|
-
if (ruleData.block.children) {
|
|
829
|
-
property = ruleData.block.children.filter(node => {
|
|
830
|
-
try {
|
|
831
|
-
return node.property == propertyName && !cssTree.generate(node.value).match(/\\9$/);
|
|
832
|
-
} catch (error) {
|
|
833
|
-
return node.property == propertyName;
|
|
834
|
-
}
|
|
835
|
-
}).tail;
|
|
836
|
-
}
|
|
837
|
-
if (property) {
|
|
838
|
-
try {
|
|
839
|
-
return cssTree.generate(property.data.value);
|
|
840
|
-
} catch (error) {
|
|
841
|
-
// ignored
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
function getFontKey(ruleData) {
|
|
847
|
-
return JSON.stringify([
|
|
848
|
-
normalizeFontFamily(getPropertyValue(ruleData, "font-family")),
|
|
849
|
-
getFontWeight(getPropertyValue(ruleData, "font-weight") || "400"),
|
|
850
|
-
getPropertyValue(ruleData, "font-style") || "normal",
|
|
851
|
-
getPropertyValue(ruleData, "unicode-range"),
|
|
852
|
-
getFontStretch(getPropertyValue(ruleData, "font-stretch")),
|
|
853
|
-
getPropertyValue(ruleData, "font-variant") || "normal",
|
|
854
|
-
getPropertyValue(ruleData, "font-feature-settings"),
|
|
855
|
-
getPropertyValue(ruleData, "font-variation-settings")
|
|
856
|
-
]);
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
function getFontStretch(stretch) {
|
|
860
|
-
return FONT_STRETCHES[stretch] || stretch;
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
function createFontsDetailsInfo() {
|
|
864
|
-
return {
|
|
865
|
-
fonts: new Map(),
|
|
866
|
-
medias: new Map(),
|
|
867
|
-
supports: new Map()
|
|
868
565
|
};
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
function getURL(urlFunction) {
|
|
872
|
-
urlFunction = urlFunction.replace(/url\(-sf-url-original\\\(\\"(.*?)\\"\\\)\\ /g, "");
|
|
873
|
-
const urlMatch = urlFunction.match(REGEXP_URL_SIMPLE_QUOTES_FN) ||
|
|
874
|
-
urlFunction.match(REGEXP_URL_DOUBLE_QUOTES_FN) ||
|
|
875
|
-
urlFunction.match(REGEXP_URL_NO_QUOTES_FN);
|
|
876
|
-
return urlMatch && urlMatch[1];
|
|
877
566
|
}
|