diff --git a/lib/internal/main/watch_mode.js b/lib/internal/main/watch_mode.js index 06c2c8602da444..5f9608dcb14971 100644 --- a/lib/internal/main/watch_mode.js +++ b/lib/internal/main/watch_mode.js @@ -34,10 +34,8 @@ markBootstrapComplete(); const kKillSignal = convertToValidSignal(getOptionValue('--watch-kill-signal')); const kShouldFilterModules = getOptionValue('--watch-path').length === 0; -const kEnvFiles = [ - ...getOptionValue('--env-file'), - ...getOptionValue('--env-file-if-exists'), -]; +const kEnvFiles = getOptionValue('--env-file'); +const kOptionalEnvFiles = getOptionValue('--env-file-if-exists'); const kWatchedPaths = ArrayPrototypeMap(getOptionValue('--watch-path'), (path) => resolve(path)); const kPreserveOutput = getOptionValue('--watch-preserve-output'); const kCommand = ArrayPrototypeSlice(process.argv, 1); @@ -105,6 +103,18 @@ function start() { if (kEnvFiles.length > 0) { ArrayPrototypeForEach(kEnvFiles, (file) => watcher.filterFile(resolve(file))); } + if (kOptionalEnvFiles.length > 0) { + ArrayPrototypeForEach(kOptionalEnvFiles, (file) => { + try { + watcher.filterFile(resolve(file)); + } catch (error) { + if (error?.code !== 'ENOENT' && error?.code !== 'ENOTDIR') { + throw error; + } + // Failed watching the file, ignore + } + }); + } child.once('exit', (code) => { exited = true; const waitingForChanges = 'Waiting for file changes before restarting...'; @@ -160,6 +170,7 @@ async function stop(child) { } let restarting = false; + async function restart(child) { if (restarting) return; restarting = true; @@ -198,5 +209,6 @@ function signalHandler(signal) { process.exit(exitCode ?? kNoFailure); }; } + process.on('SIGTERM', signalHandler('SIGTERM')); process.on('SIGINT', signalHandler('SIGINT')); diff --git a/test/sequential/test-watch-mode.mjs b/test/sequential/test-watch-mode.mjs index a5cac129ad1c21..39ad7ef72ada00 100644 --- a/test/sequential/test-watch-mode.mjs +++ b/test/sequential/test-watch-mode.mjs @@ -277,6 +277,27 @@ describe('watch mode', { concurrency: !process.env.TEST_PARALLEL, timeout: 60_00 } }); + it('should not crash when --env-file-if-exists points to a missing file', async () => { + const envKey = `TEST_ENV_${Date.now()}`; + const jsFile = createTmpFile(`console.log('ENV: ' + process.env.${envKey});`); + const missingEnvFile = path.join(tmpdir.path, `missing-${Date.now()}.env`); + const { done, restart } = runInBackground({ + args: ['--watch-path', tmpdir.path, `--env-file-if-exists=${missingEnvFile}`, jsFile], + }); + + try { + const { stderr, stdout } = await restart(); + + assert.doesNotMatch(stderr, /ENOENT: no such file or directory, watch/); + assert.deepStrictEqual(stdout, [ + 'ENV: undefined', + `Completed running ${inspect(jsFile)}. Waiting for file changes before restarting...`, + ]); + } finally { + await done(); + } + }); + it('should watch changes to a failing file', async () => { const file = createTmpFile('throw new Error("fails");'); const { stderr, stdout } = await runWriteSucceed({