summaryrefslogtreecommitdiff
path: root/lib/internal/fs/watchers.js
diff options
context:
space:
mode:
authorJames M Snell <jasnell@gmail.com>2018-05-15 12:34:49 -0700
committerJames M Snell <jasnell@gmail.com>2018-05-20 14:15:56 -0700
commit7f0f978affda555c5f7151f2d6abd212e753d4f1 (patch)
tree3f40904376635a00f0a15cd33186be09715e903c /lib/internal/fs/watchers.js
parenta14a0fa8dc401188f67d26a82daae9423c54b41f (diff)
downloadandroid-node-v8-7f0f978affda555c5f7151f2d6abd212e753d4f1.tar.gz
android-node-v8-7f0f978affda555c5f7151f2d6abd212e753d4f1.tar.bz2
android-node-v8-7f0f978affda555c5f7151f2d6abd212e753d4f1.zip
fs: refactor fs module
PR-URL: https://github.com/nodejs/node/pull/20764 Reviewed-By: Michaƫl Zasso <targos@protonmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Diffstat (limited to 'lib/internal/fs/watchers.js')
-rw-r--r--lib/internal/fs/watchers.js168
1 files changed, 168 insertions, 0 deletions
diff --git a/lib/internal/fs/watchers.js b/lib/internal/fs/watchers.js
new file mode 100644
index 0000000000..685d5be5e4
--- /dev/null
+++ b/lib/internal/fs/watchers.js
@@ -0,0 +1,168 @@
+'use strict';
+
+const errors = require('internal/errors');
+const {
+ kFsStatsFieldsLength,
+ StatWatcher: _StatWatcher
+} = process.binding('fs');
+const { FSEvent } = process.binding('fs_event_wrap');
+const { EventEmitter } = require('events');
+const {
+ getStatsFromBinding,
+ validatePath
+} = require('internal/fs/utils');
+const { toNamespacedPath } = require('path');
+const { validateUint32 } = require('internal/validators');
+const { getPathFromURL } = require('internal/url');
+const util = require('util');
+const assert = require('assert');
+
+function emitStop(self) {
+ self.emit('stop');
+}
+
+function StatWatcher() {
+ EventEmitter.call(this);
+
+ this._handle = new _StatWatcher();
+
+ // uv_fs_poll is a little more powerful than ev_stat but we curb it for
+ // the sake of backwards compatibility
+ let oldStatus = -1;
+
+ this._handle.onchange = (newStatus, stats) => {
+ if (oldStatus === -1 &&
+ newStatus === -1 &&
+ stats[2/* new nlink */] === stats[16/* old nlink */]) return;
+
+ oldStatus = newStatus;
+ this.emit('change', getStatsFromBinding(stats),
+ getStatsFromBinding(stats, kFsStatsFieldsLength));
+ };
+
+ this._handle.onstop = () => {
+ process.nextTick(emitStop, this);
+ };
+}
+util.inherits(StatWatcher, EventEmitter);
+
+
+// FIXME(joyeecheung): this method is not documented.
+// At the moment if filename is undefined, we
+// 1. Throw an Error if it's the first time .start() is called
+// 2. Return silently if .start() has already been called
+// on a valid filename and the wrap has been initialized
+// This method is a noop if the watcher has already been started.
+StatWatcher.prototype.start = function(filename, persistent, interval) {
+ assert(this._handle instanceof _StatWatcher, 'handle must be a StatWatcher');
+ if (this._handle.isActive) {
+ return;
+ }
+
+ filename = getPathFromURL(filename);
+ validatePath(filename, 'filename');
+ validateUint32(interval, 'interval');
+ const err = this._handle.start(toNamespacedPath(filename),
+ persistent, interval);
+ if (err) {
+ const error = errors.uvException({
+ errno: err,
+ syscall: 'watch',
+ path: filename
+ });
+ error.filename = filename;
+ throw error;
+ }
+};
+
+// FIXME(joyeecheung): this method is not documented while there is
+// another documented fs.unwatchFile(). The counterpart in
+// FSWatcher is .close()
+// This method is a noop if the watcher has not been started.
+StatWatcher.prototype.stop = function() {
+ assert(this._handle instanceof _StatWatcher, 'handle must be a StatWatcher');
+ if (!this._handle.isActive) {
+ return;
+ }
+ this._handle.stop();
+};
+
+
+function FSWatcher() {
+ EventEmitter.call(this);
+
+ this._handle = new FSEvent();
+ this._handle.owner = this;
+
+ this._handle.onchange = (status, eventType, filename) => {
+ // TODO(joyeecheung): we may check self._handle.initialized here
+ // and return if that is false. This allows us to avoid firing the event
+ // after the handle is closed, and to fire both UV_RENAME and UV_CHANGE
+ // if they are set by libuv at the same time.
+ if (status < 0) {
+ this._handle.close();
+ const error = errors.uvException({
+ errno: status,
+ syscall: 'watch',
+ path: filename
+ });
+ error.filename = filename;
+ this.emit('error', error);
+ } else {
+ this.emit('change', eventType, filename);
+ }
+ };
+}
+util.inherits(FSWatcher, EventEmitter);
+
+// FIXME(joyeecheung): this method is not documented.
+// At the moment if filename is undefined, we
+// 1. Throw an Error if it's the first time .start() is called
+// 2. Return silently if .start() has already been called
+// on a valid filename and the wrap has been initialized
+// This method is a noop if the watcher has already been started.
+FSWatcher.prototype.start = function(filename,
+ persistent,
+ recursive,
+ encoding) {
+ assert(this._handle instanceof FSEvent, 'handle must be a FSEvent');
+ if (this._handle.initialized) {
+ return;
+ }
+
+ filename = getPathFromURL(filename);
+ validatePath(filename, 'filename');
+
+ const err = this._handle.start(toNamespacedPath(filename),
+ persistent,
+ recursive,
+ encoding);
+ if (err) {
+ const error = errors.uvException({
+ errno: err,
+ syscall: 'watch',
+ path: filename
+ });
+ error.filename = filename;
+ throw error;
+ }
+};
+
+// This method is a noop if the watcher has not been started.
+FSWatcher.prototype.close = function() {
+ assert(this._handle instanceof FSEvent, 'handle must be a FSEvent');
+ if (!this._handle.initialized) {
+ return;
+ }
+ this._handle.close();
+ process.nextTick(emitCloseNT, this);
+};
+
+function emitCloseNT(self) {
+ self.emit('close');
+}
+
+module.exports = {
+ FSWatcher,
+ StatWatcher
+};