forked from vercel/next.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
worker.ts
97 lines (83 loc) · 2.59 KB
/
worker.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import { Worker as JestWorker } from 'next/dist/compiled/jest-worker'
type FarmOptions = ConstructorParameters<typeof JestWorker>[1]
const RESTARTED = Symbol('restarted')
export class Worker {
private _worker: JestWorker | undefined
constructor(
workerPath: string,
options: FarmOptions & {
timeout?: number
onRestart?: (method: string, args: any[], attempts: number) => void
exposedMethods: ReadonlyArray<string>
}
) {
let { timeout, onRestart, ...farmOptions } = options
let restartPromise: Promise<typeof RESTARTED>
let resolveRestartPromise: (arg: typeof RESTARTED) => void
let activeTasks = 0
this._worker = undefined
const createWorker = () => {
this._worker = new JestWorker(workerPath, farmOptions) as JestWorker
restartPromise = new Promise(
(resolve) => (resolveRestartPromise = resolve)
)
this._worker.getStdout().pipe(process.stdout)
this._worker.getStderr().pipe(process.stderr)
}
createWorker()
const onHanging = () => {
const worker = this._worker
if (!worker) return
const resolve = resolveRestartPromise
createWorker()
worker.end().then(() => {
resolve(RESTARTED)
})
}
let hangingTimer: number | false = false
const onActivity = () => {
if (hangingTimer) clearTimeout(hangingTimer)
hangingTimer = activeTasks > 0 && setTimeout(onHanging, timeout)
}
for (const method of farmOptions.exposedMethods) {
if (method.startsWith('_')) continue
;(this as any)[method] = timeout
? // eslint-disable-next-line no-loop-func
async (...args: any[]) => {
activeTasks++
try {
let attempts = 0
for (;;) {
onActivity()
const result = await Promise.race([
(this._worker as any)[method](...args),
restartPromise,
])
if (result !== RESTARTED) return result
if (onRestart) onRestart(method, args, ++attempts)
}
} finally {
activeTasks--
onActivity()
}
}
: (this._worker as any)[method].bind(this._worker)
}
}
end(): ReturnType<JestWorker['end']> {
const worker = this._worker
if (!worker) {
throw new Error('Farm is ended, no more calls can be done to it')
}
this._worker = undefined
return worker.end()
}
/**
* Quietly end the worker if it exists
*/
close(): void {
if (this._worker) {
this._worker.end()
}
}
}