diff --git a/doc/api/errors.md b/doc/api/errors.md
index 70ac01de610d6a..7d6e238f93bdaa 100644
--- a/doc/api/errors.md
+++ b/doc/api/errors.md
@@ -783,6 +783,18 @@ falsy value.
An invalid symlink type was passed to the [`fs.symlink()`][] or
[`fs.symlinkSync()`][] methods.
+
+### ERR_FS_WATCHER_ALREADY_STARTED
+
+An attempt was made to start a watcher returned by `fs.watch()` that has
+already been started.
+
+
+### ERR_FS_WATCHER_NOT_STARTED
+
+An attempt was made to initiate operations on a watcher returned by
+`fs.watch()` that has not yet been started.
+
### ERR_HTTP_HEADERS_SENT
diff --git a/lib/fs.js b/lib/fs.js
index 917c3eb3a9f640..321fbe8d54ef8e 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -77,13 +77,19 @@ Object.defineProperty(exports, 'constants', {
value: constants
});
+let assert_ = null;
+function lazyAssert() {
+ if (assert_ === null) {
+ assert_ = require('assert');
+ }
+ return assert_;
+}
+
const kMinPoolSpace = 128;
const { kMaxLength } = require('buffer');
const isWindows = process.platform === 'win32';
-const errnoException = errors.errnoException;
-
let truncateWarn = true;
function showTruncateDeprecation() {
@@ -1312,11 +1318,17 @@ function FSWatcher() {
this._handle.owner = this;
this._handle.onchange = function(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) {
self._handle.close();
- const error = !filename ?
- errnoException(status, 'Error watching file for changes:') :
- errnoException(status, `Error watching file ${filename} for changes:`);
+ const error = errors.uvException({
+ errno: status,
+ syscall: 'watch',
+ path: filename
+ });
error.filename = filename;
self.emit('error', error);
} else {
@@ -1335,21 +1347,34 @@ FSWatcher.prototype.start = function(filename,
persistent,
recursive,
encoding) {
+ lazyAssert()(this._handle instanceof FSEvent, 'handle must be a FSEvent');
+ if (this._handle.initialized) {
+ throw new errors.Error('ERR_FS_WATCHER_ALREADY_STARTED');
+ }
+
filename = getPathFromURL(filename);
- nullCheck(filename, 'filename');
+ validatePath(filename, 'filename');
+
var err = this._handle.start(pathModule.toNamespacedPath(filename),
persistent,
recursive,
encoding);
if (err) {
- this._handle.close();
- const error = errnoException(err, `watch ${filename}`);
+ const error = errors.uvException({
+ errno: err,
+ syscall: 'watch',
+ path: filename
+ });
error.filename = filename;
throw error;
}
};
FSWatcher.prototype.close = function() {
+ lazyAssert()(this._handle instanceof FSEvent, 'handle must be a FSEvent');
+ if (!this._handle.initialized) {
+ throw new errors.Error('ERR_FS_WATCHER_NOT_STARTED');
+ }
this._handle.close();
};
diff --git a/lib/internal/errors.js b/lib/internal/errors.js
index d5a5c4fc599619..fd93547d26d37e 100644
--- a/lib/internal/errors.js
+++ b/lib/internal/errors.js
@@ -658,6 +658,10 @@ E('ERR_FALSY_VALUE_REJECTION', 'Promise was rejected with falsy value', Error);
E('ERR_FS_INVALID_SYMLINK_TYPE',
'Symlink type must be one of "dir", "file", or "junction". Received "%s"',
Error); // Switch to TypeError. The current implementation does not seem right
+E('ERR_FS_WATCHER_ALREADY_STARTED',
+ 'The watcher has already been started', Error);
+E('ERR_FS_WATCHER_NOT_STARTED',
+ 'The watcher has not been started', Error);
E('ERR_HTTP2_ALTSVC_INVALID_ORIGIN',
'HTTP/2 ALTSVC frames require a valid origin', TypeError);
E('ERR_HTTP2_ALTSVC_LENGTH',
diff --git a/src/fs_event_wrap.cc b/src/fs_event_wrap.cc
index 85a09060a11edc..b3fa3e8d9a075b 100644
--- a/src/fs_event_wrap.cc
+++ b/src/fs_event_wrap.cc
@@ -31,6 +31,8 @@
namespace node {
using v8::Context;
+using v8::DontDelete;
+using v8::DontEnum;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::HandleScope;
@@ -38,6 +40,9 @@ using v8::Integer;
using v8::Local;
using v8::MaybeLocal;
using v8::Object;
+using v8::PropertyAttribute;
+using v8::ReadOnly;
+using v8::Signature;
using v8::String;
using v8::Value;
@@ -51,7 +56,7 @@ class FSEventWrap: public HandleWrap {
static void New(const FunctionCallbackInfo& args);
static void Start(const FunctionCallbackInfo& args);
static void Close(const FunctionCallbackInfo& args);
-
+ static void GetInitialized(const FunctionCallbackInfo& args);
size_t self_size() const override { return sizeof(*this); }
private:
@@ -80,6 +85,11 @@ FSEventWrap::~FSEventWrap() {
CHECK_EQ(initialized_, false);
}
+void FSEventWrap::GetInitialized(const FunctionCallbackInfo& args) {
+ FSEventWrap* wrap = Unwrap(args.This());
+ CHECK(wrap != nullptr);
+ args.GetReturnValue().Set(wrap->initialized_);
+}
void FSEventWrap::Initialize(Local