-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
hookActions.ts
57 lines (53 loc) · 1.88 KB
/
hookActions.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
import { EventEmitter } from 'node:events';
import process from 'node:process';
import type { HookAction, PluginDriver } from './PluginDriver';
function formatAction([pluginName, hookName, parameters]: HookAction): string {
const action = `(${pluginName}) ${hookName}`;
const s = JSON.stringify;
switch (hookName) {
case 'resolveId': {
return `${action} ${s(parameters[0])} ${s(parameters[1])}`;
}
case 'load': {
return `${action} ${s(parameters[0])}`;
}
case 'transform': {
return `${action} ${s(parameters[1])}`;
}
case 'shouldTransformCachedModule': {
return `${action} ${s((parameters[0] as { id: string }).id)}`;
}
case 'moduleParsed': {
return `${action} ${s((parameters[0] as { id: string }).id)}`;
}
}
return action;
}
// We do not directly listen on process to avoid max listeners warnings for
// complicated build processes
const beforeExitEvent = 'beforeExit';
// eslint-disable-next-line unicorn/prefer-event-target
const beforeExitEmitter = new EventEmitter();
beforeExitEmitter.setMaxListeners(0);
process.on(beforeExitEvent, () => beforeExitEmitter.emit(beforeExitEvent));
export async function catchUnfinishedHookActions<T>(
pluginDriver: PluginDriver,
callback: () => Promise<T>
): Promise<T> {
let handleEmptyEventLoop: () => void;
const emptyEventLoopPromise = new Promise<T>((_, reject) => {
handleEmptyEventLoop = () => {
const unfulfilledActions = pluginDriver.getUnfulfilledHookActions();
reject(
new Error(
`Unexpected early exit. This happens when Promises returned by plugins cannot resolve. Unfinished hook action(s) on exit:\n` +
[...unfulfilledActions].map(formatAction).join('\n')
)
);
};
beforeExitEmitter.once(beforeExitEvent, handleEmptyEventLoop);
});
const result = await Promise.race([callback(), emptyEventLoopPromise]);
beforeExitEmitter.off(beforeExitEvent, handleEmptyEventLoop!);
return result;
}