subfont 6.1.0 → 6.3.1
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/CHANGELOG.md +28 -6
- package/lib/gatherStylesheetsWithPredicates.js +1 -0
- package/lib/getCssRulesByProperty.js +1 -1
- package/lib/subfont.js +7 -22
- package/lib/subsetFonts.js +164 -105
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
### v6.3.1 (2021-10-22)
|
|
2
|
+
|
|
3
|
+
- [Fix tests](https://github.com/Munter/subfont/commit/4f7ab7011220457bab087a7533dbcfd5f8c8020b) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
4
|
+
- [Update postcss to ^8.3.11](https://github.com/Munter/subfont/commit/2b63273784f89f5c1e2944eec238a1d3ab2a96db) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
5
|
+
- [Update font-tracer to ^3.0.1](https://github.com/Munter/subfont/commit/1f3ce78a64c995eee5c49f5ace0ff7a6f6a1f22c) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
6
|
+
- [Drop node.js 16 for the time being because of prebuilt canvas](https://github.com/Munter/subfont/commit/2b45d966c42f7391402dc79df85d2514c71eeee3) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
7
|
+
- [Replace Travis with Github Actions](https://github.com/Munter/subfont/commit/a3851f0ac60f1205977fc2616728c94bcf6dbab8) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
8
|
+
|
|
9
|
+
### v6.3.0 (2021-09-16)
|
|
10
|
+
|
|
11
|
+
- [Update assetgraph to ^7.2.0, fixes \#152](https://github.com/Munter/subfont/commit/485e333202b935260e41f7996bbd96fca50d028a) ([Andreas Lind](mailto:andreas.lind@workday.com))
|
|
12
|
+
|
|
13
|
+
### v6.2.1 (2021-09-05)
|
|
14
|
+
|
|
15
|
+
- [Add test case where the same font-family is defined in an SVG island and the surrounding HTML](https://github.com/Munter/subfont/commit/ea6709648141efcc24177e0461fc2eb5c72c6714) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
16
|
+
- [Avoid "inline SVG in ..." in assetFileName in fontInfo for SVG islands](https://github.com/Munter/subfont/commit/8fa6bfeb41a5e9addda4904810e630e81b4f79e9) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
17
|
+
|
|
18
|
+
### v6.2.0 (2021-09-05)
|
|
19
|
+
|
|
20
|
+
- [Update to font-tracer 3.0.0](https://github.com/Munter/subfont/commit/5236fecd7a3c9387098fcd3f3b5c1ec5c73093fe) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
21
|
+
- [Rename htmlAsset vars\/params\/props for clarity](https://github.com/Munter/subfont/commit/0906ef4b70e45337c8008357d242bfdb237b9ca9) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
22
|
+
- [Handle inline SVGs correctly](https://github.com/Munter/subfont/commit/7920755e75130d89636239b6700ae048dc1c5ef7) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
23
|
+
- [Support tracing text in inline and external SVGs](https://github.com/Munter/subfont/commit/7852e82048c160a50406df9ee3fecf606d11cba0) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
24
|
+
- [Remove preload polyfill cruft](https://github.com/Munter/subfont/commit/9bfd5663a4d5bc77a27f68dec9c784835c6e27c9) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
25
|
+
- [+3 more](https://github.com/Munter/subfont/compare/v6.1.0...v6.2.0)
|
|
26
|
+
|
|
1
27
|
### v6.1.0 (2021-05-23)
|
|
2
28
|
|
|
3
29
|
- [Remove the JavaScript-based preload polyfill](https://github.com/Munter/subfont/commit/b58bc351d8002d2aae1f4f9cf82126a3efcb997e) ([Andreas Lind](mailto:andreas.lind@workday.com))
|
|
@@ -230,16 +256,13 @@
|
|
|
230
256
|
#### Pull requests
|
|
231
257
|
|
|
232
258
|
- [#76](https://github.com/Munter/subfont/pull/76) Fix the prettier setup ([Andreas Lind](mailto:andreas.lind@peakon.com))
|
|
259
|
+
- [#75](https://github.com/Munter/subfont/pull/75) Fix omitFallbacks with Google Web Fonts ([Andreas Lind](mailto:andreas.lind@peakon.com))
|
|
233
260
|
|
|
234
261
|
#### Commits to master
|
|
235
262
|
|
|
236
263
|
- [Switch to the official css-font-parser now that bramstein\/css-font-parser\#7 has been merged and released](https://github.com/Munter/subfont/commit/457c7f0e4cef0a8c1bd8f816c23ace64c9987424) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
237
264
|
- [Don't populate source map relations](https://github.com/Munter/subfont/commit/5c07218b6f1dcc6fad88702a3bcb7b33bf9df54e) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
238
265
|
|
|
239
|
-
### v4.1.2 (2020-01-09)
|
|
240
|
-
|
|
241
|
-
- [#75](https://github.com/Munter/subfont/pull/75) Fix omitFallbacks with Google Web Fonts ([Andreas Lind](mailto:andreas.lind@peakon.com))
|
|
242
|
-
|
|
243
266
|
### v4.1.1 (2020-01-04)
|
|
244
267
|
|
|
245
268
|
- [Add regression test](https://github.com/Munter/subfont/commit/46eddce9c09268dbde459b1f98fe5cec9e4c98f5) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
@@ -270,8 +293,7 @@
|
|
|
270
293
|
- [Add vscode debugger launch configuration for the test suite](https://github.com/Munter/subfont/commit/f8f9abc42909c556765555cc49f44eb40a9194db) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
271
294
|
- [Guard against an already detached relation when cleaning up](https://github.com/Munter/subfont/commit/6392fc359222772c9033a58a9020e3b35487d019) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
272
295
|
|
|
273
|
-
### v4.0.3
|
|
274
|
-
|
|
296
|
+
### v4.0.3
|
|
275
297
|
#### Pull requests
|
|
276
298
|
|
|
277
299
|
- [#67](https://github.com/Munter/subfont/pull/67) Only warn about missing fonttools install if we are actually trying t… ([Peter Müller](mailto:munter@fumle.dk))
|
|
@@ -41,7 +41,7 @@ function getCssRulesByProperty(properties, cssSource, existingPredicates) {
|
|
|
41
41
|
existingPredicates = existingPredicates || {};
|
|
42
42
|
|
|
43
43
|
const parseTree = postcss.parse(cssSource);
|
|
44
|
-
let defaultNamespaceURI
|
|
44
|
+
let defaultNamespaceURI;
|
|
45
45
|
parseTree.walkAtRules('namespace', (rule) => {
|
|
46
46
|
const fragments = rule.params.split(/\s+/);
|
|
47
47
|
if (fragments.length === 1) {
|
package/lib/subfont.js
CHANGED
|
@@ -212,7 +212,7 @@ module.exports = async function subfont(
|
|
|
212
212
|
isInline: false,
|
|
213
213
|
isLoaded: true,
|
|
214
214
|
type: {
|
|
215
|
-
$in: ['Html', 'Css', 'JavaScript'],
|
|
215
|
+
$in: ['Html', 'Svg', 'Css', 'JavaScript'],
|
|
216
216
|
},
|
|
217
217
|
})) {
|
|
218
218
|
sumSizesBefore += asset.rawSrc.length;
|
|
@@ -233,7 +233,7 @@ module.exports = async function subfont(
|
|
|
233
233
|
isInline: false,
|
|
234
234
|
isLoaded: true,
|
|
235
235
|
type: {
|
|
236
|
-
$in: ['Html', 'Css', 'JavaScript'],
|
|
236
|
+
$in: ['Html', 'Svg', 'Css', 'JavaScript'],
|
|
237
237
|
},
|
|
238
238
|
})) {
|
|
239
239
|
sumSizesAfter += asset.rawSrc.length;
|
|
@@ -247,23 +247,6 @@ module.exports = async function subfont(
|
|
|
247
247
|
relation.omitFunctionCall();
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
-
// Compress inserted javascript
|
|
251
|
-
const preloadPolyfillScripts = assetGraph.findRelations({
|
|
252
|
-
type: 'HtmlScript',
|
|
253
|
-
to: {
|
|
254
|
-
isInline: true,
|
|
255
|
-
outgoingRelations: (relation) => relation.type === 'JavaScriptStaticUrl',
|
|
256
|
-
},
|
|
257
|
-
});
|
|
258
|
-
await assetGraph.compressJavaScript({
|
|
259
|
-
type: 'JavaScript',
|
|
260
|
-
isLoaded: true,
|
|
261
|
-
outgoingRelations: (relation) => relation.type === 'JavaScriptStaticUrl',
|
|
262
|
-
});
|
|
263
|
-
for (const relation of preloadPolyfillScripts) {
|
|
264
|
-
relation.inline();
|
|
265
|
-
}
|
|
266
|
-
|
|
267
250
|
for (const asset of assetGraph.findAssets({
|
|
268
251
|
isDirty: true,
|
|
269
252
|
isInline: false,
|
|
@@ -323,7 +306,7 @@ module.exports = async function subfont(
|
|
|
323
306
|
}
|
|
324
307
|
|
|
325
308
|
let totalSavings = sumSizesBefore - sumSizesAfter;
|
|
326
|
-
for (const {
|
|
309
|
+
for (const { assetFileName, fontUsages } of fontInfo) {
|
|
327
310
|
let sumSmallestSubsetSize = 0;
|
|
328
311
|
let sumSmallestOriginalSize = 0;
|
|
329
312
|
let maxUsedCodePoints = 0;
|
|
@@ -346,7 +329,7 @@ module.exports = async function subfont(
|
|
|
346
329
|
);
|
|
347
330
|
const numFonts = Object.keys(fontUsagesByFontFamily).length;
|
|
348
331
|
log(
|
|
349
|
-
`${
|
|
332
|
+
`${assetFileName}: ${numFonts} font${numFonts === 1 ? '' : 's'} (${
|
|
350
333
|
fontUsages.length
|
|
351
334
|
} variant${fontUsages.length === 1 ? '' : 's'}) in use, ${prettyBytes(
|
|
352
335
|
sumSmallestOriginalSize
|
|
@@ -387,7 +370,9 @@ module.exports = async function subfont(
|
|
|
387
370
|
}
|
|
388
371
|
}
|
|
389
372
|
log(
|
|
390
|
-
`HTML/JS/CSS size increase: ${prettyBytes(
|
|
373
|
+
`HTML/SVG/JS/CSS size increase: ${prettyBytes(
|
|
374
|
+
sumSizesAfter - sumSizesBefore
|
|
375
|
+
)}`
|
|
391
376
|
);
|
|
392
377
|
log(`Total savings: ${prettyBytes(totalSavings)}`);
|
|
393
378
|
if (!dryRun) {
|
package/lib/subsetFonts.js
CHANGED
|
@@ -100,7 +100,7 @@ function getFontFaceDeclarationText(node, relations) {
|
|
|
100
100
|
|
|
101
101
|
// Takes the output of fontTracer
|
|
102
102
|
function groupTextsByFontFamilyProps(
|
|
103
|
-
|
|
103
|
+
htmlOrSvgAsset,
|
|
104
104
|
globalTextByPropsArray,
|
|
105
105
|
pageTextByPropsArray,
|
|
106
106
|
availableFontFaceDeclarations
|
|
@@ -138,7 +138,7 @@ function groupTextsByFontFamilyProps(
|
|
|
138
138
|
const fontUrl = getPreferredFontUrl(relations);
|
|
139
139
|
|
|
140
140
|
return {
|
|
141
|
-
|
|
141
|
+
htmlOrSvgAsset: textAndProps.htmlOrSvgAsset,
|
|
142
142
|
text: textAndProps.text,
|
|
143
143
|
props,
|
|
144
144
|
fontRelations: relations,
|
|
@@ -175,7 +175,7 @@ function groupTextsByFontFamilyProps(
|
|
|
175
175
|
texts,
|
|
176
176
|
pageText: uniqueChars(
|
|
177
177
|
textsPropsArray
|
|
178
|
-
.filter((textsProps) => textsProps.
|
|
178
|
+
.filter((textsProps) => textsProps.htmlOrSvgAsset === htmlOrSvgAsset)
|
|
179
179
|
.map((obj) => obj.text)
|
|
180
180
|
.join('')
|
|
181
181
|
),
|
|
@@ -188,7 +188,7 @@ function groupTextsByFontFamilyProps(
|
|
|
188
188
|
});
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
function getParents(
|
|
191
|
+
function getParents(asset, assetQuery) {
|
|
192
192
|
const assetMatcher = compileQuery(assetQuery);
|
|
193
193
|
const seenAssets = new Set();
|
|
194
194
|
const parents = [];
|
|
@@ -211,13 +211,13 @@ function getParents(assetGraph, asset, assetQuery) {
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
function asyncLoadStyleRelationWithFallback(
|
|
214
|
-
|
|
214
|
+
htmlOrSvgAsset,
|
|
215
215
|
originalRelation,
|
|
216
216
|
hrefType
|
|
217
217
|
) {
|
|
218
218
|
// Async load google font stylesheet
|
|
219
219
|
// Insert async CSS loading <script>
|
|
220
|
-
const asyncCssLoadingRelation =
|
|
220
|
+
const asyncCssLoadingRelation = htmlOrSvgAsset.addRelation(
|
|
221
221
|
{
|
|
222
222
|
type: 'HtmlScript',
|
|
223
223
|
hrefType: 'inline',
|
|
@@ -226,9 +226,9 @@ function asyncLoadStyleRelationWithFallback(
|
|
|
226
226
|
text: `
|
|
227
227
|
(function () {
|
|
228
228
|
var el = document.createElement('link');
|
|
229
|
-
el.href = '${
|
|
229
|
+
el.href = '${htmlOrSvgAsset.assetGraph.buildHref(
|
|
230
230
|
originalRelation.to.url,
|
|
231
|
-
|
|
231
|
+
htmlOrSvgAsset.url,
|
|
232
232
|
{ hrefType }
|
|
233
233
|
)}'.toString('url');
|
|
234
234
|
el.rel = 'stylesheet';
|
|
@@ -246,7 +246,7 @@ function asyncLoadStyleRelationWithFallback(
|
|
|
246
246
|
);
|
|
247
247
|
|
|
248
248
|
// Insert <noscript> fallback sync CSS loading
|
|
249
|
-
const noScriptFallbackRelation =
|
|
249
|
+
const noScriptFallbackRelation = htmlOrSvgAsset.addRelation(
|
|
250
250
|
{
|
|
251
251
|
type: 'HtmlNoscript',
|
|
252
252
|
to: {
|
|
@@ -269,7 +269,7 @@ function asyncLoadStyleRelationWithFallback(
|
|
|
269
269
|
|
|
270
270
|
noScriptFallbackRelation.inline();
|
|
271
271
|
asyncCssLoadingRelation.to.minify();
|
|
272
|
-
|
|
272
|
+
htmlOrSvgAsset.markDirty();
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
function getSubsetPromiseId(fontUsage, format) {
|
|
@@ -278,12 +278,12 @@ function getSubsetPromiseId(fontUsage, format) {
|
|
|
278
278
|
|
|
279
279
|
async function getSubsetsForFontUsage(
|
|
280
280
|
assetGraph,
|
|
281
|
-
|
|
281
|
+
htmlOrSvgAssetTextsWithProps,
|
|
282
282
|
formats
|
|
283
283
|
) {
|
|
284
284
|
const allFonts = [];
|
|
285
285
|
|
|
286
|
-
for (const item of
|
|
286
|
+
for (const item of htmlOrSvgAssetTextsWithProps) {
|
|
287
287
|
for (const fontUsage of item.fontUsages) {
|
|
288
288
|
if (!fontUsage.fontUrl) {
|
|
289
289
|
continue;
|
|
@@ -316,7 +316,7 @@ async function getSubsetsForFontUsage(
|
|
|
316
316
|
|
|
317
317
|
const subsetPromiseMap = {};
|
|
318
318
|
|
|
319
|
-
for (const item of
|
|
319
|
+
for (const item of htmlOrSvgAssetTextsWithProps) {
|
|
320
320
|
for (const fontUsage of item.fontUsages) {
|
|
321
321
|
const fontBuffer = originalFontBuffers[fontUsage.fontUrl];
|
|
322
322
|
const text = fontUsage.text;
|
|
@@ -552,7 +552,7 @@ async function subsetFonts(
|
|
|
552
552
|
fontDisplay = undefined;
|
|
553
553
|
}
|
|
554
554
|
|
|
555
|
-
const
|
|
555
|
+
const htmlOrSvgAssetTextsWithProps = [];
|
|
556
556
|
const subsetUrl = urltools.ensureTrailingSlash(assetGraph.root + subsetPath);
|
|
557
557
|
|
|
558
558
|
await assetGraph.applySourceMaps({ type: 'Css' });
|
|
@@ -578,11 +578,21 @@ async function subsetFonts(
|
|
|
578
578
|
// Collect texts by page
|
|
579
579
|
|
|
580
580
|
const memoizedGetCssRulesByProperty = memoizeSync(getCssRulesByProperty);
|
|
581
|
-
const
|
|
581
|
+
const htmlOrSvgAssets = assetGraph.findAssets({
|
|
582
|
+
$or: [
|
|
583
|
+
{
|
|
584
|
+
type: 'Html',
|
|
585
|
+
isInline: false,
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
type: 'Svg',
|
|
589
|
+
},
|
|
590
|
+
],
|
|
591
|
+
});
|
|
582
592
|
const traversalRelationQuery = {
|
|
583
593
|
$or: [
|
|
584
594
|
{
|
|
585
|
-
type: { $in: ['HtmlStyle', 'CssImport'] },
|
|
595
|
+
type: { $in: ['HtmlStyle', 'SvgStyle', 'CssImport'] },
|
|
586
596
|
},
|
|
587
597
|
{
|
|
588
598
|
to: {
|
|
@@ -597,20 +607,20 @@ async function subsetFonts(
|
|
|
597
607
|
// Minifying them along the way currently doesn't work because some of the
|
|
598
608
|
// manipulation is sensitive to the exact text contents. We should fix that.
|
|
599
609
|
const subsetFontsToBeMinified = new Set();
|
|
600
|
-
const
|
|
610
|
+
const fontFaceDeclarationsByHtmlOrSvgAsset = new Map();
|
|
601
611
|
const potentiallyOrphanedAssets = new Set();
|
|
602
612
|
|
|
603
613
|
const headlessBrowser = dynamic && new HeadlessBrowser({ console });
|
|
604
614
|
const globalTextByProps = [];
|
|
605
615
|
try {
|
|
606
|
-
for (const
|
|
616
|
+
for (const htmlOrSvgAsset of htmlOrSvgAssets) {
|
|
607
617
|
const accumulatedFontFaceDeclarations = [];
|
|
608
|
-
|
|
609
|
-
|
|
618
|
+
fontFaceDeclarationsByHtmlOrSvgAsset.set(
|
|
619
|
+
htmlOrSvgAsset,
|
|
610
620
|
accumulatedFontFaceDeclarations
|
|
611
621
|
);
|
|
612
622
|
assetGraph.eachAssetPreOrder(
|
|
613
|
-
|
|
623
|
+
htmlOrSvgAsset,
|
|
614
624
|
traversalRelationQuery,
|
|
615
625
|
(asset) => {
|
|
616
626
|
if (asset.type === 'Css' && asset.isLoaded) {
|
|
@@ -666,23 +676,25 @@ async function subsetFonts(
|
|
|
666
676
|
seenFontFaceCombos.add(key);
|
|
667
677
|
}
|
|
668
678
|
|
|
669
|
-
const textByProps = fontTracer(
|
|
679
|
+
const textByProps = fontTracer(htmlOrSvgAsset.parseTree, {
|
|
670
680
|
stylesheetsWithPredicates: gatherStylesheetsWithPredicates(
|
|
671
|
-
|
|
672
|
-
|
|
681
|
+
htmlOrSvgAsset.assetGraph,
|
|
682
|
+
htmlOrSvgAsset
|
|
673
683
|
),
|
|
674
684
|
getCssRulesByProperty: memoizedGetCssRulesByProperty,
|
|
675
|
-
|
|
685
|
+
asset: htmlOrSvgAsset,
|
|
676
686
|
});
|
|
677
687
|
if (headlessBrowser) {
|
|
678
|
-
textByProps.push(
|
|
688
|
+
textByProps.push(
|
|
689
|
+
...(await headlessBrowser.tracePage(htmlOrSvgAsset))
|
|
690
|
+
);
|
|
679
691
|
}
|
|
680
692
|
for (const textByPropsEntry of textByProps) {
|
|
681
|
-
textByPropsEntry.
|
|
693
|
+
textByPropsEntry.htmlOrSvgAsset = htmlOrSvgAsset;
|
|
682
694
|
}
|
|
683
695
|
globalTextByProps.push(...textByProps);
|
|
684
|
-
|
|
685
|
-
|
|
696
|
+
htmlOrSvgAssetTextsWithProps.push({
|
|
697
|
+
htmlOrSvgAsset,
|
|
686
698
|
textByProps,
|
|
687
699
|
accumulatedFontFaceDeclarations,
|
|
688
700
|
});
|
|
@@ -694,11 +706,11 @@ async function subsetFonts(
|
|
|
694
706
|
}
|
|
695
707
|
}
|
|
696
708
|
|
|
697
|
-
for (const
|
|
698
|
-
const {
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
709
|
+
for (const htmlOrSvgAssetTextsWithPropsEntry of htmlOrSvgAssetTextsWithProps) {
|
|
710
|
+
const { htmlOrSvgAsset, textByProps, accumulatedFontFaceDeclarations } =
|
|
711
|
+
htmlOrSvgAssetTextsWithPropsEntry;
|
|
712
|
+
htmlOrSvgAssetTextsWithPropsEntry.fontUsages = groupTextsByFontFamilyProps(
|
|
713
|
+
htmlOrSvgAsset,
|
|
702
714
|
globalTextByProps,
|
|
703
715
|
textByProps,
|
|
704
716
|
accumulatedFontFaceDeclarations
|
|
@@ -706,9 +718,9 @@ async function subsetFonts(
|
|
|
706
718
|
}
|
|
707
719
|
|
|
708
720
|
if (omitFallbacks) {
|
|
709
|
-
for (const
|
|
721
|
+
for (const htmlOrSvgAsset of htmlOrSvgAssets) {
|
|
710
722
|
const accumulatedFontFaceDeclarations =
|
|
711
|
-
|
|
723
|
+
fontFaceDeclarationsByHtmlOrSvgAsset.get(htmlOrSvgAsset);
|
|
712
724
|
// Remove the original @font-face rules:
|
|
713
725
|
for (const { relations } of accumulatedFontFaceDeclarations) {
|
|
714
726
|
for (const relation of relations) {
|
|
@@ -719,21 +731,21 @@ async function subsetFonts(
|
|
|
719
731
|
relation.remove();
|
|
720
732
|
}
|
|
721
733
|
}
|
|
722
|
-
|
|
734
|
+
htmlOrSvgAsset.markDirty();
|
|
723
735
|
}
|
|
724
736
|
}
|
|
725
737
|
|
|
726
738
|
if (fontDisplay) {
|
|
727
|
-
for (const
|
|
728
|
-
for (const fontUsage of
|
|
739
|
+
for (const htmlOrSvgAssetTextWithProps of htmlOrSvgAssetTextsWithProps) {
|
|
740
|
+
for (const fontUsage of htmlOrSvgAssetTextWithProps.fontUsages) {
|
|
729
741
|
fontUsage.props['font-display'] = fontDisplay;
|
|
730
742
|
}
|
|
731
743
|
}
|
|
732
744
|
}
|
|
733
745
|
|
|
734
746
|
// Generate codepoint sets for original font, the used subset and the unused subset
|
|
735
|
-
for (const
|
|
736
|
-
for (const fontUsage of
|
|
747
|
+
for (const htmlOrSvgAssetTextWithProps of htmlOrSvgAssetTextsWithProps) {
|
|
748
|
+
for (const fontUsage of htmlOrSvgAssetTextWithProps.fontUsages) {
|
|
737
749
|
const originalFont = assetGraph.findAssets({
|
|
738
750
|
url: fontUsage.fontUrl,
|
|
739
751
|
})[0];
|
|
@@ -762,24 +774,30 @@ async function subsetFonts(
|
|
|
762
774
|
|
|
763
775
|
if (onlyInfo) {
|
|
764
776
|
return {
|
|
765
|
-
fontInfo:
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
777
|
+
fontInfo: htmlOrSvgAssetTextsWithProps.map(
|
|
778
|
+
({ fontUsages, htmlOrSvgAsset }) => ({
|
|
779
|
+
assetFileName: htmlOrSvgAsset.nonInlineAncestor.urlOrDescription,
|
|
780
|
+
fontUsages: fontUsages,
|
|
781
|
+
})
|
|
782
|
+
),
|
|
769
783
|
};
|
|
770
784
|
}
|
|
771
785
|
|
|
772
786
|
// Generate subsets:
|
|
773
|
-
await getSubsetsForFontUsage(
|
|
787
|
+
await getSubsetsForFontUsage(
|
|
788
|
+
assetGraph,
|
|
789
|
+
htmlOrSvgAssetTextsWithProps,
|
|
790
|
+
formats
|
|
791
|
+
);
|
|
774
792
|
|
|
775
793
|
// Warn about missing glyphs
|
|
776
794
|
const missingGlyphsErrors = [];
|
|
777
795
|
|
|
778
796
|
for (const {
|
|
779
|
-
|
|
797
|
+
htmlOrSvgAsset,
|
|
780
798
|
fontUsages,
|
|
781
799
|
accumulatedFontFaceDeclarations,
|
|
782
|
-
} of
|
|
800
|
+
} of htmlOrSvgAssetTextsWithProps) {
|
|
783
801
|
for (const fontUsage of fontUsages) {
|
|
784
802
|
if (fontUsage.subsets) {
|
|
785
803
|
const characterSet = fontkit.create(
|
|
@@ -799,23 +817,23 @@ async function subsetFonts(
|
|
|
799
817
|
|
|
800
818
|
if (isMissing) {
|
|
801
819
|
let location;
|
|
802
|
-
const charIdx =
|
|
820
|
+
const charIdx = htmlOrSvgAsset.text.indexOf(char);
|
|
803
821
|
|
|
804
822
|
if (charIdx === -1) {
|
|
805
|
-
location = `${
|
|
823
|
+
location = `${htmlOrSvgAsset.urlOrDescription} (generated content)`;
|
|
806
824
|
} else {
|
|
807
825
|
const position = new LinesAndColumns(
|
|
808
|
-
|
|
826
|
+
htmlOrSvgAsset.text
|
|
809
827
|
).locationForIndex(charIdx);
|
|
810
|
-
location = `${
|
|
811
|
-
position.
|
|
812
|
-
}`;
|
|
828
|
+
location = `${htmlOrSvgAsset.urlOrDescription}:${
|
|
829
|
+
position.line + 1
|
|
830
|
+
}:${position.column + 1}`;
|
|
813
831
|
}
|
|
814
832
|
|
|
815
833
|
missingGlyphsErrors.push({
|
|
816
834
|
codePoint,
|
|
817
835
|
char,
|
|
818
|
-
|
|
836
|
+
htmlOrSvgAsset,
|
|
819
837
|
fontUsage,
|
|
820
838
|
location,
|
|
821
839
|
});
|
|
@@ -865,13 +883,13 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
865
883
|
|
|
866
884
|
let numFontUsagesWithSubset = 0;
|
|
867
885
|
for (const {
|
|
868
|
-
|
|
886
|
+
htmlOrSvgAsset,
|
|
869
887
|
fontUsages,
|
|
870
888
|
accumulatedFontFaceDeclarations,
|
|
871
|
-
} of
|
|
889
|
+
} of htmlOrSvgAssetTextsWithProps) {
|
|
872
890
|
const insertionPoint = assetGraph.findRelations({
|
|
873
|
-
type:
|
|
874
|
-
from:
|
|
891
|
+
type: `${htmlOrSvgAsset.type}Style`,
|
|
892
|
+
from: htmlOrSvgAsset,
|
|
875
893
|
})[0];
|
|
876
894
|
const subsetFontUsages = fontUsages.filter(
|
|
877
895
|
(fontUsage) => fontUsage.subsets
|
|
@@ -884,7 +902,7 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
884
902
|
for (const fontUsage of fontUsages) {
|
|
885
903
|
for (const relation of assetGraph.findRelations({
|
|
886
904
|
type: { $in: ['HtmlPrefetchLink', 'HtmlPreloadLink'] },
|
|
887
|
-
from:
|
|
905
|
+
from: htmlOrSvgAsset,
|
|
888
906
|
to: {
|
|
889
907
|
url: fontUsage.fontUrl,
|
|
890
908
|
},
|
|
@@ -911,7 +929,7 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
911
929
|
// Insert <link rel="preload">
|
|
912
930
|
unsubsettedFontUsagesToPreload.map((fontUsage) => {
|
|
913
931
|
// Always preload unsubsetted font files, they might be any format, so can't be clever here
|
|
914
|
-
return
|
|
932
|
+
return htmlOrSvgAsset.addRelation(
|
|
915
933
|
{
|
|
916
934
|
type: 'HtmlPreloadLink',
|
|
917
935
|
hrefType,
|
|
@@ -959,8 +977,8 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
959
977
|
if (
|
|
960
978
|
formats.length === 1 &&
|
|
961
979
|
fontUsage &&
|
|
962
|
-
(!inlineCss ||
|
|
963
|
-
|
|
980
|
+
(!inlineCss || htmlOrSvgAssetTextsWithProps.length === 1) &&
|
|
981
|
+
htmlOrSvgAssetTextsWithProps.every(({ fontUsages }) =>
|
|
964
982
|
fontUsages.some(
|
|
965
983
|
({ fontUrl, pageText }) => pageText && fontUrl === fontUsage.fontUrl
|
|
966
984
|
)
|
|
@@ -1051,22 +1069,25 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1051
1069
|
// - https://caniuse.com/#search=woff2
|
|
1052
1070
|
// - https://caniuse.com/#search=preload
|
|
1053
1071
|
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1072
|
+
if (htmlOrSvgAsset.type === 'Html') {
|
|
1073
|
+
htmlOrSvgAsset.addRelation(
|
|
1074
|
+
{
|
|
1075
|
+
type: 'HtmlPreloadLink',
|
|
1076
|
+
hrefType,
|
|
1077
|
+
to: fontAsset,
|
|
1078
|
+
as: 'font',
|
|
1079
|
+
},
|
|
1080
|
+
'before',
|
|
1081
|
+
insertionPoint
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
1064
1084
|
}
|
|
1065
1085
|
}
|
|
1066
|
-
const cssRelation =
|
|
1086
|
+
const cssRelation = htmlOrSvgAsset.addRelation(
|
|
1067
1087
|
{
|
|
1068
|
-
type:
|
|
1069
|
-
hrefType:
|
|
1088
|
+
type: `${htmlOrSvgAsset.type}Style`,
|
|
1089
|
+
hrefType:
|
|
1090
|
+
inlineCss || htmlOrSvgAsset.type === 'Svg' ? 'inline' : hrefType,
|
|
1070
1091
|
to: cssAsset,
|
|
1071
1092
|
},
|
|
1072
1093
|
'before',
|
|
@@ -1075,7 +1096,7 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1075
1096
|
|
|
1076
1097
|
if (!omitFallbacks && inlineCss && unusedVariantsCss) {
|
|
1077
1098
|
// The fallback CSS for unused variants needs to go into its own stylesheet after the crude version of the JS-based preload "polyfill"
|
|
1078
|
-
const cssAsset =
|
|
1099
|
+
const cssAsset = htmlOrSvgAsset.addRelation(
|
|
1079
1100
|
{
|
|
1080
1101
|
type: 'HtmlStyle',
|
|
1081
1102
|
to: {
|
|
@@ -1100,9 +1121,9 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1100
1121
|
|
|
1101
1122
|
// Lazy load the original @font-face declarations of self-hosted fonts (unless omitFallbacks)
|
|
1102
1123
|
const originalRelations = new Set();
|
|
1103
|
-
for (const
|
|
1124
|
+
for (const htmlOrSvgAsset of htmlOrSvgAssets) {
|
|
1104
1125
|
const accumulatedFontFaceDeclarations =
|
|
1105
|
-
|
|
1126
|
+
fontFaceDeclarationsByHtmlOrSvgAsset.get(htmlOrSvgAsset);
|
|
1106
1127
|
// TODO: Maybe group by media?
|
|
1107
1128
|
const containedRelationsByFontFaceRule = new Map();
|
|
1108
1129
|
for (const { relations } of accumulatedFontFaceDeclarations) {
|
|
@@ -1151,18 +1172,20 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1151
1172
|
cssAsset.url = cssAssetUrl;
|
|
1152
1173
|
}
|
|
1153
1174
|
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1175
|
+
if (htmlOrSvgAsset.type === 'Html') {
|
|
1176
|
+
// Create a <link rel="stylesheet"> that asyncLoadStyleRelationWithFallback can convert to async with noscript fallback:
|
|
1177
|
+
const fallbackHtmlStyle = htmlOrSvgAsset.addRelation({
|
|
1178
|
+
type: 'HtmlStyle',
|
|
1179
|
+
to: cssAsset,
|
|
1180
|
+
});
|
|
1159
1181
|
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1182
|
+
asyncLoadStyleRelationWithFallback(
|
|
1183
|
+
htmlOrSvgAsset,
|
|
1184
|
+
fallbackHtmlStyle,
|
|
1185
|
+
hrefType
|
|
1186
|
+
);
|
|
1187
|
+
relationsToRemove.add(fallbackHtmlStyle);
|
|
1188
|
+
}
|
|
1166
1189
|
}
|
|
1167
1190
|
}
|
|
1168
1191
|
|
|
@@ -1199,12 +1222,14 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1199
1222
|
|
|
1200
1223
|
if (googleFontStylesheetRelation.type === 'CssImport') {
|
|
1201
1224
|
// Gather Html parents. Relevant if we are dealing with CSS @import relations
|
|
1202
|
-
htmlParents = getParents(
|
|
1203
|
-
type: 'Html',
|
|
1225
|
+
htmlParents = getParents(googleFontStylesheetRelation.to, {
|
|
1226
|
+
type: { $in: ['Html', 'Svg'] },
|
|
1204
1227
|
isInline: false,
|
|
1205
1228
|
isLoaded: true,
|
|
1206
1229
|
});
|
|
1207
|
-
} else if (
|
|
1230
|
+
} else if (
|
|
1231
|
+
['Html', 'Svg'].includes(googleFontStylesheetRelation.from.type)
|
|
1232
|
+
) {
|
|
1208
1233
|
htmlParents = [googleFontStylesheetRelation.from];
|
|
1209
1234
|
} else {
|
|
1210
1235
|
htmlParents = [];
|
|
@@ -1235,18 +1260,20 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1235
1260
|
}
|
|
1236
1261
|
const selfHostedFallbackRelation = htmlParent.addRelation(
|
|
1237
1262
|
{
|
|
1238
|
-
type:
|
|
1263
|
+
type: `${htmlParent.type}Style`,
|
|
1239
1264
|
to: selfHostedGoogleFontsCssAsset,
|
|
1240
1265
|
hrefType,
|
|
1241
1266
|
},
|
|
1242
1267
|
'lastInBody'
|
|
1243
1268
|
);
|
|
1244
1269
|
relationsToRemove.add(selfHostedFallbackRelation);
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1270
|
+
if (htmlParent.type === 'Html') {
|
|
1271
|
+
asyncLoadStyleRelationWithFallback(
|
|
1272
|
+
htmlParent,
|
|
1273
|
+
selfHostedFallbackRelation,
|
|
1274
|
+
hrefType
|
|
1275
|
+
);
|
|
1276
|
+
}
|
|
1250
1277
|
}
|
|
1251
1278
|
relationsToRemove.add(googleFontStylesheetRelation);
|
|
1252
1279
|
}
|
|
@@ -1263,7 +1290,7 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1263
1290
|
|
|
1264
1291
|
const webfontNameMap = {};
|
|
1265
1292
|
|
|
1266
|
-
for (const { fontUsages } of
|
|
1293
|
+
for (const { fontUsages } of htmlOrSvgAssetTextsWithProps) {
|
|
1267
1294
|
for (const { subsets, fontFamilies, props } of fontUsages) {
|
|
1268
1295
|
if (subsets) {
|
|
1269
1296
|
for (const fontFamily of fontFamilies) {
|
|
@@ -1276,7 +1303,37 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1276
1303
|
}
|
|
1277
1304
|
|
|
1278
1305
|
let customPropertyDefinitions; // Avoid computing this unless necessary
|
|
1279
|
-
// Inject subset font name before original webfont
|
|
1306
|
+
// Inject subset font name before original webfont in SVG font-family attributes
|
|
1307
|
+
const svgAssets = assetGraph.findAssets({ type: 'Svg' });
|
|
1308
|
+
for (const svgAsset of svgAssets) {
|
|
1309
|
+
let changesMade = false;
|
|
1310
|
+
for (const element of Array.from(
|
|
1311
|
+
svgAsset.parseTree.querySelectorAll('[font-family]')
|
|
1312
|
+
)) {
|
|
1313
|
+
const fontFamilies = cssListHelpers.splitByCommas(
|
|
1314
|
+
element.getAttribute('font-family')
|
|
1315
|
+
);
|
|
1316
|
+
for (let i = 0; i < fontFamilies.length; i += 1) {
|
|
1317
|
+
const subsetFontFamily =
|
|
1318
|
+
webfontNameMap[fontFamily.parse(fontFamilies[i])[0].toLowerCase()];
|
|
1319
|
+
if (subsetFontFamily && !fontFamilies.includes(subsetFontFamily)) {
|
|
1320
|
+
fontFamilies.splice(
|
|
1321
|
+
i,
|
|
1322
|
+
omitFallbacks ? 1 : 0,
|
|
1323
|
+
cssQuoteIfNecessary(subsetFontFamily)
|
|
1324
|
+
);
|
|
1325
|
+
i += 1;
|
|
1326
|
+
element.setAttribute('font-family', fontFamilies.join(', '));
|
|
1327
|
+
changesMade = true;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
if (changesMade) {
|
|
1332
|
+
svgAsset.markDirty();
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
// Inject subset font name before original webfont in CSS
|
|
1280
1337
|
const cssAssets = assetGraph.findAssets({
|
|
1281
1338
|
type: 'Css',
|
|
1282
1339
|
isLoaded: true,
|
|
@@ -1408,10 +1465,12 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1408
1465
|
|
|
1409
1466
|
// Hand out some useful info about the detected subsets:
|
|
1410
1467
|
return {
|
|
1411
|
-
fontInfo:
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1468
|
+
fontInfo: htmlOrSvgAssetTextsWithProps.map(
|
|
1469
|
+
({ fontUsages, htmlOrSvgAsset }) => ({
|
|
1470
|
+
assetFileName: htmlOrSvgAsset.nonInlineAncestor.urlOrDescription,
|
|
1471
|
+
fontUsages: fontUsages.map((fontUsage) => _.omit(fontUsage, 'subsets')),
|
|
1472
|
+
})
|
|
1473
|
+
),
|
|
1415
1474
|
};
|
|
1416
1475
|
}
|
|
1417
1476
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "subfont",
|
|
3
|
-
"version": "6.1
|
|
3
|
+
"version": "6.3.1",
|
|
4
4
|
"description": "Speeds up your pages initial paint by automatically subsetting local or Google fonts and loading them optimally",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=10.0.0"
|
|
@@ -47,21 +47,21 @@
|
|
|
47
47
|
"homepage": "https://github.com/Munter/subfont#readme",
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@gustavnikolaj/async-main-wrap": "^3.0.1",
|
|
50
|
-
"assetgraph": "^
|
|
50
|
+
"assetgraph": "^7.2.0",
|
|
51
51
|
"browserslist": "^4.13.0",
|
|
52
52
|
"css-font-parser": "^0.3.0",
|
|
53
53
|
"css-font-weight-names": "^0.2.1",
|
|
54
54
|
"css-list-helpers": "^2.0.0",
|
|
55
55
|
"font-family-papandreou": "^0.2.0-patch2",
|
|
56
56
|
"font-snapper": "^1.2.0",
|
|
57
|
-
"font-tracer": "^
|
|
57
|
+
"font-tracer": "^3.0.1",
|
|
58
58
|
"fontkit": "^1.8.0",
|
|
59
59
|
"fontverter": "^2.0.0",
|
|
60
60
|
"gettemporaryfilepath": "^1.0.1",
|
|
61
61
|
"lines-and-columns": "^1.1.6",
|
|
62
62
|
"lodash": "^4.17.15",
|
|
63
63
|
"memoizesync": "^1.1.1",
|
|
64
|
-
"postcss": "^
|
|
64
|
+
"postcss": "^8.3.11",
|
|
65
65
|
"postcss-value-parser": "^4.0.2",
|
|
66
66
|
"pretty-bytes": "^5.1.0",
|
|
67
67
|
"puppeteer-core": "^8.0.0",
|