@turntrout/subfont 1.0.3 → 1.0.4
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/FontTracerPool.js +13 -1
- package/lib/HeadlessBrowser.js +3 -3
- package/lib/cli.js +1 -1
- package/lib/collectTextsByPage.js +1 -3
- package/lib/extractVisibleText.js +11 -1
- package/lib/fontFaceHelpers.js +22 -22
- package/lib/subfont.js +20 -4
- package/lib/subsetGeneration.js +10 -0
- package/package.json +1 -1
package/lib/FontTracerPool.js
CHANGED
|
@@ -159,7 +159,19 @@ class FontTracerPool {
|
|
|
159
159
|
}
|
|
160
160
|
this._pendingTasks = [];
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
// Terminate workers with a 5-second timeout to prevent hanging
|
|
163
|
+
const TERMINATE_TIMEOUT_MS = 5000;
|
|
164
|
+
await Promise.all(
|
|
165
|
+
this._workers.map((w) =>
|
|
166
|
+
Promise.race([
|
|
167
|
+
w.terminate(),
|
|
168
|
+
new Promise((resolve) => {
|
|
169
|
+
const timer = setTimeout(resolve, TERMINATE_TIMEOUT_MS);
|
|
170
|
+
timer.unref();
|
|
171
|
+
}),
|
|
172
|
+
])
|
|
173
|
+
)
|
|
174
|
+
);
|
|
163
175
|
this._workers = [];
|
|
164
176
|
this._idle = [];
|
|
165
177
|
}
|
package/lib/HeadlessBrowser.js
CHANGED
|
@@ -148,10 +148,10 @@ class HeadlessBrowser {
|
|
|
148
148
|
`${request.method()} ${request.url()} returned ${response.status()}`
|
|
149
149
|
);
|
|
150
150
|
} else {
|
|
151
|
+
const failure = request.failure();
|
|
152
|
+
const reason = failure ? failure.errorText : 'unknown error';
|
|
151
153
|
this.console.error(
|
|
152
|
-
`${request.method()} ${request.url()} failed: ${
|
|
153
|
-
request.failure().errorText
|
|
154
|
-
}`
|
|
154
|
+
`${request.method()} ${request.url()} failed: ${reason}`
|
|
155
155
|
);
|
|
156
156
|
}
|
|
157
157
|
});
|
package/lib/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ const { yargs, help, ...options } = require('./parseCommandLineOptions')();
|
|
|
5
5
|
require('@gustavnikolaj/async-main-wrap')(require('./subfont'), {
|
|
6
6
|
processError(err) {
|
|
7
7
|
yargs.showHelp();
|
|
8
|
-
if (err.
|
|
8
|
+
if (err.name === 'UsageError') {
|
|
9
9
|
// Avoid rendering a stack trace for the wrong usage errors
|
|
10
10
|
err.customOutput = err.message;
|
|
11
11
|
}
|
|
@@ -397,10 +397,8 @@ async function tracePages(
|
|
|
397
397
|
}
|
|
398
398
|
});
|
|
399
399
|
await Promise.all(tracePromises);
|
|
400
|
+
} finally {
|
|
400
401
|
await pool.destroy();
|
|
401
|
-
} catch (err) {
|
|
402
|
-
await pool.destroy();
|
|
403
|
-
throw err;
|
|
404
402
|
}
|
|
405
403
|
} else if (pagesNeedingFullTrace.length > 0) {
|
|
406
404
|
const totalPages = pagesNeedingFullTrace.length;
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
const parse5 = require('parse5');
|
|
2
2
|
|
|
3
|
-
const INVISIBLE_ELEMENTS = new Set([
|
|
3
|
+
const INVISIBLE_ELEMENTS = new Set([
|
|
4
|
+
'script',
|
|
5
|
+
'style',
|
|
6
|
+
'svg',
|
|
7
|
+
'template',
|
|
8
|
+
'head',
|
|
9
|
+
'noscript',
|
|
10
|
+
'iframe',
|
|
11
|
+
'object',
|
|
12
|
+
'embed',
|
|
13
|
+
]);
|
|
4
14
|
const TEXT_ATTRIBUTES = new Set([
|
|
5
15
|
'alt',
|
|
6
16
|
'title',
|
package/lib/fontFaceHelpers.js
CHANGED
|
@@ -18,7 +18,9 @@ function stringifyFontFamily(name) {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function maybeCssQuote(value) {
|
|
21
|
-
|
|
21
|
+
// CSS identifiers must start with a letter or underscore (or hyphen
|
|
22
|
+
// followed by a letter/underscore), not a digit or bare hyphen.
|
|
23
|
+
if (/^[a-zA-Z_][a-zA-Z0-9_-]*$|^-[a-zA-Z_][a-zA-Z0-9_-]*$/.test(value)) {
|
|
22
24
|
return value;
|
|
23
25
|
} else {
|
|
24
26
|
return `'${value.replace(/'/g, "\\'")}'`;
|
|
@@ -26,31 +28,29 @@ function maybeCssQuote(value) {
|
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
function getPreferredFontUrl(cssFontFaceSrcRelations = []) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
// Priority: woff2 > woff > truetype > opentype, preferring explicit
|
|
32
|
+
// format() declarations over asset-type guesses.
|
|
33
|
+
const formatPriority = { woff2: 0, woff: 1, truetype: 2, opentype: 3 };
|
|
34
|
+
const typePriority = { Woff2: 4, Woff: 5, Ttf: 6, Otf: 7 };
|
|
35
|
+
|
|
36
|
+
let bestUrl;
|
|
37
|
+
let bestPriority = Infinity;
|
|
38
|
+
|
|
39
|
+
for (const r of cssFontFaceSrcRelations) {
|
|
40
|
+
let priority;
|
|
41
|
+
if (r.format) {
|
|
42
|
+
priority = formatPriority[r.format.toLowerCase()];
|
|
40
43
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (relation) {
|
|
49
|
-
return relation.to.url;
|
|
44
|
+
if (priority === undefined) {
|
|
45
|
+
priority = typePriority[r.to.type];
|
|
46
|
+
}
|
|
47
|
+
if (priority !== undefined && priority < bestPriority) {
|
|
48
|
+
bestPriority = priority;
|
|
49
|
+
bestUrl = r.to.url;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
return
|
|
53
|
+
return bestUrl;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// Temporarily switch all relation hrefs to absolute so that
|
package/lib/subfont.js
CHANGED
|
@@ -6,6 +6,13 @@ const urlTools = require('urltools');
|
|
|
6
6
|
const util = require('util');
|
|
7
7
|
const subsetFonts = require('./subsetFonts');
|
|
8
8
|
|
|
9
|
+
class UsageError extends Error {
|
|
10
|
+
constructor(message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = 'UsageError';
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
9
16
|
module.exports = async function subfont(
|
|
10
17
|
{
|
|
11
18
|
root,
|
|
@@ -30,6 +37,13 @@ module.exports = async function subfont(
|
|
|
30
37
|
},
|
|
31
38
|
console
|
|
32
39
|
) {
|
|
40
|
+
if (
|
|
41
|
+
concurrency !== undefined &&
|
|
42
|
+
(!Number.isInteger(concurrency) || concurrency < 1)
|
|
43
|
+
) {
|
|
44
|
+
throw new UsageError('--concurrency must be a positive integer');
|
|
45
|
+
}
|
|
46
|
+
|
|
33
47
|
const formats = ['woff2'];
|
|
34
48
|
|
|
35
49
|
function logToConsole(severity, ...args) {
|
|
@@ -51,7 +65,7 @@ module.exports = async function subfont(
|
|
|
51
65
|
try {
|
|
52
66
|
await fsPromises.access(rootPath);
|
|
53
67
|
} catch {
|
|
54
|
-
throw new
|
|
68
|
+
throw new UsageError(`The --root path does not exist: ${rootPath}`);
|
|
55
69
|
}
|
|
56
70
|
}
|
|
57
71
|
const outRoot = output && urlTools.urlOrFsPathToUrl(output, true);
|
|
@@ -75,19 +89,19 @@ module.exports = async function subfont(
|
|
|
75
89
|
inputUrls = [`${rootUrl}**/*.html`];
|
|
76
90
|
warn(`No input files specified, defaulting to ${inputUrls[0]}`);
|
|
77
91
|
} else {
|
|
78
|
-
throw new
|
|
92
|
+
throw new UsageError(
|
|
79
93
|
"No input files and no --root specified (or it isn't file:), cannot proceed.\n"
|
|
80
94
|
);
|
|
81
95
|
}
|
|
82
96
|
|
|
83
97
|
if (!inputUrls[0].startsWith('file:') && !outRoot && !dryRun) {
|
|
84
|
-
throw new
|
|
98
|
+
throw new UsageError(
|
|
85
99
|
'--output has to be specified when using non-file input urls'
|
|
86
100
|
);
|
|
87
101
|
}
|
|
88
102
|
|
|
89
103
|
if (!inPlace && !outRoot && !dryRun) {
|
|
90
|
-
throw new
|
|
104
|
+
throw new UsageError(
|
|
91
105
|
'Either --output, --in-place, or --dry-run has to be specified'
|
|
92
106
|
);
|
|
93
107
|
}
|
|
@@ -575,3 +589,5 @@ module.exports = async function subfont(
|
|
|
575
589
|
}
|
|
576
590
|
return assetGraph;
|
|
577
591
|
};
|
|
592
|
+
|
|
593
|
+
module.exports.UsageError = UsageError;
|
package/lib/subsetGeneration.js
CHANGED
|
@@ -63,6 +63,16 @@ class SubsetDiskCache {
|
|
|
63
63
|
try {
|
|
64
64
|
await fs.writeFile(filePath, buffer);
|
|
65
65
|
} catch (err) {
|
|
66
|
+
// If the directory was removed after init, retry once
|
|
67
|
+
if (err.code === 'ENOENT') {
|
|
68
|
+
try {
|
|
69
|
+
await fs.mkdir(this._cacheDir, { recursive: true });
|
|
70
|
+
await fs.writeFile(filePath, buffer);
|
|
71
|
+
return;
|
|
72
|
+
} catch {
|
|
73
|
+
// Fall through to warning below
|
|
74
|
+
}
|
|
75
|
+
}
|
|
66
76
|
if (this._warnedWrite) return;
|
|
67
77
|
this._warnedWrite = true;
|
|
68
78
|
if (this._console) {
|
package/package.json
CHANGED