-
-
Notifications
You must be signed in to change notification settings - Fork 140
/
register.ts
96 lines (79 loc) · 2.33 KB
/
register.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
import module from 'node:module';
import { MessageChannel, type MessagePort } from 'node:worker_threads';
import type { Message } from '../types.js';
import { createScopedImport, type ScopedImport } from './scoped-import.js';
export type InitializationOptions = {
namespace?: string;
port?: MessagePort;
};
export type RegisterOptions = {
namespace?: string;
onImport?: (url: string) => void;
};
export type Unregister = () => Promise<void>;
export type NamespacedUnregister = Unregister & {
import: ScopedImport;
unregister: Unregister;
};
type RequiredProperty<Type, Keys extends keyof Type> = Type & { [P in Keys]-?: Type[P] };
export type Register = {
(options: RequiredProperty<RegisterOptions, 'namespace'>): NamespacedUnregister;
(options?: RegisterOptions): Unregister;
};
export const register: Register = (
options,
) => {
if (!module.register) {
throw new Error(`This version of Node.js (${process.version}) does not support module.register(). Please upgrade to Node v18.9 or v20.6 and above.`);
}
const { sourceMapsEnabled } = process;
process.setSourceMapsEnabled(true);
const { port1, port2 } = new MessageChannel();
module.register(
// Load new copy of loader so it can be registered multiple times
`./esm/index.mjs?${Date.now()}`,
{
parentURL: import.meta.url,
data: {
namespace: options?.namespace,
port: port2,
} satisfies InitializationOptions,
transferList: [port2],
},
);
const onImport = options?.onImport;
const importHandler = onImport && ((message: Message) => {
if (message.type === 'load') {
onImport(message.url);
}
});
if (importHandler) {
port1.on('message', importHandler);
port1.unref();
}
// unregister
const unregister = () => {
if (sourceMapsEnabled === false) {
process.setSourceMapsEnabled(false);
}
if (importHandler) {
port1.off('message', importHandler);
}
port1.postMessage('deactivate');
// Not necessary to wait, but provide the option
return new Promise<void>((resolve) => {
const onDeactivated = (message: Message) => {
if (message.type === 'deactivated') {
resolve();
port1.off('message', onDeactivated);
}
};
port1.on('message', onDeactivated);
});
};
if (options?.namespace) {
unregister.import = createScopedImport(options.namespace);
unregister.unregister = unregister;
}
return unregister;
};