Skip to content

Commit 55f70f6

Browse files
aduh95targos
authored andcommitted
esm: propagate process.exit from the loader thread to the main thread
PR-URL: #47548 Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Jacob Smith <jacob@frende.me>
1 parent bdd02a4 commit 55f70f6

3 files changed

Lines changed: 65 additions & 0 deletions

File tree

‎lib/internal/modules/esm/hooks.js‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ class HooksProxy {
504504
},
505505
});
506506
this.#worker.unref();// ! Allows the process to eventually exit.
507+
this.#worker.on('exit',process.exit);
507508
}
508509

509510
#waitForWorker(){
@@ -513,6 +514,7 @@ class HooksProxy {
513514
debug('wait for signal from worker');
514515
AtomicsWait(this.#lock,WORKER_TO_MAIN_THREAD_NOTIFICATION,0);
515516
constresponse=this.#worker.receiveMessageSync();
517+
if(response.message.status==='exit'){return;}
516518
const{ preloadScripts }=this.#unwrapMessage(response);
517519
this.#executePreloadScripts(preloadScripts);
518520
}
@@ -593,6 +595,8 @@ class HooksProxy {
593595
debug('got sync response from worker',{ method, args });
594596
if(response.message.status==='never-settle'){
595597
process.exit(kUnfinishedTopLevelAwait);
598+
}elseif(response.message.status==='exit'){
599+
process.exit(response.message.body);
596600
}
597601
returnthis.#unwrapMessage(response);
598602
}

‎lib/internal/modules/esm/worker.js‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,19 @@ async function customizedModuleWorker(lock, syncCommPort, errorHandler) {
6666
lethooks,preloadScripts,initializationError;
6767
lethasInitializationError=false;
6868

69+
{
70+
// If a custom hook is calling `process.exit`, we should wake up the main thread
71+
// so it can detect the exit event.
72+
const{ exit }=process;
73+
process.exit=function(code){
74+
syncCommPort.postMessage(wrapMessage('exit',code??process.exitCode));
75+
AtomicsAdd(lock,WORKER_TO_MAIN_THREAD_NOTIFICATION,1);
76+
AtomicsNotify(lock,WORKER_TO_MAIN_THREAD_NOTIFICATION);
77+
returnReflectApply(exit,this,arguments);
78+
};
79+
}
80+
81+
6982
try{
7083
initializeESM();
7184
constinitResult=awaitinitializeHooks();

‎test/es-module/test-esm-loader-hooks.mjs‎

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,52 @@ describe('Loader hooks', { concurrency: true }, () => {
195195
assert.strictEqual(code,0);
196196
assert.strictEqual(signal,null);
197197
});
198+
199+
it('should be fine to call `process.exit` from a custom async hook',async()=>{
200+
const{ code, signal, stdout, stderr }=awaitspawnPromisified(execPath,[
201+
'--no-warnings',
202+
'--experimental-import-meta-resolve',
203+
'--experimental-loader',
204+
'data:text/javascript,export function load(a,b,next){if(a==="data:exit")process.exit(42);return next(a,b)}',
205+
'--input-type=module',
206+
'--eval',
207+
'import "data:exit"',
208+
]);
209+
210+
assert.strictEqual(stderr,'');
211+
assert.strictEqual(stdout,'');
212+
assert.strictEqual(code,42);
213+
assert.strictEqual(signal,null);
214+
});
215+
216+
it('should be fine to call `process.exit` from a custom sync hook',async()=>{
217+
const{ code, signal, stdout, stderr }=awaitspawnPromisified(execPath,[
218+
'--no-warnings',
219+
'--experimental-import-meta-resolve',
220+
'--experimental-loader',
221+
'data:text/javascript,export function resolve(a,b,next){if(a==="exit:")process.exit(42);return next(a,b)}',
222+
'--input-type=module',
223+
'--eval',
224+
'import "data:text/javascript,import.meta.resolve(%22exit:%22)"',
225+
]);
226+
227+
assert.strictEqual(stderr,'');
228+
assert.strictEqual(stdout,'');
229+
assert.strictEqual(code,42);
230+
assert.strictEqual(signal,null);
231+
});
232+
233+
it('should be fine to call `process.exit` from the loader thread top-level',async()=>{
234+
const{ code, signal, stdout, stderr }=awaitspawnPromisified(execPath,[
235+
'--no-warnings',
236+
'--experimental-loader',
237+
'data:text/javascript,process.exit(42)',
238+
fixtures.path('empty.js'),
239+
]);
240+
241+
assert.strictEqual(stderr,'');
242+
assert.strictEqual(stdout,'');
243+
assert.strictEqual(code,42);
244+
assert.strictEqual(signal,null);
245+
});
198246
});

0 commit comments

Comments
 (0)