Skip to content

Commit

Permalink
fs: add promise support to linux watcher
Browse files Browse the repository at this point in the history
  • Loading branch information
anonrig committed Oct 22, 2022
1 parent 7d8e46c commit 84a4cea
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 5 deletions.
19 changes: 18 additions & 1 deletion lib/internal/fs/promises.js
Expand Up @@ -89,6 +89,7 @@ const {
const { EventEmitterMixin } = require('internal/event_target');
const { StringDecoder } = require('string_decoder');
const { watch } = require('internal/fs/watchers');
const linuxWatcher = require('internal/fs/watch/linux');
const { isIterable } = require('internal/streams/utils');
const assert = require('internal/assert');

Expand Down Expand Up @@ -120,6 +121,7 @@ const getDirectoryEntriesPromise = promisify(getDirents);
const validateRmOptionsPromise = promisify(validateRmOptions);

const isWindows = process.platform === 'win32';
const isLinux = process.platform === 'linux';

let cpPromises;
function lazyLoadCpPromises() {
Expand Down Expand Up @@ -917,6 +919,21 @@ async function readFile(path, options) {
return handleFdClose(readFileHandle(fd, options), fd.close);
}

function _watch(filename, options) {
options = getOptions(options, { persistent: true, recursive: false, encoding: 'utf8' });

// TODO(anonrig): Remove this when/if libuv supports it.
// libuv does not support recursive file watch on Linux due to
// the limitations of inotify.
if (options.recursive && isLinux) {
const watcher = new linuxWatcher.FSWatcher(options);
watcher[linuxWatcher.kFSWatchStart](filename);
return watcher;
}

return watch(filename, options);
}

module.exports = {
exports: {
access,
Expand Down Expand Up @@ -947,7 +964,7 @@ module.exports = {
writeFile,
appendFile,
readFile,
watch,
watch: _watch,
constants,
},

Expand Down
28 changes: 24 additions & 4 deletions lib/internal/fs/watch/linux.js
@@ -1,11 +1,12 @@
'use strict';

const { SafeMap, Symbol, StringPrototypeStartsWith, SymbolAsyncIterator, Promise } = primordials;

const { ERR_FEATURE_UNAVAILABLE_ON_PLATFORM } = require('internal/errors');
const { kEmptyObject } = require('internal/util');
const { validateObject, validateString } = require('internal/validators');
const { EventEmitter } = require('events');
const path = require('path');
const { SafeMap, Symbol, StringPrototypeStartsWith } = primordials;
const { validateObject, validateString } = require('internal/validators');
const { kEmptyObject } = require('internal/util');
const { ERR_FEATURE_UNAVAILABLE_ON_PLATFORM } = require('internal/errors');

const kFSWatchStart = Symbol('kFSWatchStart');

Expand Down Expand Up @@ -185,6 +186,25 @@ class FSWatcher extends EventEmitter {
// This is kept to have the same API with FSWatcher
throw new ERR_FEATURE_UNAVAILABLE_ON_PLATFORM('unref');
}

async next() {
if (this.#closed) {
return { done: true };
}

const result = await new Promise((resolve) => {
this.once('change', (eventType, filename) => {
resolve({ eventType, filename });
});
});

return { done: false, value: result };
}

// eslint-disable-next-line require-yield
*[SymbolAsyncIterator]() {
return this;
}
}

module.exports = {
Expand Down
40 changes: 40 additions & 0 deletions test/parallel/test-fs-watch-recursive-linux-promise.js
@@ -0,0 +1,40 @@
'use strict';

const common = require('../common');

if (!common.hasCrypto)
common.skip('missing crypto');

const { randomUUID } = require('crypto');
const assert = require('assert');
const path = require('path');
const fs = require('fs/promises');

const tmpdir = require('../common/tmpdir');
const testDir = tmpdir.path;
tmpdir.refresh();

(async () => {
{
// Add a file to already watching folder

const testsubdir = await fs.mkdtemp(testDir + path.sep);
const file = `${randomUUID()}.txt`;
const filePath = path.join(testsubdir, file);
const watcher = fs.watch(testsubdir, { recursive: true });

setTimeout(async () => {
await fs.writeFile(filePath, 'world');
}, 100);

for await (const payload of watcher) {
const { eventType, filename } = payload;

assert.ok(eventType === 'change' || eventType === 'rename');

if (filename === file) {
break;
}
}
}
})().then(common.mustCall());

0 comments on commit 84a4cea

Please sign in to comment.