-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
hookActions.ts
63 lines (59 loc) · 1.91 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
58
59
60
61
62
63
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;
}
let handleBeforeExit: null | (() => void) = null;
const rejectByPluginDriver = new Map<PluginDriver, (reason: Error) => void>();
export async function catchUnfinishedHookActions<T>(
pluginDriver: PluginDriver,
callback: () => Promise<T>
): Promise<T> {
const emptyEventLoopPromise = new Promise<T>((_, reject) => {
rejectByPluginDriver.set(pluginDriver, reject);
if (!handleBeforeExit) {
// We only ever create a single event listener to avoid max listener and
// other issues
handleBeforeExit = () => {
for (const [pluginDriver, reject] of rejectByPluginDriver) {
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')
)
);
}
};
process.once('beforeExit', handleBeforeExit);
}
});
try {
return await Promise.race([callback(), emptyEventLoopPromise]);
} finally {
rejectByPluginDriver.delete(pluginDriver);
if (rejectByPluginDriver.size === 0) {
process.off('beforeExit', handleBeforeExit!);
handleBeforeExit = null;
}
}
}