Skip to content

Commit 08d8f2e

Browse files
anonrigdanielleadams
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 9d63883 commit 08d8f2e

13 files changed

+802
-109
lines changed

doc/api/fs.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -4301,6 +4301,9 @@ The `atime` and `mtime` arguments follow these rules:
43014301
<!-- YAML
43024302
added: v0.5.10
43034303
changes:
4304+
- version: REPLACEME
4305+
pr-url: https://github.com/nodejs/node/pull/45098
4306+
description: Added recursive support for Linux, AIX and IBMi.
43044307
- version:
43054308
- v15.9.0
43064309
- v14.17.0
@@ -4358,10 +4361,6 @@ the returned {fs.FSWatcher}.
43584361
The `fs.watch` API is not 100% consistent across platforms, and is
43594362
unavailable in some situations.
43604363
4361-
The recursive option is only supported on macOS and Windows.
4362-
An `ERR_FEATURE_UNAVAILABLE_ON_PLATFORM` exception will be thrown
4363-
when the option is used on a platform that does not support it.
4364-
43654364
On Windows, no events will be emitted if the watched directory is moved or
43664365
renamed. An `EPERM` error is reported when the watched directory is deleted.
43674366

lib/fs.js

+17-9
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const {
5858

5959
const pathModule = require('path');
6060
const { isArrayBufferView } = require('internal/util/types');
61+
const nonNativeWatcher = require('internal/fs/recursive_watch');
6162

6263
// We need to get the statValues from the binding at the callsite since
6364
// it's re-initialized after deserialization.
@@ -69,7 +70,6 @@ const {
6970
codes: {
7071
ERR_FS_FILE_TOO_LARGE,
7172
ERR_INVALID_ARG_VALUE,
72-
ERR_FEATURE_UNAVAILABLE_ON_PLATFORM,
7373
},
7474
AbortError,
7575
uvErrmapGet,
@@ -166,7 +166,6 @@ let FileWriteStream;
166166
const isWindows = process.platform === 'win32';
167167
const isOSX = process.platform === 'darwin';
168168

169-
170169
const showStringCoercionDeprecation = deprecate(
171170
() => {},
172171
'Implicit coercion of objects with own toString property is deprecated.',
@@ -2309,13 +2308,22 @@ function watch(filename, options, listener) {
23092308

23102309
if (options.persistent === undefined) options.persistent = true;
23112310
if (options.recursive === undefined) options.recursive = false;
2312-
if (options.recursive && !(isOSX || isWindows))
2313-
throw new ERR_FEATURE_UNAVAILABLE_ON_PLATFORM('watch recursively');
2314-
const watcher = new watchers.FSWatcher();
2315-
watcher[watchers.kFSWatchStart](filename,
2316-
options.persistent,
2317-
options.recursive,
2318-
options.encoding);
2311+
2312+
let watcher;
2313+
2314+
// TODO(anonrig): Remove this when/if libuv supports it.
2315+
// As of November 2022, libuv does not support recursive file watch on all platforms,
2316+
// e.g. Linux due to the limitations of inotify.
2317+
if (options.recursive && !isOSX && !isWindows) {
2318+
watcher = new nonNativeWatcher.FSWatcher(options);
2319+
watcher[watchers.kFSWatchStart](filename);
2320+
} else {
2321+
watcher = new watchers.FSWatcher();
2322+
watcher[watchers.kFSWatchStart](filename,
2323+
options.persistent,
2324+
options.recursive,
2325+
options.encoding);
2326+
}
23192327

23202328
if (listener) {
23212329
watcher.addListener('change', listener);

lib/internal/fs/promises.js

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

@@ -118,6 +119,8 @@ const {
118119
const getDirectoryEntriesPromise = promisify(getDirents);
119120
const validateRmOptionsPromise = promisify(validateRmOptions);
120121

122+
const isOSX = process.platform === 'darwin';
123+
121124
let cpPromises;
122125
function lazyLoadCpPromises() {
123126
return cpPromises ??= require('internal/fs/cp/cp').cpFn;
@@ -902,6 +905,26 @@ async function readFile(path, options) {
902905
return handleFdClose(readFileHandle(fd, options), fd.close);
903906
}
904907

908+
async function* _watch(filename, options = kEmptyObject) {
909+
validateObject(options, 'options');
910+
911+
if (options.recursive != null) {
912+
validateBoolean(options.recursive, 'options.recursive');
913+
914+
// TODO(anonrig): Remove this when/if libuv supports it.
915+
// As of November 2022, libuv does not support recursive file watch on all platforms,
916+
// e.g. Linux due to the limitations of inotify.
917+
if (options.recursive && !isOSX && !isWindows) {
918+
const watcher = new nonNativeWatcher.FSWatcher(options);
919+
await watcher[kFSWatchStart](filename);
920+
yield* watcher;
921+
return;
922+
}
923+
}
924+
925+
yield* watch(filename, options);
926+
}
927+
905928
module.exports = {
906929
exports: {
907930
access,
@@ -932,7 +955,7 @@ module.exports = {
932955
writeFile,
933956
appendFile,
934957
readFile,
935-
watch,
958+
watch: !isOSX && !isWindows ? _watch : watch,
936959
constants,
937960
},
938961

0 commit comments

Comments
 (0)