subfont 6.8.0 → 6.11.0
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 +24 -0
- package/lib/getCssRulesByProperty.js +50 -24
- package/lib/parseFontVariationSettings.js +39 -0
- package/lib/subsetFonts.js +419 -169
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
### v6.11.0 (2022-09-04)
|
|
2
|
+
|
|
3
|
+
- [Treat wdth as a standard axis \(but unsupported for now\)](https://github.com/Munter/subfont/commit/20d539750448134cca0a82f2cc98f85d9a6be068) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
4
|
+
- [Detect unused ital variation axis ranges](https://github.com/Munter/subfont/commit/b848b21b66cdbcd0ff313690ba49a4bc9b9f90a4) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
5
|
+
- [Don't assume that the display name of a variation axis equals its name](https://github.com/Munter/subfont/commit/4fcf7df801de01b2d3aef3d5f1ea3d697779ffda) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
6
|
+
- [Detect unused wght variation axis ranges](https://github.com/Munter/subfont/commit/6a964d609f21bf2c9dad96da1d16d9c32c28dbfd) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
7
|
+
|
|
8
|
+
### v6.10.0 (2022-08-29)
|
|
9
|
+
|
|
10
|
+
- [Update @hookun\/parse-animation-shorthand to ^0.1.4 to make sure we get the fix for hookhookun\/parse-animation-shorthand\#16](https://github.com/Munter/subfont/commit/4db17826245e289e987c6dc02d3f67ebf5891e6f) ([Andreas Lind](mailto:andreas.lind@workday.com))
|
|
11
|
+
- [Disable notice about unused variation axis ranges when there's an out-of-bounds cubic-bezier animation timing function in play](https://github.com/Munter/subfont/commit/1cf4ff46bb6099f8df8602968c7dc0fefb1c1f21) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
12
|
+
- [Update font-tracer to ^3.6.0](https://github.com/Munter/subfont/commit/2a47adc03122aad50ba2a01f116a2d1fe0f2b29e) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
13
|
+
- [Remove accidentally committed commented out code](https://github.com/Munter/subfont/commit/e0c677f9b2c7a9af123045cf29bc78a4f9a56550) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
14
|
+
- [Warn about unused variation axis ranges, not just fully unused axes](https://github.com/Munter/subfont/commit/32fc472327b1c8fc87090d4c1e5f1085533ad8fa) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
15
|
+
- [+13 more](https://github.com/Munter/subfont/compare/v6.9.0...v6.10.0)
|
|
16
|
+
|
|
17
|
+
### v6.9.0 (2022-08-07)
|
|
18
|
+
|
|
19
|
+
- [Update font-tracer to ^3.3.0 Adds support for tracing ::marker, fixes \#166](https://github.com/Munter/subfont/commit/e46c79bac9cb3e62c70deb357823b7963114863b) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
20
|
+
- [Move some code into a collectTextsByPage function](https://github.com/Munter/subfont/commit/1dff3819fb80793f23a3cc37f9ff58bafffa4efc) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
21
|
+
- [Move the missing glyph detection to a function](https://github.com/Munter/subfont/commit/38bf36eba4370ecdbc777cb2457696b9bc7c7d1c) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
22
|
+
- [Fix typo causing regexp to not be matched correctly](https://github.com/Munter/subfont/commit/05137708b5c48af305f3119deb836b1cd9fed683) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
23
|
+
- [Remove delayed minification of CSS, seems like it's no longer necessary](https://github.com/Munter/subfont/commit/b98976b24c3a859ffbdd2a43f9f05e5048175f5a) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
24
|
+
|
|
1
25
|
### v6.8.0 (2022-07-28)
|
|
2
26
|
|
|
3
27
|
- [Update assetgraph to ^7.8.1](https://github.com/Munter/subfont/commit/888a97912f98bd937a53b7bec0f39d50ddc96023) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const specificity = require('specificity');
|
|
2
2
|
const postcss = require('postcss');
|
|
3
3
|
const unquote = require('./unquote');
|
|
4
|
+
const parseAnimationShorthand = require('@hookun/parse-animation-shorthand');
|
|
4
5
|
|
|
5
6
|
const counterRendererNames = new Set([
|
|
6
7
|
'none',
|
|
@@ -144,34 +145,59 @@ function getCssRulesByProperty(properties, cssSource, existingPredicates) {
|
|
|
144
145
|
});
|
|
145
146
|
});
|
|
146
147
|
}
|
|
147
|
-
} else if (
|
|
148
|
-
propName === 'animation' &&
|
|
149
|
-
properties.includes('animation-name')
|
|
150
|
-
) {
|
|
148
|
+
} else if (propName === 'animation') {
|
|
151
149
|
// Shorthand
|
|
152
|
-
const
|
|
150
|
+
const parsedAnimation = parseAnimationShorthand.parseSingle(node.value);
|
|
153
151
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
152
|
+
if (properties.includes('animation-name')) {
|
|
153
|
+
// Split up combined selectors as they might have different specificity
|
|
154
|
+
specificity
|
|
155
|
+
.calculate(node.parent.selector)
|
|
156
|
+
.forEach((specificityObject) => {
|
|
157
|
+
const isStyleAttribute =
|
|
158
|
+
specificityObject.selector === 'bogusselector';
|
|
160
159
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
160
|
+
rulesByProperty['animation-name'].push({
|
|
161
|
+
predicates: getCurrentPredicates(),
|
|
162
|
+
namespaceURI: defaultNamespaceURI,
|
|
163
|
+
selector: isStyleAttribute
|
|
164
|
+
? undefined
|
|
165
|
+
: specificityObject.selector.trim(),
|
|
166
|
+
specificityArray: isStyleAttribute
|
|
167
|
+
? [1, 0, 0, 0]
|
|
168
|
+
: specificityObject.specificityArray,
|
|
169
|
+
prop: 'animation-name',
|
|
170
|
+
value: parsedAnimation.name,
|
|
171
|
+
important: !!node.important,
|
|
172
|
+
});
|
|
173
173
|
});
|
|
174
|
-
|
|
174
|
+
}
|
|
175
|
+
if (properties.includes('animation-timing-function')) {
|
|
176
|
+
// Split up combined selectors as they might have different specificity
|
|
177
|
+
specificity
|
|
178
|
+
.calculate(node.parent.selector)
|
|
179
|
+
.forEach((specificityObject) => {
|
|
180
|
+
const isStyleAttribute =
|
|
181
|
+
specificityObject.selector === 'bogusselector';
|
|
182
|
+
|
|
183
|
+
rulesByProperty['animation-timing-function'].push({
|
|
184
|
+
predicates: getCurrentPredicates(),
|
|
185
|
+
namespaceURI: defaultNamespaceURI,
|
|
186
|
+
selector: isStyleAttribute
|
|
187
|
+
? undefined
|
|
188
|
+
: specificityObject.selector.trim(),
|
|
189
|
+
specificityArray: isStyleAttribute
|
|
190
|
+
? [1, 0, 0, 0]
|
|
191
|
+
: specificityObject.specificityArray,
|
|
192
|
+
prop: 'animation-timing-function',
|
|
193
|
+
value: parseAnimationShorthand.serialize({
|
|
194
|
+
name: '',
|
|
195
|
+
timingFunction: parsedAnimation.timingFunction,
|
|
196
|
+
}),
|
|
197
|
+
important: !!node.important,
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
}
|
|
175
201
|
} else if (propName === 'transition') {
|
|
176
202
|
// Shorthand
|
|
177
203
|
const transitionProperties = [];
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const postcssValueParser = require('postcss-value-parser');
|
|
2
|
+
|
|
3
|
+
module.exports = function* parseFontVariationSettings(value) {
|
|
4
|
+
let state = 'BEFORE_AXIS_NAME';
|
|
5
|
+
let axisName;
|
|
6
|
+
for (const token of postcssValueParser(value).nodes) {
|
|
7
|
+
if (token.type === 'space') {
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
switch (state) {
|
|
11
|
+
case 'BEFORE_AXIS_NAME': {
|
|
12
|
+
if (token.type !== 'string') {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
axisName = token.value;
|
|
16
|
+
state = 'AFTER_AXIS_NAME';
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
case 'AFTER_AXIS_NAME': {
|
|
20
|
+
if (token.type === 'word') {
|
|
21
|
+
const axisValue = parseFloat(token.value);
|
|
22
|
+
if (!isNaN(axisValue)) {
|
|
23
|
+
yield [axisName, axisValue];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
state = 'AFTER_AXIS_VALUE';
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
case 'AFTER_AXIS_VALUE': {
|
|
30
|
+
if (token.type !== 'div' || token.value !== ',') {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
axisName = undefined;
|
|
34
|
+
state = 'BEFORE_AXIS_NAME';
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
};
|
package/lib/subsetFonts.js
CHANGED
|
@@ -13,13 +13,14 @@ const HeadlessBrowser = require('./HeadlessBrowser');
|
|
|
13
13
|
const gatherStylesheetsWithPredicates = require('./gatherStylesheetsWithPredicates');
|
|
14
14
|
const findCustomPropertyDefinitions = require('./findCustomPropertyDefinitions');
|
|
15
15
|
const extractReferencedCustomPropertyNames = require('./extractReferencedCustomPropertyNames');
|
|
16
|
+
const parseFontVariationSettings = require('./parseFontVariationSettings');
|
|
17
|
+
const parseAnimationShorthand = require('@hookun/parse-animation-shorthand');
|
|
16
18
|
const stripLocalTokens = require('./stripLocalTokens');
|
|
17
19
|
const injectSubsetDefinitions = require('./injectSubsetDefinitions');
|
|
18
20
|
const cssFontParser = require('css-font-parser');
|
|
19
21
|
const cssListHelpers = require('css-list-helpers');
|
|
20
22
|
const LinesAndColumns = require('lines-and-columns').default;
|
|
21
23
|
const fontkit = require('fontkit');
|
|
22
|
-
const fontFamily = require('font-family-papandreou');
|
|
23
24
|
const crypto = require('crypto');
|
|
24
25
|
|
|
25
26
|
const unquote = require('./unquote');
|
|
@@ -41,6 +42,14 @@ const contentTypeByFontFormat = {
|
|
|
41
42
|
truetype: 'font/ttf',
|
|
42
43
|
};
|
|
43
44
|
|
|
45
|
+
function stringifyFontFamily(name) {
|
|
46
|
+
if (/[^a-z0-9_-]/i.test(name)) {
|
|
47
|
+
return name.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
48
|
+
} else {
|
|
49
|
+
return name;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
44
53
|
function uniqueChars(text) {
|
|
45
54
|
return [...new Set([...text])].sort().join('');
|
|
46
55
|
}
|
|
@@ -79,6 +88,21 @@ function getPreferredFontUrl(cssFontFaceSrcRelations = []) {
|
|
|
79
88
|
}
|
|
80
89
|
}
|
|
81
90
|
|
|
91
|
+
function isOutOfBoundsAnimationTimingFunction(animationTimingFunctionStr) {
|
|
92
|
+
if (typeof animationTimingFunctionStr !== 'string') {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const { timingFunction } = parseAnimationShorthand.parseSingle(
|
|
96
|
+
`${animationTimingFunctionStr} ignored-name`
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
if (timingFunction.type === 'cubic-bezier') {
|
|
100
|
+
const [, y1, , y2] = timingFunction.value;
|
|
101
|
+
return y1 > 1 || y1 < 0 || y2 > 1 || y2 < 0;
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
82
106
|
// Hack to extract '@font-face { ... }' with all absolute urls
|
|
83
107
|
function getFontFaceDeclarationText(node, relations) {
|
|
84
108
|
const originalHrefTypeByRelation = new Map();
|
|
@@ -145,8 +169,8 @@ function groupTextsByFontFamilyProps(
|
|
|
145
169
|
return [];
|
|
146
170
|
}
|
|
147
171
|
// Find all the families in the traced font-family that we have @font-face declarations for:
|
|
148
|
-
const families =
|
|
149
|
-
.
|
|
172
|
+
const families = cssFontParser
|
|
173
|
+
.parseFontFamily(family)
|
|
150
174
|
.filter((family) =>
|
|
151
175
|
availableFontFaceDeclarations.some(
|
|
152
176
|
(fontFace) =>
|
|
@@ -159,7 +183,7 @@ function groupTextsByFontFamilyProps(
|
|
|
159
183
|
availableFontFaceDeclarations,
|
|
160
184
|
{
|
|
161
185
|
...textAndProps.props,
|
|
162
|
-
'font-family':
|
|
186
|
+
'font-family': stringifyFontFamily(family),
|
|
163
187
|
}
|
|
164
188
|
);
|
|
165
189
|
|
|
@@ -170,9 +194,27 @@ function groupTextsByFontFamilyProps(
|
|
|
170
194
|
const { relations, ...props } = activeFontFaceDeclaration;
|
|
171
195
|
const fontUrl = getPreferredFontUrl(relations);
|
|
172
196
|
|
|
197
|
+
const fontStyle = normalizeFontPropertyValue(
|
|
198
|
+
'font-style',
|
|
199
|
+
textAndProps.props['font-style']
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
let fontWeight = normalizeFontPropertyValue(
|
|
203
|
+
'font-weight',
|
|
204
|
+
textAndProps.props['font-weight']
|
|
205
|
+
);
|
|
206
|
+
if (fontWeight === 'normal') {
|
|
207
|
+
fontWeight = 400;
|
|
208
|
+
}
|
|
209
|
+
|
|
173
210
|
return {
|
|
174
211
|
htmlOrSvgAsset: textAndProps.htmlOrSvgAsset,
|
|
175
212
|
text: textAndProps.text,
|
|
213
|
+
fontVariationSettings: textAndProps.props['font-variation-settings'],
|
|
214
|
+
fontStyle,
|
|
215
|
+
fontWeight,
|
|
216
|
+
animationTimingFunction:
|
|
217
|
+
textAndProps.props['animation-timing-function'],
|
|
176
218
|
props,
|
|
177
219
|
fontRelations: relations,
|
|
178
220
|
fontUrl,
|
|
@@ -190,6 +232,20 @@ function groupTextsByFontFamilyProps(
|
|
|
190
232
|
const fontFamilies = new Set(
|
|
191
233
|
textsPropsArray.map((obj) => obj.props['font-family'])
|
|
192
234
|
);
|
|
235
|
+
const fontStyles = new Set(textsPropsArray.map((obj) => obj.fontStyle));
|
|
236
|
+
const fontWeights = new Set(textsPropsArray.map((obj) => obj.fontWeight));
|
|
237
|
+
const fontVariationSettings = new Set(
|
|
238
|
+
textsPropsArray
|
|
239
|
+
.map((obj) => obj.fontVariationSettings)
|
|
240
|
+
.filter(
|
|
241
|
+
(fontVariationSettings) =>
|
|
242
|
+
fontVariationSettings &&
|
|
243
|
+
fontVariationSettings.toLowerCase() !== 'normal'
|
|
244
|
+
)
|
|
245
|
+
);
|
|
246
|
+
const hasOutOfBoundsAnimationTimingFunction = textsPropsArray.some((obj) =>
|
|
247
|
+
isOutOfBoundsAnimationTimingFunction(obj.animationTimingFunction)
|
|
248
|
+
);
|
|
193
249
|
|
|
194
250
|
let smallestOriginalSize;
|
|
195
251
|
let smallestOriginalFormat;
|
|
@@ -217,6 +273,10 @@ function groupTextsByFontFamilyProps(
|
|
|
217
273
|
props: { ...textsPropsArray[0].props },
|
|
218
274
|
fontUrl,
|
|
219
275
|
fontFamilies,
|
|
276
|
+
fontStyles,
|
|
277
|
+
fontWeights,
|
|
278
|
+
fontVariationSettings,
|
|
279
|
+
hasOutOfBoundsAnimationTimingFunction,
|
|
220
280
|
preload,
|
|
221
281
|
};
|
|
222
282
|
});
|
|
@@ -497,9 +557,9 @@ async function createSelfHostedGoogleFontsCssAsset(
|
|
|
497
557
|
assetGraph,
|
|
498
558
|
googleFontsCssAsset,
|
|
499
559
|
formats,
|
|
500
|
-
hrefType
|
|
560
|
+
hrefType,
|
|
561
|
+
subsetUrl
|
|
501
562
|
) {
|
|
502
|
-
const baseUrl = assetGraph.resolveUrl(assetGraph.root, '/subfont/');
|
|
503
563
|
const lines = [];
|
|
504
564
|
for (const cssFontFaceSrc of assetGraph.findRelations({
|
|
505
565
|
from: googleFontsCssAsset,
|
|
@@ -516,9 +576,12 @@ async function createSelfHostedGoogleFontsCssAsset(
|
|
|
516
576
|
const srcFragments = [];
|
|
517
577
|
for (const format of formats) {
|
|
518
578
|
const rawSrc = await fontverter.convert(cssFontFaceSrc.to.rawSrc, format);
|
|
519
|
-
const url =
|
|
520
|
-
|
|
521
|
-
|
|
579
|
+
const url = assetGraph.resolveUrl(
|
|
580
|
+
subsetUrl,
|
|
581
|
+
`${cssFontFaceSrc.to.baseName}-${md5HexPrefix(rawSrc)}${
|
|
582
|
+
extensionByFormat[format]
|
|
583
|
+
}`
|
|
584
|
+
);
|
|
522
585
|
const fontAsset =
|
|
523
586
|
assetGraph.findAssets({ url })[0] ||
|
|
524
587
|
(await assetGraph.addAsset({
|
|
@@ -526,7 +589,7 @@ async function createSelfHostedGoogleFontsCssAsset(
|
|
|
526
589
|
rawSrc,
|
|
527
590
|
}));
|
|
528
591
|
srcFragments.push(
|
|
529
|
-
`url(${assetGraph.buildHref(fontAsset.url,
|
|
592
|
+
`url(${assetGraph.buildHref(fontAsset.url, subsetUrl, {
|
|
530
593
|
hrefType,
|
|
531
594
|
})}) format('${format}')`
|
|
532
595
|
);
|
|
@@ -542,7 +605,7 @@ async function createSelfHostedGoogleFontsCssAsset(
|
|
|
542
605
|
const text = lines.join('\n');
|
|
543
606
|
const fallbackAsset = assetGraph.addAsset({
|
|
544
607
|
type: 'Css',
|
|
545
|
-
url:
|
|
608
|
+
url: assetGraph.resolveUrl(subsetUrl, `fallback-${md5HexPrefix(text)}.css`),
|
|
546
609
|
text,
|
|
547
610
|
});
|
|
548
611
|
return fallbackAsset;
|
|
@@ -574,62 +637,279 @@ function cssAssetIsEmpty(cssAsset) {
|
|
|
574
637
|
);
|
|
575
638
|
}
|
|
576
639
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
640
|
+
function parseFontWeightRange(str) {
|
|
641
|
+
let minFontWeight = 400;
|
|
642
|
+
let maxFontWeight = 400;
|
|
643
|
+
const fontWeightTokens = str.split(/\s+/).map((str) => parseFloat(str));
|
|
644
|
+
if (
|
|
645
|
+
[1, 2].includes(fontWeightTokens.length) &&
|
|
646
|
+
!fontWeightTokens.some(isNaN)
|
|
647
|
+
) {
|
|
648
|
+
minFontWeight = maxFontWeight = fontWeightTokens[0];
|
|
649
|
+
if (fontWeightTokens.length === 2) {
|
|
650
|
+
maxFontWeight = fontWeightTokens[1];
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
return [minFontWeight, maxFontWeight];
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
function warnAboutMissingGlyphs(htmlOrSvgAssetTextsWithProps, assetGraph) {
|
|
657
|
+
const missingGlyphsErrors = [];
|
|
658
|
+
|
|
659
|
+
for (const {
|
|
660
|
+
htmlOrSvgAsset,
|
|
661
|
+
fontUsages,
|
|
662
|
+
accumulatedFontFaceDeclarations,
|
|
663
|
+
} of htmlOrSvgAssetTextsWithProps) {
|
|
664
|
+
for (const fontUsage of fontUsages) {
|
|
665
|
+
if (fontUsage.subsets) {
|
|
666
|
+
const characterSet = fontkit.create(
|
|
667
|
+
Object.values(fontUsage.subsets)[0]
|
|
668
|
+
).characterSet;
|
|
669
|
+
|
|
670
|
+
let missedAny = false;
|
|
671
|
+
for (const char of [...fontUsage.pageText]) {
|
|
672
|
+
// Turns out that browsers don't mind that these are missing:
|
|
673
|
+
if (char === '\t' || char === '\n') {
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
const codePoint = char.codePointAt(0);
|
|
678
|
+
|
|
679
|
+
const isMissing = !characterSet.includes(codePoint);
|
|
680
|
+
|
|
681
|
+
if (isMissing) {
|
|
682
|
+
let location;
|
|
683
|
+
const charIdx = htmlOrSvgAsset.text.indexOf(char);
|
|
684
|
+
|
|
685
|
+
if (charIdx === -1) {
|
|
686
|
+
location = `${htmlOrSvgAsset.urlOrDescription} (generated content)`;
|
|
687
|
+
} else {
|
|
688
|
+
const position = new LinesAndColumns(
|
|
689
|
+
htmlOrSvgAsset.text
|
|
690
|
+
).locationForIndex(charIdx);
|
|
691
|
+
location = `${htmlOrSvgAsset.urlOrDescription}:${
|
|
692
|
+
position.line + 1
|
|
693
|
+
}:${position.column + 1}`;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
missingGlyphsErrors.push({
|
|
697
|
+
codePoint,
|
|
698
|
+
char,
|
|
699
|
+
htmlOrSvgAsset,
|
|
700
|
+
fontUsage,
|
|
701
|
+
location,
|
|
702
|
+
});
|
|
703
|
+
missedAny = true;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (missedAny) {
|
|
707
|
+
const fontFaces = accumulatedFontFaceDeclarations.filter((fontFace) =>
|
|
708
|
+
fontUsage.fontFamilies.has(fontFace['font-family'])
|
|
709
|
+
);
|
|
710
|
+
for (const fontFace of fontFaces) {
|
|
711
|
+
const cssFontFaceSrc = fontFace.relations[0];
|
|
712
|
+
const fontFaceDeclaration = cssFontFaceSrc.node;
|
|
713
|
+
if (
|
|
714
|
+
!fontFaceDeclaration.some((node) => node.prop === 'unicode-range')
|
|
715
|
+
) {
|
|
716
|
+
fontFaceDeclaration.append({
|
|
717
|
+
prop: 'unicode-range',
|
|
718
|
+
value: unicodeRange(fontUsage.codepoints.original),
|
|
719
|
+
});
|
|
720
|
+
cssFontFaceSrc.from.markDirty();
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
if (missingGlyphsErrors.length) {
|
|
729
|
+
const errorLog = missingGlyphsErrors.map(
|
|
730
|
+
({ char, fontUsage, location }) =>
|
|
731
|
+
`- \\u{${char.codePointAt(0).toString(16)}} (${char}) in font-family '${
|
|
732
|
+
fontUsage.props['font-family']
|
|
733
|
+
}' (${fontUsage.props['font-weight']}/${
|
|
734
|
+
fontUsage.props['font-style']
|
|
735
|
+
}) at ${location}`
|
|
736
|
+
);
|
|
737
|
+
|
|
738
|
+
const message = `Missing glyph fallback detected.
|
|
739
|
+
When your primary webfont doesn't contain the glyphs you use, browsers that don't support unicode-range will load your fallback fonts, which will be a potential waste of bandwidth.
|
|
740
|
+
These glyphs are used on your site, but they don't exist in the font you applied to them:`;
|
|
741
|
+
|
|
742
|
+
assetGraph.info(new Error(`${message}\n${errorLog.join('\n')}`));
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const standardVariationAxes = new Set(['wght', 'wdth', 'ital', 'slnt', 'opsz']);
|
|
747
|
+
// Tracing the ranges of these standard axes require a bit more work, so just skip them for now:
|
|
748
|
+
const ignoredVariationAxes = new Set(['wdth', 'slnt', 'opsz']);
|
|
749
|
+
|
|
750
|
+
function renderNumberRange(min, max) {
|
|
751
|
+
if (min === max) {
|
|
752
|
+
return String(min);
|
|
753
|
+
} else {
|
|
754
|
+
return `${min}-${max}`;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
function warnAboutUnusedVariationAxes(
|
|
759
|
+
htmlOrSvgAssetTextsWithProps,
|
|
760
|
+
assetGraph
|
|
591
761
|
) {
|
|
592
|
-
|
|
593
|
-
|
|
762
|
+
const seenAxisValuesByFontUrlAndAxisName = new Map();
|
|
763
|
+
const outOfBoundsAxesByFontUrl = new Map();
|
|
764
|
+
|
|
765
|
+
for (const { fontUsages } of htmlOrSvgAssetTextsWithProps) {
|
|
766
|
+
for (const {
|
|
767
|
+
fontUrl,
|
|
768
|
+
fontVariationSettings,
|
|
769
|
+
fontStyles,
|
|
770
|
+
fontWeights,
|
|
771
|
+
hasOutOfBoundsAnimationTimingFunction,
|
|
772
|
+
props,
|
|
773
|
+
} of fontUsages) {
|
|
774
|
+
let seenAxes = seenAxisValuesByFontUrlAndAxisName.get(fontUrl);
|
|
775
|
+
if (!seenAxes) {
|
|
776
|
+
seenAxes = new Map();
|
|
777
|
+
seenAxisValuesByFontUrlAndAxisName.set(fontUrl, seenAxes);
|
|
778
|
+
}
|
|
779
|
+
const seenItalValues = [];
|
|
780
|
+
if (fontStyles.has('italic')) {
|
|
781
|
+
seenItalValues.push(1);
|
|
782
|
+
}
|
|
783
|
+
// If any font-style value except italic is seen (including normal or oblique)
|
|
784
|
+
// we're also utilizing value 0:
|
|
785
|
+
if (fontStyles.size > fontStyles.has('italic') ? 1 : 0) {
|
|
786
|
+
seenItalValues.push(0);
|
|
787
|
+
}
|
|
788
|
+
if (seenItalValues.length > 0) {
|
|
789
|
+
if (seenAxes.has('ital')) {
|
|
790
|
+
seenAxes.get('ital').push(...seenItalValues);
|
|
791
|
+
} else {
|
|
792
|
+
seenAxes.set('ital', seenItalValues);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
const minMaxFontWeight = parseFontWeightRange(props['font-weight']);
|
|
797
|
+
const seenFontWeightValues = [];
|
|
798
|
+
for (const fontWeight of fontWeights) {
|
|
799
|
+
seenFontWeightValues.push(_.clamp(fontWeight, ...minMaxFontWeight));
|
|
800
|
+
}
|
|
801
|
+
if (seenFontWeightValues.length > 0) {
|
|
802
|
+
if (seenAxes.has('wght')) {
|
|
803
|
+
seenAxes.get('wght').push(...seenFontWeightValues);
|
|
804
|
+
} else {
|
|
805
|
+
seenAxes.set('wght', seenFontWeightValues);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
for (const fontVariationSettingsValue of fontVariationSettings) {
|
|
810
|
+
for (const [axisName, axisValue] of parseFontVariationSettings(
|
|
811
|
+
fontVariationSettingsValue
|
|
812
|
+
)) {
|
|
813
|
+
const seenAxisValues = seenAxes.get(axisName);
|
|
814
|
+
if (seenAxisValues) {
|
|
815
|
+
seenAxisValues.push(axisValue);
|
|
816
|
+
} else {
|
|
817
|
+
seenAxes.set(axisName, [axisValue]);
|
|
818
|
+
}
|
|
819
|
+
if (hasOutOfBoundsAnimationTimingFunction) {
|
|
820
|
+
let outOfBoundsAxes = outOfBoundsAxesByFontUrl.get(fontUrl);
|
|
821
|
+
if (!outOfBoundsAxes) {
|
|
822
|
+
outOfBoundsAxes = new Set();
|
|
823
|
+
outOfBoundsAxesByFontUrl.set(fontUrl, outOfBoundsAxes);
|
|
824
|
+
}
|
|
825
|
+
outOfBoundsAxes.add(axisName);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
594
830
|
}
|
|
595
831
|
|
|
596
|
-
const
|
|
597
|
-
|
|
832
|
+
const warnings = [];
|
|
833
|
+
for (const [
|
|
834
|
+
fontUrl,
|
|
835
|
+
seenAxisValuesByAxisName,
|
|
836
|
+
] of seenAxisValuesByFontUrlAndAxisName.entries()) {
|
|
837
|
+
const outOfBoundsAxes = outOfBoundsAxesByFontUrl.get(fontUrl) || new Set();
|
|
838
|
+
let font;
|
|
839
|
+
try {
|
|
840
|
+
font = fontkit.create(assetGraph.findAssets({ url: fontUrl })[0].rawSrc);
|
|
841
|
+
} catch (err) {
|
|
842
|
+
// Don't break if we encounter an invalid font or one that's unsupported by fontkit
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
const unusedAxes = [];
|
|
846
|
+
const underutilizedAxes = [];
|
|
847
|
+
for (const [name, { min, max, default: defaultValue }] of Object.entries(
|
|
848
|
+
font.variationAxes
|
|
849
|
+
)) {
|
|
850
|
+
if (ignoredVariationAxes.has(name)) {
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
if (seenAxisValuesByAxisName.has(name) && !outOfBoundsAxes.has(name)) {
|
|
854
|
+
const usedValues = [...seenAxisValuesByAxisName.get(name)];
|
|
855
|
+
if (!standardVariationAxes.has(name)) {
|
|
856
|
+
usedValues.push(defaultValue);
|
|
857
|
+
}
|
|
858
|
+
const minUsed = Math.min(...usedValues);
|
|
859
|
+
const maxUsed = Math.max(...usedValues);
|
|
860
|
+
if (minUsed > min || maxUsed < max) {
|
|
861
|
+
underutilizedAxes.push({
|
|
862
|
+
name,
|
|
863
|
+
minUsed,
|
|
864
|
+
maxUsed,
|
|
865
|
+
min,
|
|
866
|
+
max,
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
} else {
|
|
870
|
+
unusedAxes.push(name);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
598
873
|
|
|
599
|
-
|
|
874
|
+
if (unusedAxes.length > 0 || underutilizedAxes.length > 0) {
|
|
875
|
+
let message = `${fontUrl}:\n`;
|
|
876
|
+
if (unusedAxes.length > 0) {
|
|
877
|
+
message += ` Unused axes: ${unusedAxes.join(', ')}\n`;
|
|
878
|
+
}
|
|
879
|
+
if (underutilizedAxes.length > 0) {
|
|
880
|
+
message += ` Underutilized axes:\n${underutilizedAxes
|
|
881
|
+
.map(
|
|
882
|
+
({ name, min, max, minUsed, maxUsed }) =>
|
|
883
|
+
` ${name}: ${renderNumberRange(
|
|
884
|
+
minUsed,
|
|
885
|
+
maxUsed
|
|
886
|
+
)} used (${min}-${max} available)`
|
|
887
|
+
)
|
|
888
|
+
.join('\n')}\n`;
|
|
889
|
+
}
|
|
890
|
+
warnings.push(message);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
600
893
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
from: {
|
|
612
|
-
url: googleFontsCssUrlRegex,
|
|
613
|
-
},
|
|
614
|
-
},
|
|
615
|
-
],
|
|
616
|
-
},
|
|
617
|
-
});
|
|
894
|
+
if (warnings.length > 0) {
|
|
895
|
+
assetGraph.info(
|
|
896
|
+
new Error(`🪓 Unused variation axes detected in your variable fonts.
|
|
897
|
+
The below variable fonts contain custom axes that do not appear to be fully used on any of your pages.
|
|
898
|
+
This bloats your fonts and also the subset fonts that subfont creates.
|
|
899
|
+
Consider removing the unused axis ranges using a tool like Slice <https://slice-gui.netlify.app/>
|
|
900
|
+
${warnings.join('\n')}`)
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
618
904
|
|
|
619
|
-
|
|
905
|
+
async function collectTextsByPage(
|
|
906
|
+
assetGraph,
|
|
907
|
+
htmlOrSvgAssets,
|
|
908
|
+
{ text, console, dynamic = false } = {}
|
|
909
|
+
) {
|
|
910
|
+
const htmlOrSvgAssetTextsWithProps = [];
|
|
620
911
|
|
|
621
912
|
const memoizedGetCssRulesByProperty = memoizeSync(getCssRulesByProperty);
|
|
622
|
-
const htmlOrSvgAssets = assetGraph.findAssets({
|
|
623
|
-
$or: [
|
|
624
|
-
{
|
|
625
|
-
type: 'Html',
|
|
626
|
-
isInline: false,
|
|
627
|
-
},
|
|
628
|
-
{
|
|
629
|
-
type: 'Svg',
|
|
630
|
-
},
|
|
631
|
-
],
|
|
632
|
-
});
|
|
633
913
|
const traversalRelationQuery = {
|
|
634
914
|
$or: [
|
|
635
915
|
{
|
|
@@ -644,12 +924,7 @@ async function subsetFonts(
|
|
|
644
924
|
],
|
|
645
925
|
};
|
|
646
926
|
|
|
647
|
-
// Keep track of the injected CSS assets that should eventually be minified
|
|
648
|
-
// Minifying them along the way currently doesn't work because some of the
|
|
649
|
-
// manipulation is sensitive to the exact text contents. We should fix that.
|
|
650
|
-
const subsetFontsToBeMinified = new Set();
|
|
651
927
|
const fontFaceDeclarationsByHtmlOrSvgAsset = new Map();
|
|
652
|
-
const potentiallyOrphanedAssets = new Set();
|
|
653
928
|
|
|
654
929
|
const headlessBrowser = dynamic && new HeadlessBrowser({ console });
|
|
655
930
|
const globalTextByProps = [];
|
|
@@ -685,9 +960,8 @@ async function subsetFonts(
|
|
|
685
960
|
node.walkDecls((declaration) => {
|
|
686
961
|
const propName = declaration.prop.toLowerCase();
|
|
687
962
|
if (propName === 'font-family') {
|
|
688
|
-
fontFaceDeclaration[propName] =
|
|
689
|
-
declaration.value
|
|
690
|
-
)[0];
|
|
963
|
+
fontFaceDeclaration[propName] =
|
|
964
|
+
cssFontParser.parseFontFamily(declaration.value)[0];
|
|
691
965
|
} else {
|
|
692
966
|
fontFaceDeclaration[propName] = declaration.value;
|
|
693
967
|
}
|
|
@@ -774,7 +1048,72 @@ async function subsetFonts(
|
|
|
774
1048
|
}
|
|
775
1049
|
}
|
|
776
1050
|
}
|
|
1051
|
+
return { htmlOrSvgAssetTextsWithProps, fontFaceDeclarationsByHtmlOrSvgAsset };
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
async function subsetFonts(
|
|
1055
|
+
assetGraph,
|
|
1056
|
+
{
|
|
1057
|
+
formats = ['woff2', 'woff'],
|
|
1058
|
+
subsetPath = 'subfont/',
|
|
1059
|
+
omitFallbacks = false,
|
|
1060
|
+
inlineCss,
|
|
1061
|
+
fontDisplay,
|
|
1062
|
+
hrefType = 'rootRelative',
|
|
1063
|
+
onlyInfo,
|
|
1064
|
+
dynamic,
|
|
1065
|
+
console = global.console,
|
|
1066
|
+
text,
|
|
1067
|
+
} = {}
|
|
1068
|
+
) {
|
|
1069
|
+
if (!validFontDisplayValues.includes(fontDisplay)) {
|
|
1070
|
+
fontDisplay = undefined;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
const subsetUrl = urltools.ensureTrailingSlash(assetGraph.root + subsetPath);
|
|
1074
|
+
|
|
1075
|
+
await assetGraph.applySourceMaps({ type: 'Css' });
|
|
777
1076
|
|
|
1077
|
+
await assetGraph.populate({
|
|
1078
|
+
followRelations: {
|
|
1079
|
+
$or: [
|
|
1080
|
+
{
|
|
1081
|
+
to: {
|
|
1082
|
+
url: { $regex: googleFontsCssUrlRegex },
|
|
1083
|
+
},
|
|
1084
|
+
},
|
|
1085
|
+
{
|
|
1086
|
+
type: 'CssFontFaceSrc',
|
|
1087
|
+
from: {
|
|
1088
|
+
url: { $regex: googleFontsCssUrlRegex },
|
|
1089
|
+
},
|
|
1090
|
+
},
|
|
1091
|
+
],
|
|
1092
|
+
},
|
|
1093
|
+
});
|
|
1094
|
+
|
|
1095
|
+
const htmlOrSvgAssets = assetGraph.findAssets({
|
|
1096
|
+
$or: [
|
|
1097
|
+
{
|
|
1098
|
+
type: 'Html',
|
|
1099
|
+
isInline: false,
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
type: 'Svg',
|
|
1103
|
+
},
|
|
1104
|
+
],
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
// Collect texts by page
|
|
1108
|
+
|
|
1109
|
+
const { htmlOrSvgAssetTextsWithProps, fontFaceDeclarationsByHtmlOrSvgAsset } =
|
|
1110
|
+
await collectTextsByPage(assetGraph, htmlOrSvgAssets, {
|
|
1111
|
+
text,
|
|
1112
|
+
console,
|
|
1113
|
+
dynamic,
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
const potentiallyOrphanedAssets = new Set();
|
|
778
1117
|
if (omitFallbacks) {
|
|
779
1118
|
for (const htmlOrSvgAsset of htmlOrSvgAssets) {
|
|
780
1119
|
const accumulatedFontFaceDeclarations =
|
|
@@ -848,94 +1187,8 @@ async function subsetFonts(
|
|
|
848
1187
|
formats
|
|
849
1188
|
);
|
|
850
1189
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
for (const {
|
|
855
|
-
htmlOrSvgAsset,
|
|
856
|
-
fontUsages,
|
|
857
|
-
accumulatedFontFaceDeclarations,
|
|
858
|
-
} of htmlOrSvgAssetTextsWithProps) {
|
|
859
|
-
for (const fontUsage of fontUsages) {
|
|
860
|
-
if (fontUsage.subsets) {
|
|
861
|
-
const characterSet = fontkit.create(
|
|
862
|
-
Object.values(fontUsage.subsets)[0]
|
|
863
|
-
).characterSet;
|
|
864
|
-
|
|
865
|
-
let missedAny = false;
|
|
866
|
-
for (const char of [...fontUsage.pageText]) {
|
|
867
|
-
// Turns out that browsers don't mind that these are missing:
|
|
868
|
-
if (char === '\t' || char === '\n') {
|
|
869
|
-
continue;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
const codePoint = char.codePointAt(0);
|
|
873
|
-
|
|
874
|
-
const isMissing = !characterSet.includes(codePoint);
|
|
875
|
-
|
|
876
|
-
if (isMissing) {
|
|
877
|
-
let location;
|
|
878
|
-
const charIdx = htmlOrSvgAsset.text.indexOf(char);
|
|
879
|
-
|
|
880
|
-
if (charIdx === -1) {
|
|
881
|
-
location = `${htmlOrSvgAsset.urlOrDescription} (generated content)`;
|
|
882
|
-
} else {
|
|
883
|
-
const position = new LinesAndColumns(
|
|
884
|
-
htmlOrSvgAsset.text
|
|
885
|
-
).locationForIndex(charIdx);
|
|
886
|
-
location = `${htmlOrSvgAsset.urlOrDescription}:${
|
|
887
|
-
position.line + 1
|
|
888
|
-
}:${position.column + 1}`;
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
missingGlyphsErrors.push({
|
|
892
|
-
codePoint,
|
|
893
|
-
char,
|
|
894
|
-
htmlOrSvgAsset,
|
|
895
|
-
fontUsage,
|
|
896
|
-
location,
|
|
897
|
-
});
|
|
898
|
-
missedAny = true;
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
if (missedAny) {
|
|
902
|
-
const fontFaces = accumulatedFontFaceDeclarations.filter((fontFace) =>
|
|
903
|
-
fontUsage.fontFamilies.has(fontFace['font-family'])
|
|
904
|
-
);
|
|
905
|
-
for (const fontFace of fontFaces) {
|
|
906
|
-
const cssFontFaceSrc = fontFace.relations[0];
|
|
907
|
-
const fontFaceDeclaration = cssFontFaceSrc.node;
|
|
908
|
-
if (
|
|
909
|
-
!fontFaceDeclaration.some((node) => node.prop === 'unicode-range')
|
|
910
|
-
) {
|
|
911
|
-
fontFaceDeclaration.append({
|
|
912
|
-
prop: 'unicode-range',
|
|
913
|
-
value: unicodeRange(fontUsage.codepoints.original),
|
|
914
|
-
});
|
|
915
|
-
cssFontFaceSrc.from.markDirty();
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
if (missingGlyphsErrors.length) {
|
|
924
|
-
const errorLog = missingGlyphsErrors.map(
|
|
925
|
-
({ char, fontUsage, location }) =>
|
|
926
|
-
`- \\u{${char.codePointAt(0).toString(16)}} (${char}) in font-family '${
|
|
927
|
-
fontUsage.props['font-family']
|
|
928
|
-
}' (${fontUsage.props['font-weight']}/${
|
|
929
|
-
fontUsage.props['font-style']
|
|
930
|
-
}) at ${location}`
|
|
931
|
-
);
|
|
932
|
-
|
|
933
|
-
const message = `Missing glyph fallback detected.
|
|
934
|
-
When your primary webfont doesn't contain the glyphs you use, browsers that don't support unicode-range will load your fallback fonts, which will be a potential waste of bandwidth.
|
|
935
|
-
These glyphs are used on your site, but they don't exist in the font you applied to them:`;
|
|
936
|
-
|
|
937
|
-
assetGraph.info(new Error(`${message}\n${errorLog.join('\n')}`));
|
|
938
|
-
}
|
|
1190
|
+
warnAboutMissingGlyphs(htmlOrSvgAssetTextsWithProps, assetGraph);
|
|
1191
|
+
warnAboutUnusedVariationAxes(htmlOrSvgAssetTextsWithProps, assetGraph);
|
|
939
1192
|
|
|
940
1193
|
// Insert subsets:
|
|
941
1194
|
|
|
@@ -1021,7 +1274,7 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1021
1274
|
text: subsetCssText,
|
|
1022
1275
|
});
|
|
1023
1276
|
|
|
1024
|
-
|
|
1277
|
+
await cssAsset.minify();
|
|
1025
1278
|
|
|
1026
1279
|
for (const [i, fontRelation] of cssAsset.outgoingRelations.entries()) {
|
|
1027
1280
|
const fontAsset = fontRelation.to;
|
|
@@ -1091,7 +1344,6 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1091
1344
|
const existingCssAsset = assetGraph.findAssets({ url: cssAssetUrl })[0];
|
|
1092
1345
|
if (existingCssAsset) {
|
|
1093
1346
|
assetGraph.removeAsset(cssAsset);
|
|
1094
|
-
subsetFontsToBeMinified.delete(cssAsset);
|
|
1095
1347
|
cssAsset = existingCssAsset;
|
|
1096
1348
|
} else {
|
|
1097
1349
|
cssAsset.url = cssAssetUrl;
|
|
@@ -1105,7 +1357,7 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1105
1357
|
|
|
1106
1358
|
if (
|
|
1107
1359
|
fontAsset.contentType === 'font/woff2' &&
|
|
1108
|
-
fontRelation.to.
|
|
1360
|
+
fontRelation.to.url.startsWith(subsetUrl)
|
|
1109
1361
|
) {
|
|
1110
1362
|
const fontFaceDeclaration = fontRelation.node;
|
|
1111
1363
|
const originalFontFamily = unquote(
|
|
@@ -1226,7 +1478,7 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1226
1478
|
assetGraph.removeAsset(cssAsset);
|
|
1227
1479
|
cssAsset = existingCssAsset;
|
|
1228
1480
|
} else {
|
|
1229
|
-
|
|
1481
|
+
await cssAsset.minify();
|
|
1230
1482
|
cssAsset.url = cssAssetUrl;
|
|
1231
1483
|
}
|
|
1232
1484
|
|
|
@@ -1309,9 +1561,10 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1309
1561
|
assetGraph,
|
|
1310
1562
|
googleFontStylesheetRelation.to,
|
|
1311
1563
|
formats,
|
|
1312
|
-
hrefType
|
|
1564
|
+
hrefType,
|
|
1565
|
+
subsetUrl
|
|
1313
1566
|
);
|
|
1314
|
-
|
|
1567
|
+
await selfHostedGoogleFontsCssAsset.minify();
|
|
1315
1568
|
selfHostedGoogleCssByUrl.set(
|
|
1316
1569
|
googleFontStylesheetRelation.to.url,
|
|
1317
1570
|
selfHostedGoogleFontsCssAsset
|
|
@@ -1374,7 +1627,9 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1374
1627
|
);
|
|
1375
1628
|
for (let i = 0; i < fontFamilies.length; i += 1) {
|
|
1376
1629
|
const subsetFontFamily =
|
|
1377
|
-
webfontNameMap[
|
|
1630
|
+
webfontNameMap[
|
|
1631
|
+
cssFontParser.parseFontFamily(fontFamilies[i])[0].toLowerCase()
|
|
1632
|
+
];
|
|
1378
1633
|
if (subsetFontFamily && !fontFamilies.includes(subsetFontFamily)) {
|
|
1379
1634
|
fontFamilies.splice(
|
|
1380
1635
|
i,
|
|
@@ -1434,7 +1689,7 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1434
1689
|
for (let i = 0; i < fontFamilies.length; i += 1) {
|
|
1435
1690
|
const subsetFontFamily =
|
|
1436
1691
|
webfontNameMap[
|
|
1437
|
-
|
|
1692
|
+
cssFontParser.parseFontFamily(fontFamilies[i])[0].toLowerCase()
|
|
1438
1693
|
];
|
|
1439
1694
|
if (subsetFontFamily && !fontFamilies.includes(subsetFontFamily)) {
|
|
1440
1695
|
fontFamilies.splice(
|
|
@@ -1448,7 +1703,7 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1448
1703
|
}
|
|
1449
1704
|
}
|
|
1450
1705
|
} else if (propName === 'font') {
|
|
1451
|
-
const fontProperties = cssFontParser(cssRule.value);
|
|
1706
|
+
const fontProperties = cssFontParser.parseFont(cssRule.value);
|
|
1452
1707
|
const fontFamilies =
|
|
1453
1708
|
fontProperties && fontProperties['font-family'].map(unquote);
|
|
1454
1709
|
if (fontFamilies) {
|
|
@@ -1492,11 +1747,6 @@ These glyphs are used on your site, but they don't exist in the font you applied
|
|
|
1492
1747
|
}
|
|
1493
1748
|
}
|
|
1494
1749
|
|
|
1495
|
-
// This is a bit awkward now, but if it's done sooner, it breaks the CSS source regexping:
|
|
1496
|
-
for (const cssAsset of subsetFontsToBeMinified) {
|
|
1497
|
-
await cssAsset.minify();
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
1750
|
await assetGraph.serializeSourceMaps(undefined, {
|
|
1501
1751
|
type: 'Css',
|
|
1502
1752
|
outgoingRelations: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "subfont",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.11.0",
|
|
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,14 +47,14 @@
|
|
|
47
47
|
"homepage": "https://github.com/Munter/subfont#readme",
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@gustavnikolaj/async-main-wrap": "^3.0.1",
|
|
50
|
+
"@hookun/parse-animation-shorthand": "^0.1.4",
|
|
50
51
|
"assetgraph": "^7.8.1",
|
|
51
52
|
"browserslist": "^4.13.0",
|
|
52
|
-
"css-font-parser": "^0.
|
|
53
|
+
"css-font-parser": "^2.0.0",
|
|
53
54
|
"css-font-weight-names": "^0.2.1",
|
|
54
55
|
"css-list-helpers": "^2.0.0",
|
|
55
|
-
"font-family-papandreou": "^0.2.0-patch2",
|
|
56
56
|
"font-snapper": "^1.2.0",
|
|
57
|
-
"font-tracer": "^3.
|
|
57
|
+
"font-tracer": "^3.6.0",
|
|
58
58
|
"fontkit": "^1.8.0",
|
|
59
59
|
"fontverter": "^2.0.0",
|
|
60
60
|
"gettemporaryfilepath": "^1.0.1",
|