Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion lib/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2488,6 +2488,7 @@ function appendFileSync(path, data, options) {
* recursive?: boolean;
* encoding?: string;
* signal?: AbortSignal;
* throwIfNoEntry?: boolean;
* }} [options]
* @param {(
* eventType?: string,
Expand All @@ -2506,6 +2507,7 @@ function watch(filename, options, listener) {

if (options.persistent === undefined) options.persistent = true;
if (options.recursive === undefined) options.recursive = false;
if (options.throwIfNoEntry === undefined) options.throwIfNoEntry = true;

let watcher;
const watchers = require('internal/fs/watchers');
Expand All @@ -2523,7 +2525,8 @@ function watch(filename, options, listener) {
options.persistent,
options.recursive,
options.encoding,
options.ignore);
options.ignore,
options.throwIfNoEntry);
}

if (listener) {
Expand Down
10 changes: 7 additions & 3 deletions lib/internal/fs/recursive_watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class FSWatcher extends EventEmitter {

assert(typeof options === 'object');

const { persistent, recursive, signal, encoding, ignore } = options;
const { persistent, recursive, signal, encoding, ignore, throwIfNoEntry } = options;

// TODO(anonrig): Add non-recursive support to non-native-watcher for IBMi & AIX support.
if (recursive != null) {
Expand All @@ -66,6 +66,10 @@ class FSWatcher extends EventEmitter {
validateAbortSignal(signal, 'options.signal');
}

if (throwIfNoEntry != null) {
validateBoolean(throwIfNoEntry, 'options.throwIfNoEntry');
}
Comment thread
anonrig marked this conversation as resolved.

if (encoding != null) {
// This is required since on macOS and Windows it throws ERR_INVALID_ARG_VALUE
if (typeof encoding !== 'string') {
Expand All @@ -76,7 +80,7 @@ class FSWatcher extends EventEmitter {
validateIgnoreOption(ignore, 'options.ignore');
this.#ignoreMatcher = createIgnoreMatcher(ignore);

this.#options = { persistent, recursive, signal, encoding };
this.#options = { persistent, recursive, signal, encoding, throwIfNoEntry };
}

close() {
Expand Down Expand Up @@ -222,7 +226,7 @@ class FSWatcher extends EventEmitter {
this.#watchFolder(filename);
}
} catch (error) {
if (error.code === 'ENOENT') {
if (this.#options.throwIfNoEntry !== false && error.code === 'ENOENT') {
Comment thread
efekrskl marked this conversation as resolved.
Outdated
error.filename = filename;
throw error;
}
Expand Down
9 changes: 7 additions & 2 deletions lib/internal/fs/watchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const {
} = internalBinding('fs');

const { FSEvent } = internalBinding('fs_event_wrap');
const { UV_ENOSPC } = internalBinding('uv');
const { UV_ENOSPC, UV_ENOENT } = internalBinding('uv');
const { EventEmitter } = require('events');

const {
Expand Down Expand Up @@ -293,7 +293,8 @@ FSWatcher.prototype[kFSWatchStart] = function(filename,
persistent,
recursive,
encoding,
ignore) {
ignore,
throwIfNoEntry = true) {
if (this._handle === null) { // closed
return;
}
Expand All @@ -313,6 +314,10 @@ FSWatcher.prototype[kFSWatchStart] = function(filename,
recursive,
encoding);
if (err) {
if (!throwIfNoEntry && err === UV_ENOENT) {
return;
}

const error = new UVException({
errno: err,
syscall: 'watch',
Expand Down
11 changes: 7 additions & 4 deletions lib/internal/main/watch_mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,8 @@

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);
Expand Down Expand Up @@ -105,6 +103,9 @@
if (kEnvFiles.length > 0) {
ArrayPrototypeForEach(kEnvFiles, (file) => watcher.filterFile(resolve(file)));
}
if (kOptionalEnvFiles.length > 0) {
ArrayPrototypeForEach(kOptionalEnvFiles, (file) => watcher.filterFile(resolve(file), undefined, true));

Check failure on line 107 in lib/internal/main/watch_mode.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

Multiple spaces found before 'watcher'
Comment thread
efekrskl marked this conversation as resolved.
Outdated
}
child.once('exit', (code) => {
exited = true;
const waitingForChanges = 'Waiting for file changes before restarting...';
Expand Down Expand Up @@ -160,6 +161,7 @@
}

let restarting = false;

async function restart(child) {
if (restarting) return;
restarting = true;
Expand Down Expand Up @@ -198,5 +200,6 @@
process.exit(exitCode ?? kNoFailure);
};
}

process.on('SIGTERM', signalHandler('SIGTERM'));
process.on('SIGINT', signalHandler('SIGINT'));
10 changes: 5 additions & 5 deletions lib/internal/watch_mode/files_watcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,11 @@
return [...this.#watchers.keys()];
}

watchPath(path, recursive = true) {
watchPath(path, recursive = true, allowMissing = false) {
if (this.#isPathWatched(path)) {
return;
}
const watcher = watch(path, { recursive, signal: this.#signal });
const watcher = watch(path, { recursive, signal: this.#signal, throwIfNoEntry: !allowMissing });
watcher.on('change', (eventType, fileName) => {
// `fileName` can be `null` if it cannot be determined. See
// https://github.com/nodejs/node/pull/49891#issuecomment-1744673430.
Expand All @@ -126,14 +126,14 @@
}
}

filterFile(file, owner) {
filterFile(file, owner, allowMissing = false) {
if (!file) return;
if (supportsRecursiveWatching) {
this.watchPath(dirname(file));
this.watchPath(dirname(file),true, allowMissing);

Check failure on line 132 in lib/internal/watch_mode/files_watcher.js

View workflow job for this annotation

GitHub Actions / lint-js-and-md

A space is required after ','
} else {
// Having multiple FSWatcher's seems to be slower
// than a single recursive FSWatcher
this.watchPath(file, false);
this.watchPath(file, false, allowMissing);
}
this.#filteredFiles.add(file);
if (owner) {
Expand Down
21 changes: 21 additions & 0 deletions test/sequential/test-watch-mode.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
Loading