@turntrout/subfont 1.0.5 → 1.1.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/README.md +20 -14
- package/lib/HeadlessBrowser.js +7 -1
- package/lib/concurrencyLimit.js +10 -0
- package/lib/fontTracerWorker.js +6 -6
- package/lib/parseCommandLineOptions.js +19 -2
- package/lib/subfont.js +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,20 +36,26 @@ subfont path/to/index.html -i --cache
|
|
|
36
36
|
|
|
37
37
|
## Options
|
|
38
38
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
| `--
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
39
|
+
| Flag | Default | Description |
|
|
40
|
+
| -----------------: | :-----: | :----------------------------------------------------------- |
|
|
41
|
+
| `-i, --in-place` | off | Modify files in-place |
|
|
42
|
+
| `-o, --output` | | Output directory |
|
|
43
|
+
| `--root` | | Path to web root (deduced from input files if not specified) |
|
|
44
|
+
| `--canonical-root` | | URI root where the site will be deployed |
|
|
45
|
+
| `-r, --recursive` | off | Crawl linked pages |
|
|
46
|
+
| `--dynamic` | off | Trace with headless browser |
|
|
47
|
+
| `--dry-run` | off | Preview without writing |
|
|
48
|
+
| `--fallbacks` | on | Load the full original font for characters not in the subset |
|
|
49
|
+
| `--font-display` | `swap` | `auto`/`block`/`swap`/`fallback`/`optional` |
|
|
50
|
+
| `--text` | | Extra characters for every subset |
|
|
51
|
+
| `--cache [dir]` | off | Cache subset results to disk between runs |
|
|
52
|
+
| `--concurrency N` | | Max worker threads (capped by available memory, ~50 MB each) |
|
|
53
|
+
| `--chrome-flags` | | Custom Chrome flags for `--dynamic` |
|
|
54
|
+
| `--source-maps` | off | Preserve CSS source maps (slower) |
|
|
55
|
+
| `-s, --silent` | off | Suppress all console output |
|
|
56
|
+
| `-d, --debug` | off | Verbose timing and font glyph detection info |
|
|
57
|
+
| `--relative-urls` | off | Emit relative URLs instead of root-relative |
|
|
58
|
+
| `--inline-css` | off | Inline the subset @font-face CSS into HTML |
|
|
53
59
|
|
|
54
60
|
Run `subfont --help` for the full list.
|
|
55
61
|
|
package/lib/HeadlessBrowser.js
CHANGED
|
@@ -203,7 +203,13 @@ class HeadlessBrowser {
|
|
|
203
203
|
const launchPromise = this._launchPromise;
|
|
204
204
|
if (launchPromise) {
|
|
205
205
|
this._launchPromise = undefined;
|
|
206
|
-
|
|
206
|
+
let browser;
|
|
207
|
+
try {
|
|
208
|
+
browser = await launchPromise;
|
|
209
|
+
} catch {
|
|
210
|
+
// Launch failed — nothing to close
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
207
213
|
await browser.close();
|
|
208
214
|
}
|
|
209
215
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const os = require('os');
|
|
2
|
+
|
|
3
|
+
// Approximate memory footprint of each worker thread (jsdom + font-tracer).
|
|
4
|
+
const WORKER_MEMORY_BYTES = 50 * 1024 * 1024; // 50 MB
|
|
5
|
+
|
|
6
|
+
function getMaxConcurrency() {
|
|
7
|
+
return Math.max(1, Math.floor(os.totalmem() / WORKER_MEMORY_BYTES));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
module.exports = { WORKER_MEMORY_BYTES, getMaxConcurrency };
|
package/lib/fontTracerWorker.js
CHANGED
|
@@ -28,11 +28,11 @@ parentPort.on('message', (msg) => {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
if (msg.type === 'trace') {
|
|
31
|
+
const { taskId, htmlText, stylesheetsWithPredicates: serialized } = msg;
|
|
32
|
+
let dom;
|
|
31
33
|
try {
|
|
32
|
-
const { taskId, htmlText, stylesheetsWithPredicates: serialized } = msg;
|
|
33
|
-
|
|
34
34
|
// Re-parse HTML with jsdom to get a DOM document
|
|
35
|
-
|
|
35
|
+
dom = new JSDOM(htmlText);
|
|
36
36
|
const document = dom.window.document;
|
|
37
37
|
|
|
38
38
|
// Re-parse CSS from serialized text — asset objects with PostCSS
|
|
@@ -50,9 +50,6 @@ parentPort.on('message', (msg) => {
|
|
|
50
50
|
getCssRulesByProperty: memoizedGetCssRulesByProperty,
|
|
51
51
|
});
|
|
52
52
|
|
|
53
|
-
// Clean up jsdom to free memory
|
|
54
|
-
dom.window.close();
|
|
55
|
-
|
|
56
53
|
// Strip any non-serializable data from results
|
|
57
54
|
const serializableResults = textByProps.map((entry) => ({
|
|
58
55
|
text: entry.text,
|
|
@@ -71,6 +68,9 @@ parentPort.on('message', (msg) => {
|
|
|
71
68
|
error: err.message,
|
|
72
69
|
stack: err.stack,
|
|
73
70
|
});
|
|
71
|
+
} finally {
|
|
72
|
+
// Clean up jsdom to free memory — must run even if fontTracer throws
|
|
73
|
+
if (dom) dom.window.close();
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
});
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
module.exports = function parseCommandLineOptions(argv) {
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const { getMaxConcurrency } = require('./concurrencyLimit');
|
|
2
4
|
let yargs = require('yargs');
|
|
3
5
|
if (argv) {
|
|
4
6
|
yargs = yargs(argv);
|
|
5
7
|
}
|
|
8
|
+
|
|
9
|
+
const maxConcurrency = getMaxConcurrency();
|
|
10
|
+
|
|
6
11
|
yargs
|
|
7
12
|
.usage(
|
|
8
13
|
'Create optimal font subsets from your actual font usage.\n$0 [options] <htmlFile(s) | url(s)>'
|
|
@@ -106,10 +111,22 @@ module.exports = function parseCommandLineOptions(argv) {
|
|
|
106
111
|
},
|
|
107
112
|
})
|
|
108
113
|
.options('concurrency', {
|
|
109
|
-
describe:
|
|
110
|
-
'Maximum number of worker threads for parallel font tracing. Defaults to the number of CPU cores (max 8)',
|
|
114
|
+
describe: `Maximum number of worker threads for parallel font tracing. Defaults to the number of CPU cores (max 8). Upper bound: ${maxConcurrency} (based on available memory)`,
|
|
111
115
|
type: 'number',
|
|
112
116
|
})
|
|
117
|
+
.check((argv) => {
|
|
118
|
+
if (argv.concurrency !== undefined) {
|
|
119
|
+
if (!Number.isInteger(argv.concurrency) || argv.concurrency < 1) {
|
|
120
|
+
throw new Error('--concurrency must be a positive integer');
|
|
121
|
+
}
|
|
122
|
+
if (argv.concurrency > maxConcurrency) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`--concurrency must not exceed ${maxConcurrency} (each worker uses ~50 MB; system has ${Math.round(os.totalmem() / (1024 * 1024 * 1024))} GB total)`
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return true;
|
|
129
|
+
})
|
|
113
130
|
.options('source-maps', {
|
|
114
131
|
describe: 'Preserve CSS source maps through subfont processing',
|
|
115
132
|
type: 'boolean',
|
package/lib/subfont.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const fsPromises = require('fs/promises');
|
|
2
|
+
const os = require('os');
|
|
2
3
|
const pathModule = require('path');
|
|
4
|
+
const { getMaxConcurrency } = require('./concurrencyLimit');
|
|
3
5
|
const AssetGraph = require('assetgraph');
|
|
4
6
|
const prettyBytes = require('pretty-bytes');
|
|
5
7
|
const urlTools = require('urltools');
|
|
@@ -43,6 +45,12 @@ module.exports = async function subfont(
|
|
|
43
45
|
) {
|
|
44
46
|
throw new UsageError('--concurrency must be a positive integer');
|
|
45
47
|
}
|
|
48
|
+
const maxConcurrency = getMaxConcurrency();
|
|
49
|
+
if (concurrency !== undefined && concurrency > maxConcurrency) {
|
|
50
|
+
throw new UsageError(
|
|
51
|
+
`--concurrency must not exceed ${maxConcurrency} (each worker uses ~50 MB; system has ${Math.round(os.totalmem() / (1024 * 1024 * 1024))} GB total)`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
46
54
|
|
|
47
55
|
const formats = ['woff2'];
|
|
48
56
|
|
package/package.json
CHANGED