From a48c61be4dd0c2045ca720dacd03c24f53490393 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Wed, 10 May 2023 06:49:44 +0200 Subject: [PATCH] Properly quit on uncaught exceptions --- cli/run/watch-cli.ts | 9 +++++++-- .../samples/handles-uncaught-errors/_config.js | 10 ++++++++++ test/cli/samples/handles-uncaught-errors/main.js | 1 + .../handles-uncaught-errors/rollup.config.js | 16 ++++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 test/cli/samples/handles-uncaught-errors/_config.js create mode 100644 test/cli/samples/handles-uncaught-errors/main.js create mode 100644 test/cli/samples/handles-uncaught-errors/rollup.config.js diff --git a/cli/run/watch-cli.ts b/cli/run/watch-cli.ts index 5a58e28c12c..987c2f21bdf 100644 --- a/cli/run/watch-cli.ts +++ b/cli/run/watch-cli.ts @@ -29,7 +29,7 @@ export async function watch(command: Record): Promise { const runWatchHook = createWatchHooks(command); onExit(close); - process.on('uncaughtException', close); + process.on('uncaughtException', closeWithError); if (!process.stdin.isTTY) { process.stdin.on('end', close); process.stdin.resume(); @@ -147,7 +147,7 @@ export async function watch(command: Record): Promise { } async function close(code: number | null | undefined): Promise { - process.removeListener('uncaughtException', close); + process.removeListener('uncaughtException', closeWithError); // removing a non-existent listener is a no-op process.stdin.removeListener('end', close); @@ -160,3 +160,8 @@ export async function watch(command: Record): Promise { // return a promise that never resolves to keep the process running return new Promise(() => {}); } + +function closeWithError(error: Error): void { + error.name = `Uncaught ${error.name}`; + handleError(error); +} diff --git a/test/cli/samples/handles-uncaught-errors/_config.js b/test/cli/samples/handles-uncaught-errors/_config.js new file mode 100644 index 00000000000..5f6ead08a5d --- /dev/null +++ b/test/cli/samples/handles-uncaught-errors/_config.js @@ -0,0 +1,10 @@ +const { assertIncludes } = require('../../../utils.js'); + +module.exports = defineTest({ + description: 'handles uncaught errors', + command: 'rollup --config rollup.config.js', + error: () => true, + stderr(stderr) { + assertIncludes(stderr, 'TypeError: foo'); + } +}); diff --git a/test/cli/samples/handles-uncaught-errors/main.js b/test/cli/samples/handles-uncaught-errors/main.js new file mode 100644 index 00000000000..fd53873812e --- /dev/null +++ b/test/cli/samples/handles-uncaught-errors/main.js @@ -0,0 +1 @@ +assert.equal( 42, 42 ); diff --git a/test/cli/samples/handles-uncaught-errors/rollup.config.js b/test/cli/samples/handles-uncaught-errors/rollup.config.js new file mode 100644 index 00000000000..b1370c7df91 --- /dev/null +++ b/test/cli/samples/handles-uncaught-errors/rollup.config.js @@ -0,0 +1,16 @@ +module.exports = { + input: 'main.js', + output: { + format: 'cjs' + }, + plugins: [ + { + name: 'test', + buildStart() { + Promise.resolve().then(() => { + throw new TypeError('foo'); + }); + } + } + ] +};