subfont 6.6.0 → 6.9.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 CHANGED
@@ -1,4 +1,30 @@
1
- ### v6.5.0 (2022-07-09)
1
+ ### v6.9.0 (2022-08-07)
2
+
3
+ - [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))
4
+ - [Move some code into a collectTextsByPage function](https://github.com/Munter/subfont/commit/1dff3819fb80793f23a3cc37f9ff58bafffa4efc) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
5
+ - [Move the missing glyph detection to a function](https://github.com/Munter/subfont/commit/38bf36eba4370ecdbc777cb2457696b9bc7c7d1c) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
6
+ - [Fix typo causing regexp to not be matched correctly](https://github.com/Munter/subfont/commit/05137708b5c48af305f3119deb836b1cd9fed683) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
7
+ - [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))
8
+
9
+ ### v6.8.0 (2022-07-28)
10
+
11
+ - [Update assetgraph to ^7.8.1](https://github.com/Munter/subfont/commit/888a97912f98bd937a53b7bec0f39d50ddc96023) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
12
+ - [Don't leave stylesheets behind that only contain comments after removing @font-face rules from them https:\/\/github.com\/Munter\/subfont\/issues\/160\#issuecomment-1194672752](https://github.com/Munter/subfont/commit/3dfd9dfbbe5071b94eb52ac216796bc7c0554078) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
13
+
14
+ ### v6.7.0 (2022-07-24)
15
+
16
+ - [Update subset-font to ^1.5.0](https://github.com/Munter/subfont/commit/ba79ef6db81cedfd0e52672b530ca3afddd68a94) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
17
+ - [Fix warning about missing glyphs for BMP chars](https://github.com/Munter/subfont/commit/5ba0168f35bfef33f9bb9cc8a6d64de8fa1d5585) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
18
+ - [Fix CHANGELOG generation in preversion script](https://github.com/Munter/subfont/commit/3ea696f8c336c6072411c9817c0c2f5b8d8d61b9) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
19
+
20
+ ### v6.6.1 (2022-07-09)
21
+
22
+ - [ugrvw](https://github.com/Munter/subfont/commit/5ccb873025f9790faf65641551df1a7e65fbfdbf) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
23
+ - [Fix CHANGELOG https:\/\/github.com\/Munter\/subfont\/commit\/5365689a5a925304a158fddef2b6af702857371c\#r78084394](https://github.com/Munter/subfont/commit/d42c7a2cffd33662d42d4532c45cb8b90080f128) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
24
+ - [Expect browserslist to only prescribe woff I guess the browser features + usage statistics finally changed so woff isn't needed](https://github.com/Munter/subfont/commit/b70db9b769955cabbda2f2a76f2e7758f9968ec7) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
25
+ - [Tests: Tolerate u+ in unicode-range \(I guess an in-range dep changed\)](https://github.com/Munter/subfont/commit/92a7871457246b64a997c0bffd9bb2766655d811) ([Andreas Lind](mailto:andreaslindpetersen@gmail.com))
26
+
27
+ ### v6.6.0 (2022-07-09)
2
28
 
3
29
  #### Pull requests
4
30
 
@@ -568,62 +568,110 @@ function getCodepoints(text) {
568
568
  return codepoints;
569
569
  }
570
570
 
571
- async function subsetFonts(
572
- assetGraph,
573
- {
574
- formats = ['woff2', 'woff'],
575
- subsetPath = 'subfont/',
576
- omitFallbacks = false,
577
- inlineCss,
578
- fontDisplay,
579
- hrefType = 'rootRelative',
580
- onlyInfo,
581
- dynamic,
582
- console = global.console,
583
- text,
584
- } = {}
585
- ) {
586
- if (!validFontDisplayValues.includes(fontDisplay)) {
587
- fontDisplay = undefined;
571
+ function cssAssetIsEmpty(cssAsset) {
572
+ return cssAsset.parseTree.nodes.every(
573
+ (node) => node.type === 'comment' && !node.text.startsWith('!')
574
+ );
575
+ }
576
+
577
+ function warnAboutMissingGlyphs(htmlOrSvgAssetTextsWithProps, assetGraph) {
578
+ const missingGlyphsErrors = [];
579
+
580
+ for (const {
581
+ htmlOrSvgAsset,
582
+ fontUsages,
583
+ accumulatedFontFaceDeclarations,
584
+ } of htmlOrSvgAssetTextsWithProps) {
585
+ for (const fontUsage of fontUsages) {
586
+ if (fontUsage.subsets) {
587
+ const characterSet = fontkit.create(
588
+ Object.values(fontUsage.subsets)[0]
589
+ ).characterSet;
590
+
591
+ let missedAny = false;
592
+ for (const char of [...fontUsage.pageText]) {
593
+ // Turns out that browsers don't mind that these are missing:
594
+ if (char === '\t' || char === '\n') {
595
+ continue;
596
+ }
597
+
598
+ const codePoint = char.codePointAt(0);
599
+
600
+ const isMissing = !characterSet.includes(codePoint);
601
+
602
+ if (isMissing) {
603
+ let location;
604
+ const charIdx = htmlOrSvgAsset.text.indexOf(char);
605
+
606
+ if (charIdx === -1) {
607
+ location = `${htmlOrSvgAsset.urlOrDescription} (generated content)`;
608
+ } else {
609
+ const position = new LinesAndColumns(
610
+ htmlOrSvgAsset.text
611
+ ).locationForIndex(charIdx);
612
+ location = `${htmlOrSvgAsset.urlOrDescription}:${
613
+ position.line + 1
614
+ }:${position.column + 1}`;
615
+ }
616
+
617
+ missingGlyphsErrors.push({
618
+ codePoint,
619
+ char,
620
+ htmlOrSvgAsset,
621
+ fontUsage,
622
+ location,
623
+ });
624
+ missedAny = true;
625
+ }
626
+ }
627
+ if (missedAny) {
628
+ const fontFaces = accumulatedFontFaceDeclarations.filter((fontFace) =>
629
+ fontUsage.fontFamilies.has(fontFace['font-family'])
630
+ );
631
+ for (const fontFace of fontFaces) {
632
+ const cssFontFaceSrc = fontFace.relations[0];
633
+ const fontFaceDeclaration = cssFontFaceSrc.node;
634
+ if (
635
+ !fontFaceDeclaration.some((node) => node.prop === 'unicode-range')
636
+ ) {
637
+ fontFaceDeclaration.append({
638
+ prop: 'unicode-range',
639
+ value: unicodeRange(fontUsage.codepoints.original),
640
+ });
641
+ cssFontFaceSrc.from.markDirty();
642
+ }
643
+ }
644
+ }
645
+ }
646
+ }
588
647
  }
589
648
 
590
- const htmlOrSvgAssetTextsWithProps = [];
591
- const subsetUrl = urltools.ensureTrailingSlash(assetGraph.root + subsetPath);
649
+ if (missingGlyphsErrors.length) {
650
+ const errorLog = missingGlyphsErrors.map(
651
+ ({ char, fontUsage, location }) =>
652
+ `- \\u{${char.codePointAt(0).toString(16)}} (${char}) in font-family '${
653
+ fontUsage.props['font-family']
654
+ }' (${fontUsage.props['font-weight']}/${
655
+ fontUsage.props['font-style']
656
+ }) at ${location}`
657
+ );
592
658
 
593
- await assetGraph.applySourceMaps({ type: 'Css' });
659
+ const message = `Missing glyph fallback detected.
660
+ 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.
661
+ These glyphs are used on your site, but they don't exist in the font you applied to them:`;
594
662
 
595
- await assetGraph.populate({
596
- followRelations: {
597
- $or: [
598
- {
599
- to: {
600
- url: googleFontsCssUrlRegex,
601
- },
602
- },
603
- {
604
- type: 'CssFontFaceSrc',
605
- from: {
606
- url: googleFontsCssUrlRegex,
607
- },
608
- },
609
- ],
610
- },
611
- });
663
+ assetGraph.info(new Error(`${message}\n${errorLog.join('\n')}`));
664
+ }
665
+ }
612
666
 
613
- // Collect texts by page
667
+ async function collectTextsByPage(
668
+ assetGraph,
669
+ htmlOrSvgAssets,
670
+ { text, console, dynamic = false } = {}
671
+ ) {
672
+ const htmlOrSvgAssetTextsWithProps = [];
614
673
 
615
674
  const memoizedGetCssRulesByProperty = memoizeSync(getCssRulesByProperty);
616
- const htmlOrSvgAssets = assetGraph.findAssets({
617
- $or: [
618
- {
619
- type: 'Html',
620
- isInline: false,
621
- },
622
- {
623
- type: 'Svg',
624
- },
625
- ],
626
- });
627
675
  const traversalRelationQuery = {
628
676
  $or: [
629
677
  {
@@ -638,12 +686,7 @@ async function subsetFonts(
638
686
  ],
639
687
  };
640
688
 
641
- // Keep track of the injected CSS assets that should eventually be minified
642
- // Minifying them along the way currently doesn't work because some of the
643
- // manipulation is sensitive to the exact text contents. We should fix that.
644
- const subsetFontsToBeMinified = new Set();
645
689
  const fontFaceDeclarationsByHtmlOrSvgAsset = new Map();
646
- const potentiallyOrphanedAssets = new Set();
647
690
 
648
691
  const headlessBrowser = dynamic && new HeadlessBrowser({ console });
649
692
  const globalTextByProps = [];
@@ -768,7 +811,72 @@ async function subsetFonts(
768
811
  }
769
812
  }
770
813
  }
814
+ return { htmlOrSvgAssetTextsWithProps, fontFaceDeclarationsByHtmlOrSvgAsset };
815
+ }
771
816
 
817
+ async function subsetFonts(
818
+ assetGraph,
819
+ {
820
+ formats = ['woff2', 'woff'],
821
+ subsetPath = 'subfont/',
822
+ omitFallbacks = false,
823
+ inlineCss,
824
+ fontDisplay,
825
+ hrefType = 'rootRelative',
826
+ onlyInfo,
827
+ dynamic,
828
+ console = global.console,
829
+ text,
830
+ } = {}
831
+ ) {
832
+ if (!validFontDisplayValues.includes(fontDisplay)) {
833
+ fontDisplay = undefined;
834
+ }
835
+
836
+ const subsetUrl = urltools.ensureTrailingSlash(assetGraph.root + subsetPath);
837
+
838
+ await assetGraph.applySourceMaps({ type: 'Css' });
839
+
840
+ await assetGraph.populate({
841
+ followRelations: {
842
+ $or: [
843
+ {
844
+ to: {
845
+ url: { $regex: googleFontsCssUrlRegex },
846
+ },
847
+ },
848
+ {
849
+ type: 'CssFontFaceSrc',
850
+ from: {
851
+ url: { $regex: googleFontsCssUrlRegex },
852
+ },
853
+ },
854
+ ],
855
+ },
856
+ });
857
+
858
+ const htmlOrSvgAssets = assetGraph.findAssets({
859
+ $or: [
860
+ {
861
+ type: 'Html',
862
+ isInline: false,
863
+ },
864
+ {
865
+ type: 'Svg',
866
+ },
867
+ ],
868
+ });
869
+
870
+ // Collect texts by page
871
+
872
+ const { htmlOrSvgAssetTextsWithProps, fontFaceDeclarationsByHtmlOrSvgAsset } =
873
+ await collectTextsByPage(assetGraph, htmlOrSvgAssets, {
874
+ text,
875
+ console,
876
+ dynamic,
877
+ });
878
+
879
+ const potentiallyOrphanedAssets = new Set();
772
880
  if (omitFallbacks) {
773
881
  for (const htmlOrSvgAsset of htmlOrSvgAssets) {
774
882
  const accumulatedFontFaceDeclarations =
@@ -842,94 +950,7 @@ async function subsetFonts(
842
950
  formats
843
951
  );
844
952
 
845
- // Warn about missing glyphs
846
- const missingGlyphsErrors = [];
847
-
848
- for (const {
849
- htmlOrSvgAsset,
850
- fontUsages,
851
- accumulatedFontFaceDeclarations,
852
- } of htmlOrSvgAssetTextsWithProps) {
853
- for (const fontUsage of fontUsages) {
854
- if (fontUsage.subsets) {
855
- const characterSet = fontkit.create(
856
- Object.values(fontUsage.subsets)[0]
857
- ).characterSet;
858
-
859
- let missedAny = false;
860
- for (const char of [...fontUsage.pageText]) {
861
- // Turns out that browsers don't mind that these are missing:
862
- if (char === '\t' || char === '\n') {
863
- continue;
864
- }
865
-
866
- const codePoint = char.codePointAt(0);
867
-
868
- const isMissing = !characterSet.includes(codePoint);
869
-
870
- if (isMissing) {
871
- let location;
872
- const charIdx = htmlOrSvgAsset.text.indexOf(char);
873
-
874
- if (charIdx === -1) {
875
- location = `${htmlOrSvgAsset.urlOrDescription} (generated content)`;
876
- } else {
877
- const position = new LinesAndColumns(
878
- htmlOrSvgAsset.text
879
- ).locationForIndex(charIdx);
880
- location = `${htmlOrSvgAsset.urlOrDescription}:${
881
- position.line + 1
882
- }:${position.column + 1}`;
883
- }
884
-
885
- missingGlyphsErrors.push({
886
- codePoint,
887
- char,
888
- htmlOrSvgAsset,
889
- fontUsage,
890
- location,
891
- });
892
- missedAny = true;
893
- }
894
- }
895
- if (missedAny) {
896
- const fontFaces = accumulatedFontFaceDeclarations.filter((fontFace) =>
897
- fontUsage.fontFamilies.has(fontFace['font-family'])
898
- );
899
- for (const fontFace of fontFaces) {
900
- const cssFontFaceSrc = fontFace.relations[0];
901
- const fontFaceDeclaration = cssFontFaceSrc.node;
902
- if (
903
- !fontFaceDeclaration.some((node) => node.prop === 'unicode-range')
904
- ) {
905
- fontFaceDeclaration.append({
906
- prop: 'unicode-range',
907
- value: unicodeRange(fontUsage.codepoints.original),
908
- });
909
- cssFontFaceSrc.from.markDirty();
910
- }
911
- }
912
- }
913
- }
914
- }
915
- }
916
-
917
- if (missingGlyphsErrors.length) {
918
- const errorLog = missingGlyphsErrors.map(
919
- ({ char, fontUsage, location }) =>
920
- `- \\u{${char.charCodeAt(0).toString(16)}} (${char}) in font-family '${
921
- fontUsage.props['font-family']
922
- }' (${fontUsage.props['font-weight']}/${
923
- fontUsage.props['font-style']
924
- }) at ${location}`
925
- );
926
-
927
- const message = `Missing glyph fallback detected.
928
- 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.
929
- These glyphs are used on your site, but they don't exist in the font you applied to them:`;
930
-
931
- assetGraph.info(new Error(`${message}\n${errorLog.join('\n')}`));
932
- }
953
+ warnAboutMissingGlyphs(htmlOrSvgAssetTextsWithProps, assetGraph);
933
954
 
934
955
  // Insert subsets:
935
956
 
@@ -1015,7 +1036,7 @@ These glyphs are used on your site, but they don't exist in the font you applied
1015
1036
  text: subsetCssText,
1016
1037
  });
1017
1038
 
1018
- subsetFontsToBeMinified.add(cssAsset);
1039
+ await cssAsset.minify();
1019
1040
 
1020
1041
  for (const [i, fontRelation] of cssAsset.outgoingRelations.entries()) {
1021
1042
  const fontAsset = fontRelation.to;
@@ -1085,7 +1106,6 @@ These glyphs are used on your site, but they don't exist in the font you applied
1085
1106
  const existingCssAsset = assetGraph.findAssets({ url: cssAssetUrl })[0];
1086
1107
  if (existingCssAsset) {
1087
1108
  assetGraph.removeAsset(cssAsset);
1088
- subsetFontsToBeMinified.delete(cssAsset);
1089
1109
  cssAsset = existingCssAsset;
1090
1110
  } else {
1091
1111
  cssAsset.url = cssAssetUrl;
@@ -1220,7 +1240,7 @@ These glyphs are used on your site, but they don't exist in the font you applied
1220
1240
  assetGraph.removeAsset(cssAsset);
1221
1241
  cssAsset = existingCssAsset;
1222
1242
  } else {
1223
- subsetFontsToBeMinified.add(cssAsset);
1243
+ await cssAsset.minify();
1224
1244
  cssAsset.url = cssAssetUrl;
1225
1245
  }
1226
1246
 
@@ -1252,8 +1272,9 @@ These glyphs are used on your site, but they don't exist in the font you applied
1252
1272
  cssAsset.markDirty();
1253
1273
  maybeEmptyCssAssets.add(cssAsset);
1254
1274
  }
1275
+
1255
1276
  for (const cssAsset of maybeEmptyCssAssets) {
1256
- if (cssAsset.isEmpty) {
1277
+ if (cssAssetIsEmpty(cssAsset)) {
1257
1278
  for (const incomingRelation of cssAsset.incomingRelations) {
1258
1279
  incomingRelation.detach();
1259
1280
  }
@@ -1304,7 +1325,7 @@ These glyphs are used on your site, but they don't exist in the font you applied
1304
1325
  formats,
1305
1326
  hrefType
1306
1327
  );
1307
- subsetFontsToBeMinified.add(selfHostedGoogleFontsCssAsset);
1328
+ await selfHostedGoogleFontsCssAsset.minify();
1308
1329
  selfHostedGoogleCssByUrl.set(
1309
1330
  googleFontStylesheetRelation.to.url,
1310
1331
  selfHostedGoogleFontsCssAsset
@@ -1485,11 +1506,6 @@ These glyphs are used on your site, but they don't exist in the font you applied
1485
1506
  }
1486
1507
  }
1487
1508
 
1488
- // This is a bit awkward now, but if it's done sooner, it breaks the CSS source regexping:
1489
- for (const cssAsset of subsetFontsToBeMinified) {
1490
- await cssAsset.minify();
1491
- }
1492
-
1493
1509
  await assetGraph.serializeSourceMaps(undefined, {
1494
1510
  type: 'Css',
1495
1511
  outgoingRelations: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "subfont",
3
- "version": "6.6.0",
3
+ "version": "6.9.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"
@@ -10,7 +10,7 @@
10
10
  "test": "mocha && npm run lint",
11
11
  "travis": "npm run coverage && npm run lint",
12
12
  "coverage": "nyc --reporter=lcov --reporter=text -- mocha",
13
- "preversion": "offline-github-changelog --next=${npm_package_version} > CHANGELOG.md && git add CHANGELOG.md"
13
+ "preversion": "offline-github-changelog --next=${npm_new_version} > CHANGELOG.md && git add CHANGELOG.md"
14
14
  },
15
15
  "bin": {
16
16
  "subfont": "lib/cli.js"
@@ -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
- "assetgraph": "^7.2.0",
50
+ "assetgraph": "^7.8.1",
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": "^3.2.0",
57
+ "font-tracer": "^3.3.0",
58
58
  "fontkit": "^1.8.0",
59
59
  "fontverter": "^2.0.0",
60
60
  "gettemporaryfilepath": "^1.0.1",
@@ -66,7 +66,7 @@
66
66
  "pretty-bytes": "^5.1.0",
67
67
  "puppeteer-core": "^8.0.0",
68
68
  "specificity": "^0.4.1",
69
- "subset-font": "^1.4.0",
69
+ "subset-font": "^1.5.0",
70
70
  "urltools": "^0.4.1",
71
71
  "yargs": "^15.4.0"
72
72
  },