f9795fdd26
#### improve export spinner update at least once a minute in non-tty update progress regularly when using the spinner decrease frequency of the spinner (windows console output is expensive) #### restart static page generation and collecting page data worker pools when hanging when for 1 minute no activity happens on the worker pool, restart it log a warning for hanging jobs #### add page generation duration to summary tree ![image](https://user-images.githubusercontent.com/1365881/125750454-8845f1b1-faf0-4598-b7a4-ea796b884691.png) for `[+n more pages]` is will show `(avg 321 ms)` when the average is over the threshold. It will allocate 8 lines for preview pages (instead of 4) when they contain slow pages ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [x] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes
88 lines
2.4 KiB
TypeScript
88 lines
2.4 KiB
TypeScript
import { Worker as JestWorker } from '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()
|
|
}
|
|
}
|