esm: make `process.exit()` default to exit code 0

Due to a bug in top-level await implementation, it used to default to
exit code 13.

PR-URL: https://github.com/nodejs/node/pull/41388
Fixes: https://github.com/nodejs/node/issues/40808
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Guy Bedford <guybedford@gmail.com>
This commit is contained in:
Gang Chen 2022-01-14 08:37:41 +08:00 committed by GitHub
parent 73434330b8
commit 775bfd1579
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 51 additions and 9 deletions

View File

@ -0,0 +1,12 @@
'use strict';
// Handle a Promise from running code that potentially does Top-Level Await.
// In that case, it makes sense to set the exit code to a specific non-zero
// value if the main code never finishes running.
function handleProcessExit() {
process.exitCode ??= 13;
}
module.exports = {
handleProcessExit,
};

View File

@ -8,6 +8,9 @@ const CJSLoader = require('internal/modules/cjs/loader');
const { Module, toRealPath, readPackageScope } = CJSLoader;
const { getOptionValue } = require('internal/options');
const path = require('path');
const {
handleProcessExit,
} = require('internal/modules/esm/handle_process_exit');
function resolveMainPath(main) {
// Note extension resolution for the main entry point can be deprecated in a
@ -53,18 +56,11 @@ function runMainESM(mainPath) {
}
async function handleMainPromise(promise) {
// Handle a Promise from running code that potentially does Top-Level Await.
// In that case, it makes sense to set the exit code to a specific non-zero
// value if the main code never finishes running.
function handler() {
if (process.exitCode === undefined)
process.exitCode = 13;
}
process.on('exit', handler);
process.on('exit', handleProcessExit);
try {
return await promise;
} finally {
process.off('exit', handler);
process.off('exit', handleProcessExit);
}
}

View File

@ -48,6 +48,10 @@ const {
} = require('internal/validators');
const constants = internalBinding('constants').os.signals;
const {
handleProcessExit,
} = require('internal/modules/esm/handle_process_exit');
const kInternal = Symbol('internal properties');
function assert(x, msg) {
@ -175,6 +179,8 @@ function wrapProcessMethods(binding) {
memoryUsage.rss = rss;
function exit(code) {
process.off('exit', handleProcessExit);
if (code || code === 0)
process.exitCode = code;

View File

@ -80,3 +80,21 @@ import fixtures from '../common/fixtures.js';
assert.deepStrictEqual([status, stdout], [1, '']);
assert.match(stderr, /Error: Xyz/);
}
{
// Calling process.exit() in .mjs should return status 0
const { status, stdout, stderr } = child_process.spawnSync(
process.execPath,
[fixtures.path('es-modules/tla/process-exit.mjs')],
{ encoding: 'utf8' });
assert.deepStrictEqual([status, stdout, stderr], [0, '', '']);
}
{
// Calling process.exit() in worker thread shouldn't influence main thread
const { status, stdout, stderr } = child_process.spawnSync(
process.execPath,
[fixtures.path('es-modules/tla/unresolved-with-worker-process-exit.mjs')],
{ encoding: 'utf8' });
assert.deepStrictEqual([status, stdout, stderr], [13, '', '']);
}

View File

@ -0,0 +1 @@
process.exit();

View File

@ -0,0 +1,8 @@
import { Worker, isMainThread } from 'worker_threads';
if (isMainThread) {
new Worker(new URL(import.meta.url));
await new Promise(() => {});
} else {
process.exit();
}

View File

@ -79,6 +79,7 @@ const expectedModules = new Set([
'NativeModule internal/modules/esm/resolve',
'NativeModule internal/modules/esm/initialize_import_meta',
'NativeModule internal/modules/esm/translators',
'NativeModule internal/modules/esm/handle_process_exit',
'NativeModule internal/process/esm_loader',
'NativeModule internal/options',
'NativeModule internal/perf/event_loop_delay',