diff --git a/doc/api/cli.md b/doc/api/cli.md index af1feb9ac10bc5..819c70124f2ad0 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1624,10 +1624,6 @@ This flag cannot be combined with $ node --watch-path=./src --watch-path=./tests index.js ``` -This option is only supported on macOS and Windows. -An `ERR_FEATURE_UNAVAILABLE_ON_PLATFORM` exception will be thrown -when the option is used on a platform that does not support it. - ### `--zero-fill-buffers` - -Used when a feature that is not available -to the current platform which is running Node.js is used. - ### `ERR_FS_CP_DIR_TO_NON_DIR` diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 5e08c1b1b3a7db..8e335156715015 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -1025,10 +1025,6 @@ E('ERR_FALSY_VALUE_REJECTION', function(reason) { this.reason = reason; return 'Promise was rejected with falsy value'; }, Error); -E('ERR_FEATURE_UNAVAILABLE_ON_PLATFORM', - 'The feature %s is unavailable on the current platform' + - ', which is being used to run Node.js', - TypeError); E('ERR_FS_CP_DIR_TO_NON_DIR', 'Cannot overwrite directory with non-directory', SystemError); E('ERR_FS_CP_EEXIST', 'Target already exists', SystemError); diff --git a/lib/internal/watch_mode/files_watcher.js b/lib/internal/watch_mode/files_watcher.js index 3c756c4b5d77c9..007c5f41d693fe 100644 --- a/lib/internal/watch_mode/files_watcher.js +++ b/lib/internal/watch_mode/files_watcher.js @@ -18,10 +18,6 @@ const { fileURLToPath } = require('url'); const { resolve, dirname } = require('path'); const { setTimeout } = require('timers'); - -const supportsRecursiveWatching = process.platform === 'win32' || - process.platform === 'darwin'; - class FilesWatcher extends EventEmitter { #watchers = new SafeMap(); #filteredFiles = new SafeSet(); @@ -45,8 +41,8 @@ class FilesWatcher extends EventEmitter { return true; } - for (const { 0: watchedPath, 1: watcher } of this.#watchers.entries()) { - if (watcher.recursive && StringPrototypeStartsWith(path, watchedPath)) { + for (const watchedPath of this.#watchers.keys()) { + if (StringPrototypeStartsWith(path, watchedPath)) { return true; } } @@ -85,28 +81,19 @@ class FilesWatcher extends EventEmitter { return [...this.#watchers.keys()]; } - watchPath(path, recursive = true) { + watchPath(path) { if (this.#isPathWatched(path)) { return; } - const watcher = watch(path, { recursive }); - watcher.on('change', (eventType, fileName) => this - .#onChange(recursive ? resolve(path, fileName) : path)); - this.#watchers.set(path, { handle: watcher, recursive }); - if (recursive) { - this.#removeWatchedChildren(path); - } + const watcher = watch(path, { recursive: true }); + watcher.on('change', (eventType, fileName) => this.#onChange(resolve(path, fileName))); + this.#watchers.set(path, { handle: watcher }); + this.#removeWatchedChildren(path); } filterFile(file, owner) { if (!file) return; - if (supportsRecursiveWatching) { - this.watchPath(dirname(file)); - } else { - // Having multiple FSWatcher's seems to be slower - // than a single recursive FSWatcher - this.watchPath(file, false); - } + this.watchPath(dirname(file)); this.#filteredFiles.add(file); if (owner) { const owners = this.#depencencyOwners.get(file) ?? new SafeSet(); diff --git a/test/parallel/test-watch-mode-files_watcher.mjs b/test/parallel/test-watch-mode-files_watcher.mjs index 4c507d61e00c7f..0e532ffe6ea6f7 100644 --- a/test/parallel/test-watch-mode-files_watcher.mjs +++ b/test/parallel/test-watch-mode-files_watcher.mjs @@ -15,8 +15,6 @@ import watcher from 'internal/watch_mode/files_watcher'; if (common.isIBMi) common.skip('IBMi does not support `fs.watch()`'); -const supportsRecursiveWatching = common.isOSX || common.isWindows; - const { FilesWatcher } = watcher; tmpdir.refresh(); @@ -70,14 +68,13 @@ describe('watch mode file watcher', () => { assert.ok(changesCount < 5); }); - it('should ignore files in watched directory if they are not filtered', - { skip: !supportsRecursiveWatching }, async () => { - watcher.on('changed', common.mustNotCall()); - watcher.watchPath(tmpdir.path); - writeFileSync(path.join(tmpdir.path, 'file3'), '1'); - // Wait for this long to make sure changes are not triggered - await setTimeout(1000); - }); + it('should ignore files in watched directory if they are not filtered', async () => { + watcher.on('changed', common.mustNotCall()); + watcher.watchPath(tmpdir.path); + writeFileSync(path.join(tmpdir.path, 'file3'), '1'); + // Wait for this long to make sure changes are not triggered + await setTimeout(1000); + }); it('should allow clearing filters', async () => { const file = path.join(tmpdir.path, 'file4'); @@ -95,57 +92,52 @@ describe('watch mode file watcher', () => { assert.strictEqual(changesCount, 1); }); - it('should watch all files in watched path when in "all" mode', - { skip: !supportsRecursiveWatching }, async () => { - watcher = new FilesWatcher({ throttle: 100, mode: 'all' }); - watcher.on('changed', () => changesCount++); - - const file = path.join(tmpdir.path, 'file5'); - watcher.watchPath(tmpdir.path); - - const changed = once(watcher, 'changed'); - writeFileSync(file, 'changed'); - await changed; - assert.strictEqual(changesCount, 1); - }); - - it('should ruse existing watcher if it exists', - { skip: !supportsRecursiveWatching }, () => { - assert.deepStrictEqual(watcher.watchedPaths, []); - watcher.watchPath(tmpdir.path); - assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); - watcher.watchPath(tmpdir.path); - assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); - }); - - it('should ruse existing watcher of a parent directory', - { skip: !supportsRecursiveWatching }, () => { - assert.deepStrictEqual(watcher.watchedPaths, []); - watcher.watchPath(tmpdir.path); - assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); - watcher.watchPath(path.join(tmpdir.path, 'subdirectory')); - assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); - }); - - it('should remove existing watcher if adding a parent directory watcher', - { skip: !supportsRecursiveWatching }, () => { - assert.deepStrictEqual(watcher.watchedPaths, []); - const subdirectory = path.join(tmpdir.path, 'subdirectory'); - mkdirSync(subdirectory); - watcher.watchPath(subdirectory); - assert.deepStrictEqual(watcher.watchedPaths, [subdirectory]); - watcher.watchPath(tmpdir.path); - assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); - }); - - it('should clear all watchers when calling clear', - { skip: !supportsRecursiveWatching }, () => { - assert.deepStrictEqual(watcher.watchedPaths, []); - watcher.watchPath(tmpdir.path); - assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); - watcher.clear(); - assert.deepStrictEqual(watcher.watchedPaths, []); - }); + it('should watch all files in watched path when in "all" mode', async () => { + watcher = new FilesWatcher({ throttle: 100, mode: 'all' }); + watcher.on('changed', () => changesCount++); + + const file = path.join(tmpdir.path, 'file5'); + watcher.watchPath(tmpdir.path); + + const changed = once(watcher, 'changed'); + writeFileSync(file, 'changed'); + await changed; + assert.strictEqual(changesCount, 1); + }); + + it('should ruse existing watcher if it exists', () => { + assert.deepStrictEqual(watcher.watchedPaths, []); + watcher.watchPath(tmpdir.path); + assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); + watcher.watchPath(tmpdir.path); + assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); + }); + + it('should ruse existing watcher of a parent directory', () => { + assert.deepStrictEqual(watcher.watchedPaths, []); + watcher.watchPath(tmpdir.path); + assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); + watcher.watchPath(path.join(tmpdir.path, 'subdirectory')); + assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); + }); + + it('should remove existing watcher if adding a parent directory watcher', () => { + assert.deepStrictEqual(watcher.watchedPaths, []); + const subdirectory = path.join(tmpdir.path, 'subdirectory'); + mkdirSync(subdirectory); + watcher.watchPath(subdirectory); + assert.deepStrictEqual(watcher.watchedPaths, [subdirectory]); + watcher.watchPath(tmpdir.path); + assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); + }); + + it('should clear all watchers when calling clear', () => { + assert.deepStrictEqual(watcher.watchedPaths, []); + watcher.watchPath(tmpdir.path); + assert.deepStrictEqual(watcher.watchedPaths, [tmpdir.path]); + watcher.clear(); + assert.deepStrictEqual(watcher.watchedPaths, []); + }); it('should watch files from subprocess IPC events', async () => { const file = fixtures.path('watch-mode/ipc.js'); @@ -153,9 +145,7 @@ describe('watch mode file watcher', () => { watcher.watchChildProcessModules(child); await once(child, 'exit'); let expected = [file, path.join(tmpdir.path, 'file')]; - if (supportsRecursiveWatching) { - expected = expected.map((file) => path.dirname(file)); - } + expected = expected.map((file) => path.dirname(file)); assert.deepStrictEqual(watcher.watchedPaths, expected); }); }); diff --git a/test/sequential/test-watch-mode.mjs b/test/sequential/test-watch-mode.mjs index 9b894922621e3b..7121b4924550da 100644 --- a/test/sequential/test-watch-mode.mjs +++ b/test/sequential/test-watch-mode.mjs @@ -14,8 +14,6 @@ import { createInterface } from 'node:readline/promises'; if (common.isIBMi) common.skip('IBMi does not support `fs.watch()`'); -const supportsRecursive = common.isOSX || common.isWindows; - function restart(file) { // To avoid flakiness, we save the file repeatedly until test is done writeFileSync(file, readFileSync(file)); @@ -125,9 +123,7 @@ describe('watch mode', { concurrency: true, timeout: 60_000 }, () => { }); }); - it('should watch when running an non-existing file - when specified under --watch-path', { - skip: !supportsRecursive - }, async () => { + it('should watch when running an non-existing file - when specified under --watch-path', async () => { const file = fixtures.path('watch-mode/subdir/non-existing.js'); const watchedFile = fixtures.path('watch-mode/subdir/file.js'); const { stderr, stdout } = await spawnWithRestarts({ @@ -234,38 +230,29 @@ describe('watch mode', { concurrency: true, timeout: 60_000 }, () => { }); }); - // TODO: Remove skip after https://github.com/nodejs/node/pull/45271 lands - it('should not watch when running an missing file', { - skip: !supportsRecursive - }, async () => { + it('should not watch when running an missing file', async () => { const nonExistingfile = path.join(tmpdir.path, `${tmpFiles++}.js`); await failWriteSucceed({ file: nonExistingfile, watchedFile: nonExistingfile }); }); - it('should not watch when running an missing mjs file', { - skip: !supportsRecursive - }, async () => { - const nonExistingfile = path.join(tmpdir.path, `${tmpFiles++}.mjs`); - await failWriteSucceed({ file: nonExistingfile, watchedFile: nonExistingfile }); - }); + // it('should not watch when running an missing mjs file', async () => { + // const nonExistingfile = path.join(tmpdir.path, `${tmpFiles++}.mjs`); + // await failWriteSucceed({ file: nonExistingfile, watchedFile: nonExistingfile }); + // }); - it('should watch changes to previously missing dependency', { - skip: !supportsRecursive - }, async () => { - const dependency = path.join(tmpdir.path, `${tmpFiles++}.js`); - const relativeDependencyPath = `./${path.basename(dependency)}`; - const dependant = createTmpFile(`console.log(require('${relativeDependencyPath}'))`); + // it('should watch changes to previously missing dependency', async () => { + // const dependency = path.join(tmpdir.path, `${tmpFiles++}.js`); + // const relativeDependencyPath = `./${path.basename(dependency)}`; + // const dependant = createTmpFile(`console.log(require('${relativeDependencyPath}'))`); - await failWriteSucceed({ file: dependant, watchedFile: dependency }); - }); + // await failWriteSucceed({ file: dependant, watchedFile: dependency }); + // }); - it('should watch changes to previously missing ESM dependency', { - skip: !supportsRecursive - }, async () => { - const dependency = path.join(tmpdir.path, `${tmpFiles++}.mjs`); - const relativeDependencyPath = `./${path.basename(dependency)}`; - const dependant = createTmpFile(`import '${relativeDependencyPath}'`, '.mjs'); + // it('should watch changes to previously missing ESM dependency', async () => { + // const dependency = path.join(tmpdir.path, `${tmpFiles++}.mjs`); + // const relativeDependencyPath = `./${path.basename(dependency)}`; + // const dependant = createTmpFile(`import '${relativeDependencyPath}'`, '.mjs'); - await failWriteSucceed({ file: dependant, watchedFile: dependency }); - }); + // await failWriteSucceed({ file: dependant, watchedFile: dependency }); + // }); });