diff options
author | James M Snell <jasnell@gmail.com> | 2018-05-15 12:34:49 -0700 |
---|---|---|
committer | James M Snell <jasnell@gmail.com> | 2018-05-20 14:15:56 -0700 |
commit | 7f0f978affda555c5f7151f2d6abd212e753d4f1 (patch) | |
tree | 3f40904376635a00f0a15cd33186be09715e903c /lib/internal/fs/watchers.js | |
parent | a14a0fa8dc401188f67d26a82daae9423c54b41f (diff) | |
download | android-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.js | 168 |
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 +}; |