@turntrout/subfont 1.5.1 → 1.7.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/lib/unquote.js CHANGED
@@ -1,10 +1,15 @@
1
1
  function unescapeCssString(str) {
2
2
  return str.replace(
3
3
  /\\([0-9a-f]{1,6})(\s?)/gi,
4
- ($0, hexChars, followingWhitespace) =>
5
- `${String.fromCodePoint(parseInt(hexChars, 16))}${
6
- hexChars.length === 6 ? followingWhitespace : ''
7
- }`
4
+ ($0, hexChars, followingWhitespace) => {
5
+ try {
6
+ return `${String.fromCodePoint(parseInt(hexChars, 16))}${
7
+ hexChars.length === 6 ? followingWhitespace : ''
8
+ }`;
9
+ } catch {
10
+ return $0;
11
+ }
12
+ }
8
13
  );
9
14
  }
10
15
 
@@ -8,26 +8,11 @@ const standardVariationAxes = new Set(['wght', 'wdth', 'ital', 'slnt', 'opsz']);
8
8
  // CSS maps oblique to slnt -14.
9
9
  const DEFAULT_OBLIQUE_SLNT = -14;
10
10
 
11
- // When no opsz values are determined from font-size or font-variation-settings,
12
- // the axis is pinned to its default value rather than preserving the full range,
13
- // which can significantly bloat variable font subsets.
11
+ // When no opsz values are determined from font-variation-settings, the axis is
12
+ // pinned to its default value rather than preserving the full range, which can
13
+ // significantly bloat variable font subsets.
14
14
  const ignoredVariationAxes = new Set();
15
15
 
16
- // Parse a CSS font-size value to a numeric px value.
17
- // Returns the number if the value is in absolute units (px, pt), NaN otherwise.
18
- // Relative units (em, rem, %, vw, etc.) cannot be resolved without DOM context.
19
- const PX_PER_PT = 4 / 3;
20
- function parseFontSizePx(value) {
21
- if (typeof value === 'number') return value;
22
- if (typeof value !== 'string') return NaN;
23
- const match = value.match(/^([\d.]+)(px|pt)?$/i);
24
- if (!match) return NaN;
25
- const num = parseFloat(match[1]);
26
- if (Number.isNaN(num) || num <= 0) return NaN;
27
- const unit = (match[2] || 'px').toLowerCase();
28
- return unit === 'pt' ? num * PX_PER_PT : num;
29
- }
30
-
31
16
  function clamp(value, min, max) {
32
17
  return Math.min(Math.max(value, min), max);
33
18
  }
@@ -71,7 +56,6 @@ function getVariationAxisUsage(
71
56
  fontWeights,
72
57
  fontStretches,
73
58
  fontVariationSettings,
74
- fontSizes,
75
59
  props,
76
60
  } of fontUsages) {
77
61
  if (seenFontUrls.has(fontUrl)) continue;
@@ -113,18 +97,6 @@ function getVariationAxisUsage(
113
97
  );
114
98
  }
115
99
 
116
- // Map font-size to the opsz axis. With font-optical-sizing: auto
117
- // (the CSS default), browsers set opsz = font-size in px.
118
- // Only absolute units (px, pt) can be resolved without DOM context.
119
- if (fontSizes) {
120
- for (const fontSize of fontSizes) {
121
- const px = parseFontSizePx(fontSize);
122
- if (!Number.isNaN(px)) {
123
- noteUsedValue(fontUrl, 'opsz', px);
124
- }
125
- }
126
- }
127
-
128
100
  for (const fontVariationSettingsValue of fontVariationSettings) {
129
101
  for (const [axisName, axisValue] of parseFontVariationSettings(
130
102
  fontVariationSettingsValue
@@ -195,7 +167,6 @@ async function getVariationAxisBounds(
195
167
  module.exports = {
196
168
  standardVariationAxes,
197
169
  ignoredVariationAxes,
198
- parseFontSizePx,
199
170
  renderNumberRange,
200
171
  getVariationAxisUsage,
201
172
  getVariationAxisBounds,
@@ -42,6 +42,41 @@ async function warnAboutMissingGlyphs(
42
42
  accumulatedFontFaceDeclarations,
43
43
  } of htmlOrSvgAssetTextsWithProps) {
44
44
  let linesAndColumns;
45
+ // Dedupe scans for the same missing char across different fontUsages on
46
+ // this page. On KaTeX-heavy pages the same symbol is often missing in
47
+ // several font-families, and each scan is an O(N) walk of the HTML text.
48
+ const charLookupCache = new Map();
49
+ const lookupChar = (char) => {
50
+ let cached = charLookupCache.get(char);
51
+ if (cached) return cached;
52
+ let firstLocation;
53
+ let occurrences = 0;
54
+ if (char.length > 0) {
55
+ const sourceText = htmlOrSvgAsset.text;
56
+ let searchIdx = 0;
57
+ while (true) {
58
+ const charIdx = sourceText.indexOf(char, searchIdx);
59
+ if (charIdx === -1) break;
60
+ occurrences++;
61
+ if (occurrences === 1) {
62
+ if (!linesAndColumns) {
63
+ linesAndColumns = new LinesAndColumns(sourceText);
64
+ }
65
+ const position = linesAndColumns.locationForIndex(charIdx);
66
+ firstLocation = `${htmlOrSvgAsset.urlOrDescription}:${
67
+ position.line + 1
68
+ }:${position.column + 1}`;
69
+ }
70
+ searchIdx = charIdx + char.length;
71
+ }
72
+ }
73
+ if (!firstLocation) {
74
+ firstLocation = `${htmlOrSvgAsset.urlOrDescription} (generated content)`;
75
+ }
76
+ cached = { firstLocation, occurrences };
77
+ charLookupCache.set(char, cached);
78
+ return cached;
79
+ };
45
80
  for (const fontUsage of fontUsages) {
46
81
  if (!fontUsage.subsets) continue;
47
82
  const subsetBuffer = Object.values(fontUsage.subsets)[0];
@@ -63,31 +98,7 @@ async function warnAboutMissingGlyphs(
63
98
  // Report only the first location plus a count of remaining
64
99
  // occurrences. A character like U+200B can appear thousands of
65
100
  // times on a page and per-occurrence lines drown the log.
66
- let firstLocation;
67
- let occurrences = 0;
68
- if (char.length > 0) {
69
- const sourceText = htmlOrSvgAsset.text;
70
- let searchIdx = 0;
71
- while (true) {
72
- const charIdx = sourceText.indexOf(char, searchIdx);
73
- if (charIdx === -1) break;
74
- occurrences++;
75
- if (occurrences === 1) {
76
- if (!linesAndColumns) {
77
- linesAndColumns = new LinesAndColumns(sourceText);
78
- }
79
- const position = linesAndColumns.locationForIndex(charIdx);
80
- firstLocation = `${htmlOrSvgAsset.urlOrDescription}:${
81
- position.line + 1
82
- }:${position.column + 1}`;
83
- }
84
- searchIdx = charIdx + char.length;
85
- }
86
- }
87
-
88
- if (!firstLocation) {
89
- firstLocation = `${htmlOrSvgAsset.urlOrDescription} (generated content)`;
90
- }
101
+ const { firstLocation, occurrences } = lookupChar(char);
91
102
 
92
103
  missingGlyphsErrors.push({
93
104
  codePoint,
package/lib/wasmQueue.js CHANGED
@@ -5,10 +5,14 @@
5
5
  let queue = Promise.resolve();
6
6
 
7
7
  function enqueue(fn) {
8
- return (queue = queue.then(
8
+ // Chain fn after the previous task settles. Both handlers wrap fn() in
9
+ // an arrow to avoid leaking the previous result/error as an argument.
10
+ // The error handler ensures a prior rejection doesn't block the queue.
11
+ queue = queue.then(
9
12
  () => fn(),
10
13
  () => fn()
11
- ));
14
+ );
15
+ return queue;
12
16
  }
13
17
 
14
18
  module.exports = enqueue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turntrout/subfont",
3
- "version": "1.5.1",
3
+ "version": "1.7.0",
4
4
  "description": "Automatically subset web fonts to only the characters used on your pages. Fork of Munter/subfont with modern defaults.",
5
5
  "engines": {
6
6
  "node": ">=18.0.0"
@@ -57,12 +57,12 @@
57
57
  "jsdom": "^25.0.0",
58
58
  "lines-and-columns": "^1.1.6",
59
59
  "memoizesync": "^1.1.1",
60
- "p-limit": "^3.0.0",
61
60
  "parse5": "^7.0.0",
62
61
  "postcss": "^8.3.11",
63
62
  "postcss-value-parser": "^4.0.2",
64
63
  "pretty-bytes": "^5.1.0",
65
64
  "puppeteer-core": "^24.39.1",
65
+ "sanitize-filename": "^1.6.4",
66
66
  "specificity": "^0.4.1",
67
67
  "urltools": "^0.4.1",
68
68
  "yargs": "^17.7.2"