@turntrout/subfont 1.10.7 → 1.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 +1 -1
- package/README.md +21 -21
- package/lib/FontTracerPool.d.ts +22 -27
- package/lib/FontTracerPool.d.ts.map +1 -1
- package/lib/FontTracerPool.js +51 -277
- package/lib/FontTracerPool.js.map +1 -1
- package/lib/HeadlessBrowser.d.ts.map +1 -1
- package/lib/HeadlessBrowser.js +44 -28
- package/lib/HeadlessBrowser.js.map +1 -1
- package/lib/cli.js +14 -10
- package/lib/cli.js.map +1 -1
- package/lib/codepointMaps.d.ts.map +1 -1
- package/lib/codepointMaps.js +0 -3
- package/lib/codepointMaps.js.map +1 -1
- package/lib/collectTextsByPage.d.ts +2 -1
- package/lib/collectTextsByPage.d.ts.map +1 -1
- package/lib/collectTextsByPage.js +15 -4
- package/lib/collectTextsByPage.js.map +1 -1
- package/lib/escapeJsStringLiteral.d.ts.map +1 -1
- package/lib/escapeJsStringLiteral.js +12 -5
- package/lib/escapeJsStringLiteral.js.map +1 -1
- package/lib/extractVisibleText.d.ts.map +1 -1
- package/lib/extractVisibleText.js +68 -147
- package/lib/extractVisibleText.js.map +1 -1
- package/lib/fontConverter.d.ts +5 -1
- package/lib/fontConverter.d.ts.map +1 -1
- package/lib/fontConverter.js +47 -122
- package/lib/fontConverter.js.map +1 -1
- package/lib/fontConverterWorker.d.ts +14 -1
- package/lib/fontConverterWorker.d.ts.map +1 -1
- package/lib/fontConverterWorker.js +16 -16
- package/lib/fontConverterWorker.js.map +1 -1
- package/lib/fontFaceHelpers.d.ts +3 -2
- package/lib/fontFaceHelpers.d.ts.map +1 -1
- package/lib/fontFaceHelpers.js +18 -17
- package/lib/fontFaceHelpers.js.map +1 -1
- package/lib/fontFeatureHelpers.d.ts.map +1 -1
- package/lib/fontFeatureHelpers.js +5 -1
- package/lib/fontFeatureHelpers.js.map +1 -1
- package/lib/fontTracerWorker.d.ts +17 -4
- package/lib/fontTracerWorker.d.ts.map +1 -1
- package/lib/fontTracerWorker.js +31 -60
- package/lib/fontTracerWorker.js.map +1 -1
- package/lib/getCssRulesByProperty.d.ts.map +1 -1
- package/lib/getCssRulesByProperty.js +11 -2
- package/lib/getCssRulesByProperty.js.map +1 -1
- package/lib/parseCommandLineOptions.d.ts.map +1 -1
- package/lib/parseCommandLineOptions.js +12 -7
- package/lib/parseCommandLineOptions.js.map +1 -1
- package/lib/piscinaRunWithTimeout.d.ts +18 -0
- package/lib/piscinaRunWithTimeout.d.ts.map +1 -0
- package/lib/piscinaRunWithTimeout.js +78 -0
- package/lib/piscinaRunWithTimeout.js.map +1 -0
- package/lib/subfont.js.map +1 -1
- package/lib/subsetFontWithGlyphs.d.ts +1 -0
- package/lib/subsetFontWithGlyphs.d.ts.map +1 -1
- package/lib/subsetFontWithGlyphs.js +4 -2
- package/lib/subsetFontWithGlyphs.js.map +1 -1
- package/lib/subsetFonts.d.ts +4 -3
- package/lib/subsetFonts.d.ts.map +1 -1
- package/lib/subsetFonts.js +19 -11
- package/lib/subsetFonts.js.map +1 -1
- package/lib/subsetGeneration.d.ts +1 -1
- package/lib/subsetGeneration.d.ts.map +1 -1
- package/lib/subsetGeneration.js +7 -7
- package/lib/subsetGeneration.js.map +1 -1
- package/lib/types/shared.d.ts +1 -0
- package/lib/types/shared.d.ts.map +1 -1
- package/lib/types/shared.js.map +1 -1
- package/lib/unquote.d.ts +17 -0
- package/lib/unquote.d.ts.map +1 -1
- package/lib/unquote.js +63 -9
- package/lib/unquote.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -22,7 +22,7 @@ On [TurnTrout.com](https://github.com/alexander-turner/TurnTrout.com) (382 pages
|
|
|
22
22
|
### New features
|
|
23
23
|
|
|
24
24
|
- **`--cache [dir]`** -- Cache subset results to disk. Speeds up repeat builds.
|
|
25
|
-
- **`--chrome-flags`** -- Custom
|
|
25
|
+
- **`--chrome-flags`** -- Custom flag for headless Chrome with `--dynamic`. Use the `=` form and repeat for multiple flags (`--chrome-flags=--no-sandbox --chrome-flags=--disable-features=Foo,Bar`); the prior comma-separated form was dropped because real flag values like `--disable-features=Foo,Bar` contain commas.
|
|
26
26
|
- **`--concurrency N`** -- Control worker thread count for parallel font tracing.
|
|
27
27
|
- **Parallel font tracing** -- Worker pool (up to 8 threads). Pages sharing identical CSS are traced once.
|
|
28
28
|
- **`--root` validation** -- Fails early with a clear error.
|
package/README.md
CHANGED
|
@@ -65,27 +65,27 @@ subfont path/to/index.html -i --cache
|
|
|
65
65
|
|
|
66
66
|
## Options
|
|
67
67
|
|
|
68
|
-
| Flag | Default | Description
|
|
69
|
-
| -----------------: | :-----: |
|
|
70
|
-
| `-i, --in-place` | off | Modify files in-place
|
|
71
|
-
| `-o, --output` | | Output directory
|
|
72
|
-
| `--root` | | Path to web root (deduced from input files if not specified)
|
|
73
|
-
| `--canonical-root` | | URI root where the site will be deployed
|
|
74
|
-
| `-r, --recursive` | off | Crawl linked pages
|
|
75
|
-
| `--dynamic` | off | Trace with headless browser
|
|
76
|
-
| `--dry-run` | off | Preview without writing
|
|
77
|
-
| `--fallbacks` | on | Async-load the full original font as a fallback for dynamic content
|
|
78
|
-
| `--font-display` | `swap` | `auto`/`block`/`swap`/`fallback`/`optional`
|
|
79
|
-
| `--text` | | Extra characters for every subset
|
|
80
|
-
| `--cache [dir]` | off | Cache subset results to disk between runs
|
|
81
|
-
| `--concurrency N` | auto | Max worker threads (defaults to CPU count, max 8). Warns when exceeding memory-based estimate (~50 MB per worker)
|
|
82
|
-
| `--chrome-flags` | | Custom Chrome
|
|
83
|
-
| `--source-maps` | off | Preserve CSS source maps (slower)
|
|
84
|
-
| `--strict` | off | Exit non-zero if any warnings are emitted
|
|
85
|
-
| `-s, --silent` | off | Suppress all console output
|
|
86
|
-
| `-d, --debug` | off | Verbose timing and font glyph detection info
|
|
87
|
-
| `--relative-urls` | off | Emit relative URLs instead of root-relative
|
|
88
|
-
| `--inline-css` | off | Inline the subset @font-face CSS into HTML
|
|
68
|
+
| Flag | Default | Description |
|
|
69
|
+
| -----------------: | :-----: | :---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
70
|
+
| `-i, --in-place` | off | Modify files in-place |
|
|
71
|
+
| `-o, --output` | | Output directory |
|
|
72
|
+
| `--root` | | Path to web root (deduced from input files if not specified) |
|
|
73
|
+
| `--canonical-root` | | URI root where the site will be deployed |
|
|
74
|
+
| `-r, --recursive` | off | Crawl linked pages |
|
|
75
|
+
| `--dynamic` | off | Trace with headless browser |
|
|
76
|
+
| `--dry-run` | off | Preview without writing |
|
|
77
|
+
| `--fallbacks` | on | Async-load the full original font as a fallback for dynamic content |
|
|
78
|
+
| `--font-display` | `swap` | `auto`/`block`/`swap`/`fallback`/`optional` |
|
|
79
|
+
| `--text` | | Extra characters for every subset |
|
|
80
|
+
| `--cache [dir]` | off | Cache subset results to disk between runs |
|
|
81
|
+
| `--concurrency N` | auto | Max worker threads (defaults to CPU count, max 8). Warns when exceeding memory-based estimate (~50 MB per worker) |
|
|
82
|
+
| `--chrome-flags` | | Custom Chrome flag for `--dynamic`. Use the `=` form and repeat for multiple flags: `--chrome-flags=--no-sandbox --chrome-flags=--disable-features=Foo,Bar` |
|
|
83
|
+
| `--source-maps` | off | Preserve CSS source maps (slower) |
|
|
84
|
+
| `--strict` | off | Exit non-zero if any warnings are emitted |
|
|
85
|
+
| `-s, --silent` | off | Suppress all console output |
|
|
86
|
+
| `-d, --debug` | off | Verbose timing and font glyph detection info |
|
|
87
|
+
| `--relative-urls` | off | Emit relative URLs instead of root-relative |
|
|
88
|
+
| `--inline-css` | off | Inline the subset @font-face CSS into HTML |
|
|
89
89
|
|
|
90
90
|
Run `subfont --help` for the full list.
|
|
91
91
|
|
package/lib/FontTracerPool.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
interface FontTracerPoolOptions {
|
|
2
|
+
taskTimeoutMs?: number;
|
|
3
|
+
}
|
|
1
4
|
interface StylesheetWithPredicates {
|
|
2
5
|
text?: string;
|
|
3
6
|
asset?: {
|
|
@@ -5,39 +8,31 @@ interface StylesheetWithPredicates {
|
|
|
5
8
|
};
|
|
6
9
|
predicates?: Record<string, unknown>;
|
|
7
10
|
}
|
|
8
|
-
interface
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
interface TextByPropsEntry {
|
|
12
|
+
text: string;
|
|
13
|
+
props: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
interface TraceOptions {
|
|
16
|
+
signal?: AbortSignal;
|
|
11
17
|
}
|
|
12
18
|
declare class FontTracerPool {
|
|
13
|
-
private
|
|
14
|
-
private _numWorkers;
|
|
19
|
+
private _pool;
|
|
15
20
|
private _taskTimeoutMs;
|
|
16
|
-
|
|
17
|
-
private _workers;
|
|
18
|
-
private _idle;
|
|
19
|
-
private _pendingTasks;
|
|
20
|
-
private _taskCallbacks;
|
|
21
|
-
private _taskTimers;
|
|
22
|
-
private _taskByWorker;
|
|
23
|
-
private _nextTaskId;
|
|
24
|
-
private _destroying;
|
|
25
|
-
private _spawning;
|
|
26
|
-
constructor(numWorkers: number, { taskTimeoutMs, respawnOnExit, }?: FontTracerPoolOptions);
|
|
27
|
-
private _spawnOne;
|
|
21
|
+
constructor(numWorkers: number, { taskTimeoutMs }?: FontTracerPoolOptions);
|
|
28
22
|
init(): Promise<void>;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
trace(htmlText: string, stylesheetsWithPredicates: StylesheetWithPredicates[], { signal }?: TraceOptions): Promise<TextByPropsEntry[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Number of worker threads in the underlying piscina pool. Piscina
|
|
26
|
+
* spawns up to minThreads === maxThreads at construction time, though
|
|
27
|
+
* individual workers may still be loading their module when this is
|
|
28
|
+
* read. Exposed primarily for tests.
|
|
29
|
+
*/
|
|
30
|
+
get threadCount(): number;
|
|
36
31
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
32
|
+
* Number of trace tasks waiting in the piscina queue (not yet dispatched
|
|
33
|
+
* to a worker).
|
|
39
34
|
*/
|
|
40
|
-
|
|
35
|
+
get queueSize(): number;
|
|
41
36
|
destroy(): Promise<void>;
|
|
42
37
|
}
|
|
43
38
|
export = FontTracerPool;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FontTracerPool.d.ts","sourceRoot":"","sources":["../src/FontTracerPool.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FontTracerPool.d.ts","sourceRoot":"","sources":["../src/FontTracerPool.ts"],"names":[],"mappings":"AAmBA,UAAU,qBAAqB;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,wBAAwB;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAI1B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAaD,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAC;IAGb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,UAAU,YAAY;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,cAAM,cAAc;IAClB,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,cAAc,CAAS;gBAG7B,UAAU,EAAE,MAAM,EAClB,EAAE,aAAuC,EAAE,GAAE,qBAA0B;IAgBnE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB3B,KAAK,CACH,QAAQ,EAAE,MAAM,EAChB,yBAAyB,EAAE,wBAAwB,EAAE,EACrD,EAAE,MAAM,EAAE,GAAE,YAAiB,GAC5B,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAuB9B;;;;;OAKG;IACH,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;;OAGG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAEK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAG/B;AAED,SAAS,cAAc,CAAC"}
|
package/lib/FontTracerPool.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const pathModule = require("path");
|
|
3
|
-
const
|
|
3
|
+
const piscina_1 = require("piscina");
|
|
4
|
+
const piscinaRunWithTimeout_1 = require("./piscinaRunWithTimeout");
|
|
4
5
|
/**
|
|
5
6
|
* Worker pool for running fontTracer in parallel across pages.
|
|
6
7
|
* Each worker re-parses HTML with jsdom and runs fontTracer independently.
|
|
8
|
+
*
|
|
9
|
+
* Wraps `piscina` to preserve the existing FontTracerPool surface (init,
|
|
10
|
+
* trace, destroy) while delegating worker lifecycle, queueing, and
|
|
11
|
+
* structured-clone handling to a battle-tested dependency.
|
|
7
12
|
*/
|
|
8
13
|
// Heavy pages (large blog posts with elaborate CSS) under CPU contention on
|
|
9
14
|
// CI runners can comfortably exceed a minute in jsdom + font-tracer. The
|
|
@@ -11,298 +16,67 @@ const worker_threads_1 = require("worker_threads");
|
|
|
11
16
|
// errs generous.
|
|
12
17
|
const DEFAULT_TASK_TIMEOUT_MS = 600_000;
|
|
13
18
|
class FontTracerPool {
|
|
14
|
-
|
|
15
|
-
_numWorkers;
|
|
19
|
+
_pool;
|
|
16
20
|
_taskTimeoutMs;
|
|
17
|
-
|
|
18
|
-
_workers;
|
|
19
|
-
_idle;
|
|
20
|
-
_pendingTasks;
|
|
21
|
-
_taskCallbacks;
|
|
22
|
-
_taskTimers;
|
|
23
|
-
_taskByWorker;
|
|
24
|
-
_nextTaskId;
|
|
25
|
-
_destroying;
|
|
26
|
-
_spawning;
|
|
27
|
-
constructor(numWorkers, { taskTimeoutMs = DEFAULT_TASK_TIMEOUT_MS, respawnOnExit = true, } = {}) {
|
|
28
|
-
this._workerPath = pathModule.join(__dirname, 'fontTracerWorker.js');
|
|
29
|
-
this._numWorkers = numWorkers;
|
|
21
|
+
constructor(numWorkers, { taskTimeoutMs = DEFAULT_TASK_TIMEOUT_MS } = {}) {
|
|
30
22
|
this._taskTimeoutMs = taskTimeoutMs;
|
|
31
|
-
this.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
_spawnOne() {
|
|
43
|
-
this._spawning++;
|
|
44
|
-
return new Promise((resolve, reject) => {
|
|
45
|
-
let worker;
|
|
46
|
-
try {
|
|
47
|
-
worker = new worker_threads_1.Worker(this._workerPath);
|
|
48
|
-
}
|
|
49
|
-
catch (err) {
|
|
50
|
-
this._spawning--;
|
|
51
|
-
reject(err instanceof Error ? err : new Error(String(err)));
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
this._workers.push(worker);
|
|
55
|
-
const detachInitHandlers = () => {
|
|
56
|
-
worker.off('message', onMessage);
|
|
57
|
-
worker.off('error', failOnInit);
|
|
58
|
-
worker.off('exit', exitDuringInit);
|
|
59
|
-
};
|
|
60
|
-
const failOnInit = (err) => {
|
|
61
|
-
detachInitHandlers();
|
|
62
|
-
const idx = this._workers.indexOf(worker);
|
|
63
|
-
if (idx !== -1)
|
|
64
|
-
this._workers.splice(idx, 1);
|
|
65
|
-
this._spawning--;
|
|
66
|
-
worker.terminate();
|
|
67
|
-
reject(err);
|
|
68
|
-
};
|
|
69
|
-
const exitDuringInit = (code) => {
|
|
70
|
-
// Worker exited before sending 'ready' (e.g. terminated by destroy()
|
|
71
|
-
// mid-spawn, or crashed during init without emitting 'error'). Settle
|
|
72
|
-
// the spawn promise so the .catch() upstream can run.
|
|
73
|
-
detachInitHandlers();
|
|
74
|
-
const idx = this._workers.indexOf(worker);
|
|
75
|
-
if (idx !== -1)
|
|
76
|
-
this._workers.splice(idx, 1);
|
|
77
|
-
this._spawning--;
|
|
78
|
-
reject(new Error(`Worker exited during init with code ${code}`));
|
|
79
|
-
};
|
|
80
|
-
const onMessage = (msg) => {
|
|
81
|
-
if (msg.type === 'ready') {
|
|
82
|
-
detachInitHandlers();
|
|
83
|
-
worker.on('message', (m) => this._onWorkerMessage(worker, m));
|
|
84
|
-
worker.on('error', (e) => this._onWorkerError(worker, e));
|
|
85
|
-
worker.on('exit', (code) => this._onWorkerExit(worker, code));
|
|
86
|
-
this._idle.push(worker);
|
|
87
|
-
this._spawning--;
|
|
88
|
-
resolve();
|
|
89
|
-
this._dispatchPending();
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
worker.on('message', onMessage);
|
|
93
|
-
worker.on('error', failOnInit);
|
|
94
|
-
worker.on('exit', exitDuringInit);
|
|
95
|
-
worker.postMessage({ type: 'init' });
|
|
23
|
+
this._pool = new piscina_1.Piscina({
|
|
24
|
+
filename: pathModule.join(__dirname, 'fontTracerWorker.js'),
|
|
25
|
+
// Pin pool size so concurrency is bounded by the caller-supplied
|
|
26
|
+
// worker count (memory and CPU contention both scale with this).
|
|
27
|
+
minThreads: numWorkers,
|
|
28
|
+
maxThreads: numWorkers,
|
|
29
|
+
concurrentTasksPerWorker: 1,
|
|
30
|
+
// Workers stay warm for the lifetime of the pool — pages on the
|
|
31
|
+
// same site share stylesheet parses through per-worker memoization.
|
|
32
|
+
idleTimeout: Infinity,
|
|
96
33
|
});
|
|
97
34
|
}
|
|
98
35
|
async init() {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (taskId !== undefined) {
|
|
111
|
-
this._taskByWorker.delete(worker);
|
|
112
|
-
this._clearTaskTimer(taskId);
|
|
113
|
-
const cb = this._taskCallbacks.get(taskId);
|
|
114
|
-
if (cb) {
|
|
115
|
-
this._taskCallbacks.delete(taskId);
|
|
116
|
-
cb.reject(err);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
_clearTaskTimer(taskId) {
|
|
121
|
-
const timer = this._taskTimers.get(taskId);
|
|
122
|
-
if (timer) {
|
|
123
|
-
clearTimeout(timer);
|
|
124
|
-
this._taskTimers.delete(taskId);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
_onWorkerMessage(worker, msg) {
|
|
128
|
-
if (msg.type === 'ready')
|
|
129
|
-
return;
|
|
130
|
-
this._taskByWorker.delete(worker);
|
|
131
|
-
this._clearTaskTimer(msg.taskId);
|
|
132
|
-
const cb = this._taskCallbacks.get(msg.taskId);
|
|
133
|
-
if (cb) {
|
|
134
|
-
this._taskCallbacks.delete(msg.taskId);
|
|
135
|
-
if (msg.type === 'result') {
|
|
136
|
-
cb.resolve(msg.textByProps);
|
|
137
|
-
}
|
|
138
|
-
else if (msg.type === 'error') {
|
|
139
|
-
const detail = msg.stack ? `${msg.error}\n${msg.stack}` : msg.error;
|
|
140
|
-
cb.reject(new Error(`Worker error: ${detail}`));
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
// Worker is now idle, check for pending tasks
|
|
144
|
-
this._idle.push(worker);
|
|
145
|
-
this._dispatchPending();
|
|
146
|
-
}
|
|
147
|
-
_onWorkerExit(worker, code) {
|
|
148
|
-
const workerIdx = this._workers.indexOf(worker);
|
|
149
|
-
if (workerIdx !== -1) {
|
|
150
|
-
this._workers.splice(workerIdx, 1);
|
|
151
|
-
}
|
|
152
|
-
const idleIdx = this._idle.indexOf(worker);
|
|
153
|
-
if (idleIdx !== -1) {
|
|
154
|
-
this._idle.splice(idleIdx, 1);
|
|
155
|
-
}
|
|
156
|
-
// Reject any task that was in-flight on this worker. A graceful exit
|
|
157
|
-
// (code 0) mid-task still leaves the caller waiting, so don't gate on
|
|
158
|
-
// code !== 0.
|
|
159
|
-
const taskId = this._taskByWorker.get(worker);
|
|
160
|
-
this._taskByWorker.delete(worker);
|
|
161
|
-
if (taskId !== undefined) {
|
|
162
|
-
this._clearTaskTimer(taskId);
|
|
163
|
-
const cb = this._taskCallbacks.get(taskId);
|
|
164
|
-
if (cb) {
|
|
165
|
-
this._taskCallbacks.delete(taskId);
|
|
166
|
-
cb.reject(new Error(`Worker exited with code ${code}`));
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
if (this._destroying)
|
|
170
|
-
return;
|
|
171
|
-
if (this._respawnOnExit) {
|
|
172
|
-
this._spawnOne().catch((err) => {
|
|
173
|
-
// Respawn failed — fall back to draining if the pool is now empty.
|
|
174
|
-
if (this._workers.length === 0 &&
|
|
175
|
-
this._spawning === 0 &&
|
|
176
|
-
this._pendingTasks.length > 0) {
|
|
177
|
-
this._drainPendingWith(`All workers have crashed and respawn failed: ${err.message}`);
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
if (code !== 0 && this._workers.length === 0) {
|
|
183
|
-
this._drainPendingWith('All workers have crashed, no workers available');
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
_drainPendingWith(message) {
|
|
187
|
-
for (const task of this._pendingTasks) {
|
|
188
|
-
const cb = this._taskCallbacks.get(task.message.taskId);
|
|
189
|
-
if (cb) {
|
|
190
|
-
this._taskCallbacks.delete(task.message.taskId);
|
|
191
|
-
cb.reject(new Error(message));
|
|
192
|
-
}
|
|
36
|
+
// Piscina spawns workers eagerly when minThreads === maxThreads, but
|
|
37
|
+
// each worker still pays a one-time `require('jsdom')` + `postcss` cost
|
|
38
|
+
// on its first message — hundreds of ms cold. Fire one warmup trace
|
|
39
|
+
// per worker so that cost lands in init() (where the phase tracker
|
|
40
|
+
// expects it) instead of in the first real `trace()` batch.
|
|
41
|
+
//
|
|
42
|
+
// Empty HTML produces an empty result quickly and exercises the full
|
|
43
|
+
// require chain. Tasks load-balance across all idle workers.
|
|
44
|
+
const minThreads = this._pool.options.minThreads;
|
|
45
|
+
if (minThreads > 0) {
|
|
46
|
+
await Promise.all(Array.from({ length: minThreads }, () => this._pool.run({ htmlText: '', stylesheetsWithPredicates: [] })));
|
|
193
47
|
}
|
|
194
|
-
this._pendingTasks = [];
|
|
195
48
|
}
|
|
196
|
-
|
|
197
|
-
if (this._taskTimeoutMs <= 0)
|
|
198
|
-
return;
|
|
199
|
-
const timer = setTimeout(() => {
|
|
200
|
-
this._taskTimers.delete(taskId);
|
|
201
|
-
const cb = this._taskCallbacks.get(taskId);
|
|
202
|
-
if (cb) {
|
|
203
|
-
this._taskCallbacks.delete(taskId);
|
|
204
|
-
cb.reject(new Error(`Font tracing task ${taskId} timed out after ${this._taskTimeoutMs}ms`));
|
|
205
|
-
}
|
|
206
|
-
// Terminate the hung worker so it doesn't permanently consume a pool
|
|
207
|
-
// slot. _onWorkerExit will remove it from _workers and _idle.
|
|
208
|
-
for (const [worker, tid] of this._taskByWorker) {
|
|
209
|
-
if (tid === taskId) {
|
|
210
|
-
this._taskByWorker.delete(worker);
|
|
211
|
-
worker.terminate();
|
|
212
|
-
break;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}, this._taskTimeoutMs);
|
|
216
|
-
timer.unref();
|
|
217
|
-
this._taskTimers.set(taskId, timer);
|
|
218
|
-
}
|
|
219
|
-
_dispatchPending() {
|
|
220
|
-
while (this._idle.length > 0 && this._pendingTasks.length > 0) {
|
|
221
|
-
const worker = this._idle.pop();
|
|
222
|
-
const task = this._pendingTasks.shift();
|
|
223
|
-
this._taskByWorker.set(worker, task.message.taskId);
|
|
224
|
-
try {
|
|
225
|
-
worker.postMessage(task.message);
|
|
226
|
-
this._startTaskTimer(task.message.taskId);
|
|
227
|
-
}
|
|
228
|
-
catch (err) {
|
|
229
|
-
// postMessage can fail synchronously (e.g. structured clone error).
|
|
230
|
-
// Return the worker to the idle pool and reject the task.
|
|
231
|
-
this._taskByWorker.delete(worker);
|
|
232
|
-
this._idle.push(worker);
|
|
233
|
-
const cb = this._taskCallbacks.get(task.message.taskId);
|
|
234
|
-
if (cb) {
|
|
235
|
-
this._taskCallbacks.delete(task.message.taskId);
|
|
236
|
-
cb.reject(err);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Run fontTracer on the given HTML text + stylesheets in a worker.
|
|
243
|
-
* Returns a promise that resolves to textByProps.
|
|
244
|
-
*/
|
|
245
|
-
// The pool is payload-agnostic; callers (subsetFonts.ts) interpret the
|
|
246
|
-
// returned textByProps according to font-tracer's contract.
|
|
247
|
-
trace(htmlText, stylesheetsWithPredicates
|
|
248
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
249
|
-
) {
|
|
250
|
-
const taskId = this._nextTaskId++;
|
|
49
|
+
trace(htmlText, stylesheetsWithPredicates, { signal } = {}) {
|
|
251
50
|
// Serialize stylesheets to plain data — asset objects contain DOM/PostCSS
|
|
252
51
|
// trees that cannot be transferred via structured clone.
|
|
253
|
-
const
|
|
52
|
+
const serialized = stylesheetsWithPredicates.map((entry) => ({
|
|
254
53
|
text: entry.text || (entry.asset && entry.asset.text) || '',
|
|
255
54
|
predicates: entry.predicates || {},
|
|
256
55
|
}));
|
|
257
|
-
const
|
|
258
|
-
type: 'trace',
|
|
259
|
-
taskId,
|
|
56
|
+
const task = {
|
|
260
57
|
htmlText,
|
|
261
|
-
stylesheetsWithPredicates:
|
|
58
|
+
stylesheetsWithPredicates: serialized,
|
|
262
59
|
};
|
|
263
|
-
return
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
60
|
+
return (0, piscinaRunWithTimeout_1.runWithTimeoutAndSignal)(this._pool, task, signal, this._taskTimeoutMs, (ms) => `Font tracing task timed out after ${ms}ms`);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Number of worker threads in the underlying piscina pool. Piscina
|
|
64
|
+
* spawns up to minThreads === maxThreads at construction time, though
|
|
65
|
+
* individual workers may still be loading their module when this is
|
|
66
|
+
* read. Exposed primarily for tests.
|
|
67
|
+
*/
|
|
68
|
+
get threadCount() {
|
|
69
|
+
return this._pool.threads.length;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Number of trace tasks waiting in the piscina queue (not yet dispatched
|
|
73
|
+
* to a worker).
|
|
74
|
+
*/
|
|
75
|
+
get queueSize() {
|
|
76
|
+
return this._pool.queueSize;
|
|
268
77
|
}
|
|
269
78
|
async destroy() {
|
|
270
|
-
this.
|
|
271
|
-
// Clear all task timers
|
|
272
|
-
for (const timer of this._taskTimers.values()) {
|
|
273
|
-
clearTimeout(timer);
|
|
274
|
-
}
|
|
275
|
-
this._taskTimers.clear();
|
|
276
|
-
// Reject any tasks still waiting in the queue
|
|
277
|
-
for (const task of this._pendingTasks) {
|
|
278
|
-
const cb = this._taskCallbacks.get(task.message.taskId);
|
|
279
|
-
if (cb) {
|
|
280
|
-
this._taskCallbacks.delete(task.message.taskId);
|
|
281
|
-
cb.reject(new Error('Worker pool destroyed'));
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
this._pendingTasks = [];
|
|
285
|
-
// Reject any in-flight tasks still assigned to workers.
|
|
286
|
-
// Clear _taskByWorker before terminate() so _onWorkerExit won't double-reject.
|
|
287
|
-
for (const [, taskId] of this._taskByWorker) {
|
|
288
|
-
const cb = this._taskCallbacks.get(taskId);
|
|
289
|
-
if (cb) {
|
|
290
|
-
this._taskCallbacks.delete(taskId);
|
|
291
|
-
cb.reject(new Error('Worker pool destroyed'));
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
this._taskByWorker.clear();
|
|
295
|
-
// Terminate workers with a 5-second timeout to prevent hanging
|
|
296
|
-
const TERMINATE_TIMEOUT_MS = 5000;
|
|
297
|
-
await Promise.all(this._workers.map((w) => Promise.race([
|
|
298
|
-
w.terminate(),
|
|
299
|
-
new Promise((resolve) => {
|
|
300
|
-
const timer = setTimeout(resolve, TERMINATE_TIMEOUT_MS);
|
|
301
|
-
timer.unref();
|
|
302
|
-
}),
|
|
303
|
-
])));
|
|
304
|
-
this._workers = [];
|
|
305
|
-
this._idle = [];
|
|
79
|
+
await this._pool.destroy();
|
|
306
80
|
}
|
|
307
81
|
}
|
|
308
82
|
module.exports = FontTracerPool;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FontTracerPool.js","sourceRoot":"","sources":["../src/FontTracerPool.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FontTracerPool.js","sourceRoot":"","sources":["../src/FontTracerPool.ts"],"names":[],"mappings":";AAAA,mCAAoC;AACpC,qCAAkC;AAClC,mEAAkE;AAElE;;;;;;;GAOG;AAEH,4EAA4E;AAC5E,yEAAyE;AACzE,2EAA2E;AAC3E,iBAAiB;AACjB,MAAM,uBAAuB,GAAG,OAAO,CAAC;AAqCxC,MAAM,cAAc;IACV,KAAK,CAAU;IACf,cAAc,CAAS;IAE/B,YACE,UAAkB,EAClB,EAAE,aAAa,GAAG,uBAAuB,KAA4B,EAAE;QAEvE,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;QACpC,IAAI,CAAC,KAAK,GAAG,IAAI,iBAAO,CAAC;YACvB,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,qBAAqB,CAAC;YAC3D,iEAAiE;YACjE,iEAAiE;YACjE,UAAU,EAAE,UAAU;YACtB,UAAU,EAAE,UAAU;YACtB,wBAAwB,EAAE,CAAC;YAC3B,gEAAgE;YAChE,oEAAoE;YACpE,WAAW,EAAE,QAAQ;SACtB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI;QACR,qEAAqE;QACrE,wEAAwE;QACxE,oEAAoE;QACpE,mEAAmE;QACnE,4DAA4D;QAC5D,EAAE;QACF,qEAAqE;QACrE,6DAA6D;QAC7D,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC;QACjD,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACnB,MAAM,OAAO,CAAC,GAAG,CACf,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,CACtC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,yBAAyB,EAAE,EAAE,EAAE,CAAC,CAChE,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CACH,QAAgB,EAChB,yBAAqD,EACrD,EAAE,MAAM,KAAmB,EAAE;QAE7B,0EAA0E;QAC1E,yDAAyD;QACzD,MAAM,UAAU,GAA2B,yBAAyB,CAAC,GAAG,CACtE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACV,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE;YAC3D,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,EAAE;SACnC,CAAC,CACH,CAAC;QACF,MAAM,IAAI,GAAc;YACtB,QAAQ;YACR,yBAAyB,EAAE,UAAU;SACtC,CAAC;QAEF,OAAO,IAAA,+CAAuB,EAC5B,IAAI,CAAC,KAAK,EACV,IAAI,EACJ,MAAM,EACN,IAAI,CAAC,cAAc,EACnB,CAAC,EAAE,EAAE,EAAE,CAAC,qCAAqC,EAAE,IAAI,CACpD,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;IACnC,CAAC;IAED;;;OAGG;IACH,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;CACF;AAED,iBAAS,cAAc,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HeadlessBrowser.d.ts","sourceRoot":"","sources":["../src/HeadlessBrowser.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAc,KAAK,EAAE,MAAM,YAAY,CAAC;AAKpD,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA2E3C,UAAU,sBAAsB;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,cAAM,eAAe;IACnB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,WAAW,CAAW;IAC9B,OAAO,CAAC,cAAc,CAAC,CAA4B;IACnD,OAAO,CAAC,OAAO,CAAkB;gBAErB,EAAE,OAAO,EAAE,UAAe,EAAE,EAAE,sBAAsB;IAKhE,OAAO,CAAC,sBAAsB;IAgB9B,OAAO,CAAC,yBAAyB;
|
|
1
|
+
{"version":3,"file":"HeadlessBrowser.d.ts","sourceRoot":"","sources":["../src/HeadlessBrowser.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAc,KAAK,EAAE,MAAM,YAAY,CAAC;AAKpD,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA2E3C,UAAU,sBAAsB;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,cAAM,eAAe;IACnB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,WAAW,CAAW;IAC9B,OAAO,CAAC,cAAc,CAAC,CAA4B;IACnD,OAAO,CAAC,OAAO,CAAkB;gBAErB,EAAE,OAAO,EAAE,UAAe,EAAE,EAAE,sBAAsB;IAKhE,OAAO,CAAC,sBAAsB;IAgB9B,OAAO,CAAC,yBAAyB;IA0CjC,OAAO,CAAC,qBAAqB;IA2BvB,SAAS,CAAC,SAAS,EAAE,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAwEnD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAkB7B;AAED,SAAS,eAAe,CAAC"}
|
package/lib/HeadlessBrowser.js
CHANGED
|
@@ -114,40 +114,41 @@ class HeadlessBrowser {
|
|
|
114
114
|
return this._launchPromise;
|
|
115
115
|
}
|
|
116
116
|
_attachRequestInterceptor(page, assetGraph, baseUrl) {
|
|
117
|
+
// Puppeteer request methods return promises, but page.on callbacks
|
|
118
|
+
// are synchronous. Attach .catch(noop) to each call so rejections
|
|
119
|
+
// (e.g. request already handled, page closing) don't become
|
|
120
|
+
// unhandled-rejection crashes.
|
|
121
|
+
const noop = () => { };
|
|
117
122
|
page.on('request', (request) => {
|
|
118
123
|
const url = request.url();
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
else {
|
|
137
|
-
request.respond({ status: 404, body: '' });
|
|
138
|
-
}
|
|
139
|
-
return;
|
|
124
|
+
if (url.startsWith(baseUrl)) {
|
|
125
|
+
let agUrl = url.replace(baseUrl, assetGraph.root);
|
|
126
|
+
if (/\/$/.test(agUrl)) {
|
|
127
|
+
agUrl += 'index.html';
|
|
128
|
+
}
|
|
129
|
+
const asset = assetGraph.findAssets({
|
|
130
|
+
isLoaded: true,
|
|
131
|
+
url: agUrl,
|
|
132
|
+
})[0];
|
|
133
|
+
if (asset) {
|
|
134
|
+
void request
|
|
135
|
+
.respond({
|
|
136
|
+
status: 200,
|
|
137
|
+
contentType: asset.contentType,
|
|
138
|
+
body: asset.rawSrc,
|
|
139
|
+
})
|
|
140
|
+
.catch(noop);
|
|
140
141
|
}
|
|
141
|
-
|
|
142
|
-
request.
|
|
143
|
-
return;
|
|
142
|
+
else {
|
|
143
|
+
void request.respond({ status: 404, body: '' }).catch(noop);
|
|
144
144
|
}
|
|
145
|
-
|
|
146
|
-
request.abort('failed');
|
|
145
|
+
return;
|
|
147
146
|
}
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
if (url.startsWith('file:')) {
|
|
148
|
+
void request.continue().catch(noop);
|
|
149
|
+
return;
|
|
150
150
|
}
|
|
151
|
+
void request.abort('failed').catch(noop);
|
|
151
152
|
});
|
|
152
153
|
}
|
|
153
154
|
_attachErrorListeners(page) {
|
|
@@ -211,6 +212,21 @@ class HeadlessBrowser {
|
|
|
211
212
|
}
|
|
212
213
|
}
|
|
213
214
|
finally {
|
|
215
|
+
// Detach request interception before closing so puppeteer's CDP
|
|
216
|
+
// bookkeeping has a chance to flush pending intercepted requests;
|
|
217
|
+
// skipping this can leave puppeteer's internal queue waiting on a
|
|
218
|
+
// continue/abort for a request whose page just went away. Failures
|
|
219
|
+
// here are noisy but non-fatal — log and continue so they can't
|
|
220
|
+
// mask a real page.close() error that the caller does need to see.
|
|
221
|
+
try {
|
|
222
|
+
await page.setRequestInterception(false);
|
|
223
|
+
}
|
|
224
|
+
catch (err) {
|
|
225
|
+
this.console.error(`HeadlessBrowser: setRequestInterception(false) failed during cleanup: ${err.message}`);
|
|
226
|
+
}
|
|
227
|
+
// Intentionally do NOT swallow a page.close() rejection — if the page
|
|
228
|
+
// can't be closed cleanly that's a real problem the orchestrator
|
|
229
|
+
// (subsetFonts.ts) needs to surface.
|
|
214
230
|
await page.close();
|
|
215
231
|
}
|
|
216
232
|
}
|