Skip to content

Commit 34bfef9

Browse files
anonrigRafaelGSS
authored andcommitted
fs: add recursive watch to linux
PR-URL: #45098 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: Moshe Atlow <[email protected]> Reviewed-By: Rich Trott <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 97547bc commit 34bfef9

13 files changed

+801
-109
lines changed

doc/api/fs.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -4320,6 +4320,9 @@ The `atime` and `mtime` arguments follow these rules:
43204320
<!-- YAML
43214321
added: v0.5.10
43224322
changes:
4323+
- version: REPLACEME
4324+
pr-url: https://github.com/nodejs/node/pull/45098
4325+
description: Added recursive support for Linux, AIX and IBMi.
43234326
- version:
43244327
- v15.9.0
43254328
- v14.17.0
@@ -4377,10 +4380,6 @@ the returned {fs.FSWatcher}.
43774380
The `fs.watch` API is not 100% consistent across platforms, and is
43784381
unavailable in some situations.
43794382
4380-
The recursive option is only supported on macOS and Windows.
4381-
An `ERR_FEATURE_UNAVAILABLE_ON_PLATFORM` exception will be thrown
4382-
when the option is used on a platform that does not support it.
4383-
43844383
On Windows, no events will be emitted if the watched directory is moved or
43854384
renamed. An `EPERM` error is reported when the watched directory is deleted.
43864385

lib/fs.js

+17-9
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ const {
5757

5858
const pathModule = require('path');
5959
const { isArrayBufferView } = require('internal/util/types');
60+
const nonNativeWatcher = require('internal/fs/recursive_watch');
6061

6162
// We need to get the statValues from the binding at the callsite since
6263
// it's re-initialized after deserialization.
@@ -68,7 +69,6 @@ const {
6869
codes: {
6970
ERR_FS_FILE_TOO_LARGE,
7071
ERR_INVALID_ARG_VALUE,
71-
ERR_FEATURE_UNAVAILABLE_ON_PLATFORM,
7272
},
7373
AbortError,
7474
uvErrmapGet,
@@ -163,7 +163,6 @@ let FileWriteStream;
163163
const isWindows = process.platform === 'win32';
164164
const isOSX = process.platform === 'darwin';
165165

166-
167166
function showTruncateDeprecation() {
168167
if (truncateWarn) {
169168
process.emitWarning(
@@ -2297,13 +2296,22 @@ function watch(filename, options, listener) {
22972296

22982297
if (options.persistent === undefined) options.persistent = true;
22992298
if (options.recursive === undefined) options.recursive = false;
2300-
if (options.recursive && !(isOSX || isWindows))
2301-
throw new ERR_FEATURE_UNAVAILABLE_ON_PLATFORM('watch recursively');
2302-
const watcher = new watchers.FSWatcher();
2303-
watcher[watchers.kFSWatchStart](filename,
2304-
options.persistent,
2305-
options.recursive,
2306-
options.encoding);
2299+
2300+
let watcher;
2301+
2302+
// TODO(anonrig): Remove this when/if libuv supports it.
2303+
// As of November 2022, libuv does not support recursive file watch on all platforms,
2304+
// e.g. Linux due to the limitations of inotify.
2305+
if (options.recursive && !isOSX && !isWindows) {
2306+
watcher = new nonNativeWatcher.FSWatcher(options);
2307+
watcher[watchers.kFSWatchStart](filename);
2308+
} else {
2309+
watcher = new watchers.FSWatcher();
2310+
watcher[watchers.kFSWatchStart](filename,
2311+
options.persistent,
2312+
options.recursive,
2313+
options.encoding);
2314+
}
23072315

23082316
if (listener) {
23092317
watcher.addListener('change', listener);

lib/internal/fs/promises.js

+24-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ const {
8888
} = require('internal/util');
8989
const { EventEmitterMixin } = require('internal/event_target');
9090
const { StringDecoder } = require('string_decoder');
91-
const { watch } = require('internal/fs/watchers');
91+
const { kFSWatchStart, watch } = require('internal/fs/watchers');
92+
const nonNativeWatcher = require('internal/fs/recursive_watch');
9293
const { isIterable } = require('internal/streams/utils');
9394
const assert = require('internal/assert');
9495

@@ -120,6 +121,7 @@ const getDirectoryEntriesPromise = promisify(getDirents);
120121
const validateRmOptionsPromise = promisify(validateRmOptions);
121122

122123
const isWindows = process.platform === 'win32';
124+
const isOSX = process.platform === 'darwin';
123125

124126
let cpPromises;
125127
function lazyLoadCpPromises() {
@@ -917,6 +919,26 @@ async function readFile(path, options) {
917919
return handleFdClose(readFileHandle(fd, options), fd.close);
918920
}
919921

922+
async function* _watch(filename, options = kEmptyObject) {
923+
validateObject(options, 'options');
924+
925+
if (options.recursive != null) {
926+
validateBoolean(options.recursive, 'options.recursive');
927+
928+
// TODO(anonrig): Remove this when/if libuv supports it.
929+
// As of November 2022, libuv does not support recursive file watch on all platforms,
930+
// e.g. Linux due to the limitations of inotify.
931+
if (options.recursive && !isOSX && !isWindows) {
932+
const watcher = new nonNativeWatcher.FSWatcher(options);
933+
await watcher[kFSWatchStart](filename);
934+
yield* watcher;
935+
return;
936+
}
937+
}
938+
939+
yield* watch(filename, options);
940+
}
941+
920942
module.exports = {
921943
exports: {
922944
access,
@@ -947,7 +969,7 @@ module.exports = {
947969
writeFile,
948970
appendFile,
949971
readFile,
950-
watch,
972+
watch: !isOSX && !isWindows ? _watch : watch,
951973
constants,
952974
},
953975

0 commit comments

Comments
 (0)