@nitpicker/core 0.6.4 → 0.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/utils.d.ts DELETED
@@ -1,36 +0,0 @@
1
- type SequentialEachChunkCallback<R, T> = (item: T, i: number) => Promise<R>;
2
- /**
3
- * Sequential processing each chunk
4
- *
5
- * 1. Split an array
6
- * ┌───────── array ────────┐
7
- * chunk1 │ chunk2 | chunk3
8
- * ├item ├item ├item
9
- * ├item ├item ├item
10
- * └item └item └item
11
- *
12
- * 2. Runs parallel and sequential
13
- * chunk1 ┬ callback(item) ┐
14
- * │ ├ callback(item) │ Promise.all
15
- * │ └ callback(item) ┘
16
- * │
17
- * (Sequential)
18
- * │
19
- * chunk2 ┬ callback(item) ┐
20
- * │ ├ callback(item) │ Promise.all
21
- * │ └ callback(item) ┘
22
- * │
23
- * (Sequential)
24
- * │
25
- * chunk3 ┬ callback(item) ┐
26
- * │ ├ callback(item) │ Promise.all
27
- * │ └ callback(item) ┘
28
- * │
29
- * (Done)
30
- * @param array
31
- * @param split
32
- * @param callback
33
- * @returns
34
- */
35
- export declare function sequentialEachChunk<R, T>(array: T[], split: number, callback: SequentialEachChunkCallback<R, T>): Promise<R[]>;
36
- export {};
package/lib/utils.js DELETED
@@ -1,43 +0,0 @@
1
- /**
2
- * Sequential processing each chunk
3
- *
4
- * 1. Split an array
5
- * ┌───────── array ────────┐
6
- * chunk1 │ chunk2 | chunk3
7
- * ├item ├item ├item
8
- * ├item ├item ├item
9
- * └item └item └item
10
- *
11
- * 2. Runs parallel and sequential
12
- * chunk1 ┬ callback(item) ┐
13
- * │ ├ callback(item) │ Promise.all
14
- * │ └ callback(item) ┘
15
- * │
16
- * (Sequential)
17
- * │
18
- * chunk2 ┬ callback(item) ┐
19
- * │ ├ callback(item) │ Promise.all
20
- * │ └ callback(item) ┘
21
- * │
22
- * (Sequential)
23
- * │
24
- * chunk3 ┬ callback(item) ┐
25
- * │ ├ callback(item) │ Promise.all
26
- * │ └ callback(item) ┘
27
- * │
28
- * (Done)
29
- * @param array
30
- * @param split
31
- * @param callback
32
- * @returns
33
- */
34
- export async function sequentialEachChunk(array, split, callback) {
35
- array = [...array];
36
- const result = [];
37
- do {
38
- const chunk = array.splice(0, split);
39
- const segRes = await Promise.all(chunk.map((item, i) => callback(item, i + result.length)));
40
- result.push(...segRes);
41
- } while (array.length > 0);
42
- return result;
43
- }
@@ -1,51 +0,0 @@
1
- import type { UrlEventBus } from '../url-event-bus.js';
2
- /**
3
- * Parameters for {@link runInWorker}.
4
- * @template I - Shape of the additional data merged into `workerData`.
5
- */
6
- export interface RunInWorkerParams<I extends Record<string, unknown>> {
7
- /** Absolute path to the module to execute in the Worker. */
8
- readonly filePath: string;
9
- /** Zero-based index of the current item (for progress display). */
10
- readonly num: number;
11
- /** Total number of items in the batch. */
12
- readonly total: number;
13
- /** URL event bus; `'url'` messages from the Worker are re-emitted here. */
14
- readonly emitter: UrlEventBus;
15
- /** Plugin-specific data to pass to the Worker module. */
16
- readonly initialData: I;
17
- }
18
- /**
19
- * Spawns a Worker thread to execute a plugin module and returns its result.
20
- *
21
- * This is the bridge between the main thread's `deal()` parallelism and the
22
- * per-page Worker execution. Each call creates a new Worker, passes the
23
- * initial data via `workerData`, and listens for messages until the Worker
24
- * signals completion.
25
- *
26
- * ## Why Worker threads?
27
- *
28
- * DOM-heavy plugins (JSDOM + axe-core, markuplint, etc.) allocate significant
29
- * memory per page. Running them in Workers ensures:
30
- * - **Memory isolation**: JSDOM windows are fully GC'd when the Worker exits
31
- * - **Crash containment**: A plugin segfault/OOM kills only the Worker, not the process
32
- * - **Signal handling**: Graceful cleanup on SIGABRT, SIGQUIT, and other signals
33
- *
34
- * ## Message protocol
35
- *
36
- * The Worker sends two types of messages:
37
- * - `{ type: 'url', url: string }` - URL discovery notification, forwarded to the emitter
38
- * - `{ type: 'finish', result: R }` - Execution complete, resolves the Promise
39
- *
40
- * ## Fallback mode
41
- *
42
- * When `useWorker` is `false`, execution delegates directly to
43
- * {@link ./runner.ts!runner} in the main thread.
44
- * @template I - Shape of the additional data merged into `workerData`.
45
- * @template R - Return type expected from the Worker module.
46
- * @param params - Parameters containing file path, progress info, emitter, and data.
47
- * @returns The result produced by the Worker module's default export.
48
- * @see {@link ./worker.ts} for the Worker-side entry point
49
- * @see {@link ./runner.ts!runner} for the direct (non-Worker) execution path
50
- */
51
- export declare function runInWorker<I extends Record<string, unknown>, R>(params: RunInWorkerParams<I>): Promise<R>;
@@ -1,125 +0,0 @@
1
- import path from 'node:path';
2
- import { Worker } from 'node:worker_threads';
3
- import { runner } from './runner.js';
4
- const __filename = new URL(import.meta.url).pathname;
5
- const __dirname = path.dirname(__filename);
6
- /** Resolved path to the compiled Worker thread entry point ({@link ./worker.ts}). */
7
- const workerPath = path.resolve(__dirname, 'worker.js');
8
- /**
9
- * Feature flag controlling whether plugin execution uses Worker threads.
10
- * When `true` (default), each plugin invocation runs in an isolated Worker,
11
- * providing memory isolation and crash protection. When `false`, the runner
12
- * executes directly in the main thread (useful for debugging).
13
- */
14
- const useWorker = true;
15
- /**
16
- * Spawns a Worker thread to execute a plugin module and returns its result.
17
- *
18
- * This is the bridge between the main thread's `deal()` parallelism and the
19
- * per-page Worker execution. Each call creates a new Worker, passes the
20
- * initial data via `workerData`, and listens for messages until the Worker
21
- * signals completion.
22
- *
23
- * ## Why Worker threads?
24
- *
25
- * DOM-heavy plugins (JSDOM + axe-core, markuplint, etc.) allocate significant
26
- * memory per page. Running them in Workers ensures:
27
- * - **Memory isolation**: JSDOM windows are fully GC'd when the Worker exits
28
- * - **Crash containment**: A plugin segfault/OOM kills only the Worker, not the process
29
- * - **Signal handling**: Graceful cleanup on SIGABRT, SIGQUIT, and other signals
30
- *
31
- * ## Message protocol
32
- *
33
- * The Worker sends two types of messages:
34
- * - `{ type: 'url', url: string }` - URL discovery notification, forwarded to the emitter
35
- * - `{ type: 'finish', result: R }` - Execution complete, resolves the Promise
36
- *
37
- * ## Fallback mode
38
- *
39
- * When `useWorker` is `false`, execution delegates directly to
40
- * {@link ./runner.ts!runner} in the main thread.
41
- * @template I - Shape of the additional data merged into `workerData`.
42
- * @template R - Return type expected from the Worker module.
43
- * @param params - Parameters containing file path, progress info, emitter, and data.
44
- * @returns The result produced by the Worker module's default export.
45
- * @see {@link ./worker.ts} for the Worker-side entry point
46
- * @see {@link ./runner.ts!runner} for the direct (non-Worker) execution path
47
- */
48
- export function runInWorker(params) {
49
- const { filePath, num, total, emitter, initialData } = params;
50
- if (useWorker) {
51
- const worker = new Worker(workerPath, {
52
- workerData: {
53
- filePath,
54
- num,
55
- total,
56
- ...initialData,
57
- },
58
- });
59
- return new Promise((resolve, reject) => {
60
- const killWorker = async (sig) => {
61
- await worker.terminate();
62
- worker.unref();
63
- worker.removeAllListeners();
64
- process.removeListener('SIGABRT', killWorker);
65
- process.removeListener('SIGLOST', killWorker);
66
- process.removeListener('SIGQUIT', killWorker);
67
- process.removeListener('disconnect', killWorker);
68
- process.removeListener('exit', killWorker);
69
- process.removeListener('uncaughtException', killWorker);
70
- process.removeListener('uncaughtExceptionMonitor', killWorker);
71
- process.removeListener('unhandledRejection', killWorker);
72
- // eslint-disable-next-line no-console
73
- console.log(`Kill Worker cause: %O`, sig);
74
- reject(`SIG: ${sig}`);
75
- };
76
- // Changed from old issue
77
- // @see https://github.com/nodejs/node-v0.x-archive/issues/6339
78
- // process.once('SIGKILL', killWorker);
79
- // process.once('SIGSTOP', killWorker);
80
- process.once('SIGABRT', killWorker);
81
- process.once('SIGLOST', killWorker);
82
- process.once('SIGQUIT', killWorker);
83
- process.once('disconnect', killWorker);
84
- process.once('exit', killWorker);
85
- process.once('uncaughtException', killWorker);
86
- process.once('uncaughtExceptionMonitor', killWorker);
87
- process.once('unhandledRejection', killWorker);
88
- worker.once('error', killWorker);
89
- worker.once('messageerror', killWorker);
90
- worker.on('message', async (message) => {
91
- if (!message) {
92
- return;
93
- }
94
- if (message.type === 'url') {
95
- void emitter.emit('url', message.url);
96
- }
97
- if (message.type === 'finish') {
98
- await worker.terminate();
99
- worker.removeAllListeners();
100
- worker.unref();
101
- process.removeListener('SIGABRT', killWorker);
102
- process.removeListener('SIGLOST', killWorker);
103
- process.removeListener('SIGQUIT', killWorker);
104
- process.removeListener('disconnect', killWorker);
105
- process.removeListener('exit', killWorker);
106
- process.removeListener('uncaughtException', killWorker);
107
- process.removeListener('uncaughtExceptionMonitor', killWorker);
108
- process.removeListener('unhandledRejection', killWorker);
109
- if (message.error) {
110
- reject(new Error(message.error));
111
- }
112
- else {
113
- resolve(message.result);
114
- }
115
- }
116
- });
117
- });
118
- }
119
- return runner({
120
- filePath,
121
- num,
122
- total,
123
- ...initialData,
124
- }, emitter);
125
- }