Comparing version 0.9.0 to 0.10.0
516
index.js
@@ -7,9 +7,9 @@ 'use strict'; | ||
var fsevents, recursiveReaddir; | ||
var fsevents, readdirp; | ||
try { | ||
fsevents = require('fsevents'); | ||
recursiveReaddir = require('recursive-readdir'); | ||
readdirp = require('readdirp'); | ||
} catch (error) {} | ||
var isWindows = os.platform() === 'win32'; | ||
var isWin32 = os.platform() === 'win32'; | ||
var canUseFsEvents = os.platform() === 'darwin' && !!fsevents; | ||
@@ -21,3 +21,16 @@ | ||
// Binary file handling code. | ||
var _binExts = ['adp', 'au', 'mid', 'mp4a', 'mpga', 'oga', 's3m', 'sil', 'eol', 'dra', 'dts', 'dtshd', 'lvp', 'pya', 'ecelp4800', 'ecelp7470', 'ecelp9600', 'rip', 'weba', 'aac', 'aif', 'caf', 'flac', 'mka', 'm3u', 'wax', 'wma', 'wav', 'xm', 'flac', '3gp', '3g2', 'h261', 'h263', 'h264', 'jpgv', 'jpm', 'mj2', 'mp4', 'mpeg', 'ogv', 'qt', 'uvh', 'uvm', 'uvp', 'uvs', 'dvb', 'fvt', 'mxu', 'pyv', 'uvu', 'viv', 'webm', 'f4v', 'fli', 'flv', 'm4v', 'mkv', 'mng', 'asf', 'vob', 'wm', 'wmv', 'wmx', 'wvx', 'movie', 'smv', 'ts', 'bmp', 'cgm', 'g3', 'gif', 'ief', 'jpg', 'jpeg', 'ktx', 'png', 'btif', 'sgi', 'svg', 'tiff', 'psd', 'uvi', 'sub', 'djvu', 'dwg', 'dxf', 'fbs', 'fpx', 'fst', 'mmr', 'rlc', 'mdi', 'wdp', 'npx', 'wbmp', 'xif', 'webp', '3ds', 'ras', 'cmx', 'fh', 'ico', 'pcx', 'pic', 'pnm', 'pbm', 'pgm', 'ppm', 'rgb', 'tga', 'xbm', 'xpm', 'xwd', 'zip', 'rar', 'tar', 'bz2', 'eot', 'ttf', 'woff']; | ||
var _binExts = [ | ||
'adp', 'au', 'mid', 'mp4a', 'mpga', 'oga', 's3m', 'sil', 'eol', 'dra', 'dts', | ||
'dtshd', 'lvp', 'pya', 'ecelp4800', 'ecelp7470', 'ecelp9600', 'rip', 'weba', | ||
'aac', 'aif', 'caf', 'flac', 'mka', 'm3u', 'wax', 'wma', 'wav', 'xm', 'flac', | ||
'3gp', '3g2', 'h261', 'h263', 'h264', 'jpgv', 'jpm', 'mj2', 'mp4', 'mpeg', | ||
'ogv', 'qt', 'uvh', 'uvm', 'uvp', 'uvs', 'dvb', 'fvt', 'mxu', 'pyv', 'uvu', | ||
'viv', 'webm', 'f4v', 'fli', 'flv', 'm4v', 'mkv', 'mng', 'asf', 'vob', 'wm', | ||
'wmv', 'wmx', 'wvx', 'movie', 'smv', 'ts', 'bmp', 'cgm', 'g3', 'gif', 'ief', | ||
'jpg', 'jpeg', 'ktx', 'png', 'btif', 'sgi', 'svg', 'tiff', 'psd', 'uvi', | ||
'sub', 'djvu', 'dwg', 'dxf', 'fbs', 'fpx', 'fst', 'mmr', 'rlc', 'mdi', 'wdp', | ||
'npx', 'wbmp', 'xif', 'webp', '3ds', 'ras', 'cmx', 'fh', 'ico', 'pcx', 'pic', | ||
'pnm', 'pbm', 'pgm', 'ppm', 'rgb', 'tga', 'xbm', 'xpm', 'xwd', 'zip', 'rar', | ||
'tar', 'bz2', 'eot', 'ttf', 'woff' | ||
]; | ||
@@ -27,3 +40,3 @@ var binExts = Object.create(null); | ||
var isBinary = function(extension) { | ||
function isBinary(extension) { | ||
if (extension === '') return false; | ||
@@ -33,65 +46,64 @@ return !!binExts[extension]; | ||
var isBinaryPath = function(path) { | ||
function isBinaryPath(path) { | ||
return isBinary(sysPath.extname(path).slice(1)); | ||
}; | ||
} | ||
exports.isBinaryPath = isBinaryPath; | ||
// Main code. | ||
// | ||
// Public: Main class. | ||
// Watches files & directories for changes. | ||
// | ||
// Emitted events: `add`, `change`, `unlink`, `error`. | ||
// * _opts - object, chokidar options hash | ||
// | ||
// Emitted events: | ||
// `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `all`, `error` | ||
// | ||
// Examples | ||
// | ||
// var watcher = new FSWatcher() | ||
// .add(directories) | ||
// .on('add', function(path) {console.log('File', path, 'was added');}) | ||
// .on('change', function(path) {console.log('File', path, 'was changed');}) | ||
// .on('unlink', function(path) {console.log('File', path, 'was removed');}) | ||
// var watcher = new FSWatcher() | ||
// .add(directories) | ||
// .on('add', function(path) {console.log('File', path, 'was added');}) | ||
// .on('change', function(path) {console.log('File', path, 'was changed');}) | ||
// .on('unlink', function(path) {console.log('File', path, 'was removed');}) | ||
// .on('all', function(event, path) {console.log(path, ' emitted ', event);}) | ||
// | ||
function FSWatcher(_opts) { | ||
if (_opts == null) _opts = {}; | ||
var opts = {}; | ||
for (var opt in _opts) opts[opt] = _opts[opt] | ||
this.close = this.close.bind(this); | ||
EventEmitter.call(this); | ||
// in case _opts that is passed in is a frozen object | ||
if (_opts) for (var opt in _opts) opts[opt] = _opts[opt]; | ||
this.watched = Object.create(null); | ||
this.watchers = []; | ||
this.closed = false; | ||
this.listeners = Object.create(null); | ||
this._throttled = Object.create(null); | ||
// Set up default options. | ||
if (opts.persistent == null) opts.persistent = false; | ||
if (opts.ignoreInitial == null) opts.ignoreInitial = false; | ||
if (opts.interval == null) opts.interval = 100; | ||
if (opts.binaryInterval == null) opts.binaryInterval = 300; | ||
if (!('persistent' in opts)) opts.persistent = false; | ||
if (!('ignoreInitial' in opts)) opts.ignoreInitial = false; | ||
if (!('ignorePermissionErrors' in opts)) opts.ignorePermissionErrors = false; | ||
if (!('interval' in opts)) opts.interval = 100; | ||
if (!('binaryInterval' in opts)) opts.binaryInterval = 300; | ||
this.enableBinaryInterval = opts.binaryInterval !== opts.interval; | ||
// Use polling on Mac and Linux. | ||
// Disable polling on Windows. | ||
if (opts.usePolling == null) opts.usePolling = !isWindows; | ||
// Enable fsevents on OS X when polling is disabled. | ||
// Which is basically super fast watcher. | ||
if (opts.useFsEvents == null) opts.useFsEvents = !opts.usePolling; | ||
if (!('useFsEvents' in opts)) opts.useFsEvents = !opts.usePolling; | ||
// If we can't use fs events, disable it in any case. | ||
if (!canUseFsEvents) opts.useFsEvents = false; | ||
if (opts.ignorePermissionErrors == null) opts.ignorePermissionErrors = false; | ||
// Use polling by default on Linux and Mac (if not using fsevents). | ||
// Disable polling on Windows. | ||
if (!('usePolling' in opts) && !opts.useFsEvents) opts.usePolling = !isWin32; | ||
this.enableBinaryInterval = opts.binaryInterval !== opts.interval; | ||
this._isIgnored = (function(ignored) { | ||
switch (toString.call(ignored)) { | ||
case '[object RegExp]': | ||
return function(string) { | ||
return ignored.test(string); | ||
}; | ||
case '[object Function]': | ||
return ignored; | ||
default: | ||
return function() { | ||
return false; | ||
}; | ||
case '[object RegExp]': | ||
return function(string) { | ||
return ignored.test(string); | ||
}; | ||
case '[object Function]': | ||
return ignored; | ||
default: | ||
return function() { | ||
return false; | ||
}; | ||
} | ||
@@ -108,9 +120,36 @@ })(opts.ignored); | ||
// Common helpers | ||
// -------------- | ||
FSWatcher.prototype._emit = function(event) { | ||
var args = [].slice.apply(arguments); | ||
this.emit.apply(this, args); | ||
if (event !== 'error') this.emit.apply(this, ['all'].concat(args)); | ||
return this; | ||
}; | ||
FSWatcher.prototype._handleError = function(error) { | ||
if (error && error.code !== 'ENOENT') this.emit('error', error); | ||
return error || this.closed; | ||
}; | ||
FSWatcher.prototype._throttle = function(action, path, timeout) { | ||
if (!(action in this._throttled)) { | ||
this._throttled[action] = Object.create(null); | ||
} | ||
var throttled = this._throttled[action]; | ||
if (path in throttled) return false; | ||
function clear() { | ||
delete throttled[path]; | ||
clearTimeout(timeoutObject); | ||
} | ||
var timeoutObject = setTimeout(clear, timeout); | ||
throttled[path] = {timeoutObject: timeoutObject, clear: clear}; | ||
return throttled[path]; | ||
}; | ||
// Directory helpers | ||
// ----------------- | ||
var directoryEndRegex = /[\\\/]$/; | ||
FSWatcher.prototype._getWatchedDir = function(directory) { | ||
var dir = directory.replace(directoryEndRegex, ''); | ||
if (this.watched[dir] == null) { this.watched[dir] = []; } | ||
var dir = sysPath.resolve(directory); | ||
if (!(dir in this.watched)) this.watched[dir] = []; | ||
return this.watched[dir]; | ||
@@ -121,3 +160,3 @@ }; | ||
var watchedFiles = this._getWatchedDir(directory); | ||
return watchedFiles.push(basename); | ||
watchedFiles.push(basename); | ||
}; | ||
@@ -127,7 +166,4 @@ | ||
var watchedFiles = this._getWatchedDir(directory); | ||
return watchedFiles.some(function(watchedFile, index) { | ||
if (watchedFile === file) { | ||
watchedFiles.splice(index, 1); | ||
return true; | ||
} | ||
watchedFiles.some(function(watchedFile, index) { | ||
if (watchedFile === file) return watchedFiles.splice(index, 1); | ||
}); | ||
@@ -142,7 +178,7 @@ }; | ||
// | ||
// stats - fs.Stats object | ||
// * stats - object, result of fs.stat | ||
// | ||
// Returns Boolean | ||
FSWatcher.prototype._hasReadPermissions = function(stats) { | ||
return Boolean(4 & parseInt((stats.mode & 0x1ff).toString(8)[0])); | ||
return Boolean(4 & parseInt((stats.mode & 0x1ff).toString(8)[0], 10)); | ||
}; | ||
@@ -154,4 +190,4 @@ | ||
// | ||
// directory - string, directory within which the following item is located | ||
// item - string, base path of item/directory | ||
// * directory - string, directory within which the following item is located | ||
// * item - string, base path of item/directory | ||
// | ||
@@ -167,2 +203,6 @@ // Returns nothing. | ||
// prevent duplicate handling in case of arriving here nearly simultaneously | ||
// via multiple paths (such as _handleFile and _handleDir) | ||
if (!this._throttle('remove', fullPath, 5)) return; | ||
// This will create a new entry in the watched object in either case | ||
@@ -177,3 +217,3 @@ // so we got to do the directory check beforehand | ||
nestedDirectoryChildren.forEach(function(nestedItem) { | ||
return this._remove(fullPath, nestedItem); | ||
this._remove(fullPath, nestedItem); | ||
}, this); | ||
@@ -190,45 +230,66 @@ | ||
var eventName = isDirectory ? 'unlinkDir' : 'unlink'; | ||
this.emit(eventName, fullPath); | ||
this._emit(eventName, fullPath); | ||
}; | ||
// FS Events helper. | ||
var createFSEventsInstance = function(path, callback) { | ||
var watcher = new fsevents(path); | ||
watcher.on('fsevent', callback); | ||
watcher.start(); | ||
return watcher; | ||
}; | ||
function createFSEventsInstance(path, callback) { | ||
return (new fsevents(path)).on('fsevent', callback).start(); | ||
} | ||
FSWatcher.prototype._watchWithFsEvents = function(path) { | ||
var _this = this; | ||
var watcher = createFSEventsInstance(path, function(path, flags) { | ||
var emit, info; | ||
if (_this._isIgnored(path)) { | ||
return; | ||
} | ||
info = fsevents.getInfo(path, flags); | ||
emit = function(event) { | ||
var name; | ||
name = info.type === 'file' ? event : "" + event + "Dir"; | ||
if (event === 'add' || event === 'addDir') { | ||
_this._addToWatchedDir(sysPath.dirname(path), sysPath.basename(path)); | ||
} else if (event === 'unlink' || event === 'unlinkDir') { | ||
_this._remove(sysPath.dirname(path), sysPath.basename(path)); | ||
FSWatcher.prototype._watchWithFsEvents = function(watchPath) { | ||
if (this._isIgnored(watchPath)) return; | ||
var watcher = createFSEventsInstance(watchPath, function(fullPath, flags) { | ||
var info = fsevents.getInfo(fullPath, flags); | ||
var path = sysPath.join(watchPath, sysPath.relative(watchPath, fullPath)); | ||
// ensure directories are tracked | ||
var parent = sysPath.dirname(path); | ||
var item = sysPath.basename(path); | ||
var watchedDir = this._getWatchedDir( | ||
info.type === 'directory' ? path : parent | ||
); | ||
var handleEvent = function handleEvent(event) { | ||
if (event === 'add') { | ||
this._addToWatchedDir(parent, item); | ||
} else if (event === 'unlink') { | ||
this._remove(parent, item); | ||
return; // Don't emit event twice. | ||
} | ||
return _this.emit(name, path); | ||
}; | ||
switch (info.event) { | ||
case 'created': | ||
return emit('add'); | ||
case 'modified': | ||
return emit('change'); | ||
case 'deleted': | ||
return emit('unlink'); | ||
case 'moved': | ||
return fs.stat(path, function(error, stats) { | ||
return emit(error || !stats ? 'unlink' : 'add'); | ||
var eventName = info.type === 'file' ? event : event + 'Dir'; | ||
this._emit(eventName, path); | ||
}.bind(this); | ||
// correct for wrong events emitted | ||
function addOrChange() { | ||
handleEvent(watchedDir.indexOf(item) !== -1 ? 'change' : 'add'); | ||
} | ||
var wrongEventFlags = [69888, 70400, 71424, 72704, 131328, 131840]; | ||
if (wrongEventFlags.indexOf(flags) !== -1) { | ||
if (info.event === 'deleted' || info.event === 'moved') { | ||
fs.stat(path, function(error, stats) { | ||
if (stats) { | ||
addOrChange(); | ||
} else { | ||
handleEvent('unlink'); | ||
} | ||
}); | ||
} else { | ||
addOrChange(); | ||
} | ||
return; | ||
} | ||
}); | ||
switch (info.event) { | ||
case 'created': | ||
return handleEvent('add'); | ||
case 'modified': | ||
return handleEvent('change'); | ||
case 'deleted': | ||
return handleEvent('unlink'); | ||
case 'moved': | ||
return fs.stat(path, function(error, stats) { | ||
handleEvent(stats ? flags === 72960 ? 'change' : 'add' : 'unlink'); | ||
}); | ||
} | ||
}.bind(this)); | ||
return this.watchers.push(watcher); | ||
@@ -239,17 +300,15 @@ }; | ||
// item - string, path to file or directory. | ||
// callback - function that will be executed on fs change. | ||
// * item - string, path to file or directory. | ||
// * callback - function that will be executed on fs change. | ||
// Returns nothing. | ||
FSWatcher.prototype._watch = function(item, callback) { | ||
var basename, directory, options, parent, watcher, absolutePath, listener; | ||
if (callback == null) callback = Function.prototype; // empty function | ||
directory = sysPath.dirname(item); | ||
basename = sysPath.basename(item); | ||
parent = this._getWatchedDir(directory); | ||
absolutePath = sysPath.resolve(item); | ||
var directory = sysPath.dirname(item); | ||
var basename = sysPath.basename(item); | ||
var parent = this._getWatchedDir(directory); | ||
if (parent.indexOf(basename) !== -1) return; | ||
var absolutePath = sysPath.resolve(item); | ||
var options = {persistent: this.options.persistent}; | ||
this._addToWatchedDir(directory, basename); | ||
options = {persistent: this.options.persistent}; | ||
if (!callback) callback = Function.prototype; // empty function | ||
@@ -259,4 +318,5 @@ if (this.options.usePolling) { | ||
this.options.binaryInterval : this.options.interval; | ||
listener = this.listeners[absolutePath] = function(curr, prev) { | ||
if (curr.mtime.getTime() > prev.mtime.getTime()) { | ||
var listener = this.listeners[absolutePath] = function(curr, prev) { | ||
var currmtime = curr.mtime.getTime(); | ||
if (currmtime > prev.mtime.getTime() || currmtime === 0) { | ||
callback(item, curr); | ||
@@ -267,25 +327,15 @@ } | ||
} else { | ||
watcher = fs.watch(item, options, function(event, path) { | ||
if (!isWindows) { | ||
return callback(item); | ||
var watcher = fs.watch(item, options, function(event, path) { | ||
callback(item); | ||
}); | ||
var _handleError = this._handleError; | ||
watcher.on('error', function(error) { | ||
// Workaround for https://github.com/joyent/node/issues/4337 | ||
if (isWin32 && error.code === 'EPERM') { | ||
fs.exists(item, function(exists) { | ||
if (exists) _handleError(error); | ||
}); | ||
} else { | ||
_handleError(error); | ||
} | ||
if (!path) { | ||
return callback(item); | ||
} | ||
var self = this; | ||
// Ignore the event if it's currently being throttled | ||
if (!self.throttling) { | ||
self.throttling = {}; | ||
} | ||
if (self.throttling[path]) { | ||
return; | ||
} | ||
self.throttling[path] = true; | ||
setTimeout(function() { | ||
delete self.throttling[path]; | ||
callback(item); | ||
}, 0); | ||
}); | ||
@@ -296,34 +346,29 @@ this.watchers.push(watcher); | ||
// Workaround for the "Windows rough edge" regarding the deletion of directories | ||
// (https://github.com/joyent/node/issues/4337) | ||
FSWatcher.prototype._emitError = function(error) { | ||
var emit = (function() { | ||
this.emit('error', error); | ||
}).bind(this); | ||
if (isWindows && error.code === 'EPERM') { | ||
fs.exists(item, function(exists) { | ||
if (exists) emit(); | ||
}); | ||
} else { | ||
emit(); | ||
} | ||
}; | ||
// Private: Emit `change` event once and watch file to emit it in the future | ||
// once the file is changed. | ||
// file - string, fs path. | ||
// stats - object, result of executing stat(1) on file. | ||
// initialAdd - boolean, was the file added at the launch? | ||
// * file - string, fs path. | ||
// * stats - object, result of fs.stat | ||
// * initialAdd - boolean, was the file added at watch instantiation? | ||
// Returns nothing. | ||
FSWatcher.prototype._handleFile = function(file, stats, initialAdd) { | ||
var _this = this; | ||
if (initialAdd == null) initialAdd = false; | ||
this._watch(file, function(file, newStats) { | ||
return _this.emit('change', file, newStats); | ||
}); | ||
if (!this._throttle('watch', file, 5)) return; | ||
if (newStats && newStats.mtime.getTime() === 0) { | ||
fs.exists(file, function(exists) { | ||
// Fix issues where mtime is null but file is still present | ||
if (!exists) { | ||
this._remove(sysPath.dirname(file), sysPath.basename(file)); | ||
} else { | ||
this._emit('change', file, newStats); | ||
} | ||
}.bind(this)); | ||
} else { | ||
this._emit('change', file, newStats); | ||
} | ||
}.bind(this)); | ||
if (!(initialAdd && this.options.ignoreInitial)) { | ||
return this.emit('add', file, stats); | ||
if (!this._throttle('add', file, 0)) return; | ||
this._emit('add', file, stats); | ||
} | ||
@@ -335,14 +380,18 @@ }; | ||
// directory - string, fs path. | ||
// * directory - string, fs path. | ||
// * stats - object, result of fs.stat | ||
// * initialAdd - boolean, was the file added at watch instantiation? | ||
// Returns nothing. | ||
FSWatcher.prototype._handleDir = function(directory, stats, initialAdd) { | ||
var _this = this; | ||
var read = function(directory, initialAdd) { | ||
return fs.readdir(directory, function(error, current) { | ||
if (error != null) return _this._emitError(error); | ||
if (!current) return; | ||
var read = function read(directory, initialAdd) { | ||
var throttler = this._throttle('readdir', directory, 1000); | ||
if (!throttler) return; | ||
fs.readdir(directory, function(error, current) { | ||
throttler.clear(); | ||
if (this._handleError(error) || !current) return; | ||
// Normalize the directory name on Windows | ||
directory = sysPath.join(directory, ''); | ||
var previous = this._getWatchedDir(directory); | ||
var previous = _this._getWatchedDir(directory); | ||
// Files that absent in current directory snapshot | ||
@@ -352,6 +401,6 @@ // but present in previous emit `remove` event | ||
previous.filter(function(file) { | ||
return current.indexOf(file) === -1; | ||
return file !== directory && current.indexOf(file) === -1; | ||
}).forEach(function(file) { | ||
return _this._remove(directory, file); | ||
}); | ||
this._remove(directory, file); | ||
}, this); | ||
@@ -364,12 +413,15 @@ // Files that present in current directory snapshot | ||
}).forEach(function(file) { | ||
_this._handle(sysPath.join(directory, file), initialAdd); | ||
}); | ||
}); | ||
}; | ||
this._handle(sysPath.join(directory, file), initialAdd); | ||
}, this); | ||
}.bind(this)); | ||
}.bind(this); | ||
read(directory, initialAdd); | ||
this._watch(directory, function(dir) { | ||
return read(dir, false); | ||
this._watch(directory, function(dir, stats) { | ||
// Current directory is removed, do nothing | ||
if (stats && stats.mtime.getTime() === 0) return; | ||
read(dir, false); | ||
}); | ||
if (!(initialAdd && this.options.ignoreInitial)) { | ||
return this.emit('addDir', directory, stats); | ||
this._emit('addDir', directory, stats); | ||
} | ||
@@ -381,66 +433,51 @@ }; | ||
// item - string, path to file or directory. | ||
// * item - string, path to file or directory. | ||
// * initialAdd - boolean, was the file added at watch instantiation? | ||
// Returns nothing. | ||
FSWatcher.prototype._handle = function(item, initialAdd) { | ||
var _this = this; | ||
if (this._isIgnored(item)) return; | ||
if (_this.closed) return; | ||
if (this._isIgnored(item) || this.closed) return; | ||
return fs.realpath(item, function(error, path) { | ||
if (_this.closed) return; | ||
if (error && error.code === 'ENOENT') return; | ||
if (error != null) return _this._emitError(error); | ||
fs.realpath(item, function(error, path) { | ||
if (this._handleError(error)) return; | ||
fs.stat(path, function(error, stats) { | ||
if (_this.closed) return; | ||
if (error && error.code === 'ENOENT') return; | ||
if (error != null) return _this._emitError(error); | ||
if (_this.options.ignorePermissionErrors && (!_this._hasReadPermissions(stats))) { | ||
return; | ||
if (this._handleError(error)) return; | ||
if (( | ||
this.options.ignorePermissionErrors && | ||
!this._hasReadPermissions(stats) | ||
) || ( | ||
this._isIgnored.length === 2 && | ||
this._isIgnored(item, stats) | ||
)) return; | ||
if (stats.isFile() || stats.isCharacterDevice()) { | ||
this._handleFile(item, stats, initialAdd); | ||
} else if (stats.isDirectory()) { | ||
this._handleDir(item, stats, initialAdd); | ||
} | ||
if (_this._isIgnored.length === 2 && _this._isIgnored(item, stats)) { | ||
return; | ||
} | ||
if (stats.isFile() || stats.isCharacterDevice()) _this._handleFile(item, stats, initialAdd); | ||
if (stats.isDirectory()) _this._handleDir(item, stats, initialAdd); | ||
}); | ||
}); | ||
}.bind(this)); | ||
}.bind(this)); | ||
}; | ||
FSWatcher.prototype.emit = function(event, arg1) { | ||
var data = arguments.length === 2 ? [arg1] : [].slice.call(arguments, 1); | ||
var args = [event].concat(data); | ||
EventEmitter.prototype.emit.apply(this, args); | ||
if (event === 'add' || event === 'addDir' || event === 'change' || | ||
event === 'unlink' || event === 'unlinkDir') { | ||
EventEmitter.prototype.emit.apply(this, ['all'].concat(args)); | ||
FSWatcher.prototype._addToFsEvents = function(file) { | ||
var emitAdd = function(path, stats) { | ||
this._addToWatchedDir(sysPath.dirname(path), sysPath.basename(path)); | ||
this._emit(stats.isDirectory() ? 'addDir' : 'add', path, stats); | ||
}.bind(this); | ||
if (!this.options.ignoreInitial) { | ||
fs.stat(file, function(error, stats) { | ||
if (this._handleError(error)) return; | ||
if (stats.isDirectory()) { | ||
this._emit('addDir', file, stats); | ||
readdirp({root: file, entryType: 'both'}) | ||
.on('data', function(entry) { | ||
if (this._isIgnored(entry.path)) return; | ||
emitAdd(sysPath.join(file, entry.path), entry.stat); | ||
}.bind(this)); | ||
} else { | ||
emitAdd(file, stats); | ||
} | ||
}.bind(this)); | ||
} | ||
}; | ||
FSWatcher.prototype._addToFsEvents = function(files) { | ||
var _this = this; | ||
var handle = function(path) { | ||
return _this.emit('add', path); | ||
}; | ||
files.forEach(function(file) { | ||
if (!_this.options.ignoreInitial) { | ||
fs.stat(file, function(error, stats) { | ||
if (error != null) return _this._emitError(error); | ||
if (stats.isDirectory()) { | ||
recursiveReaddir(file, function(error, dirFiles) { | ||
if (error != null) return _this._emitError(error); | ||
dirFiles | ||
.filter(function(path) { | ||
return !_this._isIgnored(path); | ||
}) | ||
.forEach(handle); | ||
}); | ||
} else { | ||
handle(file); | ||
} | ||
}); | ||
} | ||
_this._watchWithFsEvents(file); | ||
}); | ||
this._watchWithFsEvents(file); | ||
return this; | ||
@@ -451,18 +488,17 @@ }; | ||
// * files - array of strings (file paths). | ||
// * files - array of strings (file or directory paths). | ||
// Examples | ||
// add ['app', 'vendor'] | ||
// Returns an instance of FSWatcher for chaning. | ||
// Returns an instance of FSWatcher for chaining. | ||
FSWatcher.prototype.add = function(files) { | ||
if (this._initialAdd == null) this._initialAdd = true; | ||
if (!('_initialAdd' in this)) this._initialAdd = true; | ||
if (!Array.isArray(files)) files = [files]; | ||
if (this.options.useFsEvents) return this._addToFsEvents(files); | ||
files.forEach(function(file) { | ||
return this._handle(file, this._initialAdd); | ||
if (this.options.useFsEvents) { | ||
this._addToFsEvents(file); | ||
} else { | ||
this._handle(file, this._initialAdd); | ||
} | ||
}, this); | ||
this._initialAdd = false; | ||
@@ -473,9 +509,8 @@ return this; | ||
// Public: Remove all listeners from watched files. | ||
// Returns an instance of FSWatcher for chaning. | ||
// Returns an instance of FSWatcher for chaining. | ||
FSWatcher.prototype.close = function() { | ||
if (this.closed) return this; | ||
var listeners = this.listeners; | ||
if(this.closed) { | ||
return this; | ||
} | ||
var watched = this.watched; | ||
var useFsEvents = this.options.useFsEvents; | ||
@@ -490,6 +525,5 @@ var method = useFsEvents ? 'stop' : 'close'; | ||
if (this.options.usePolling) { | ||
var watched = this.watched; | ||
Object.keys(watched).forEach(function(directory) { | ||
return watched[directory].forEach(function(file) { | ||
var absolutePath = sysPath.resolve(directory, file) | ||
watched[directory].forEach(function(file) { | ||
var absolutePath = sysPath.resolve(directory, file); | ||
fs.unwatchFile(absolutePath, listeners[absolutePath]); | ||
@@ -496,0 +530,0 @@ delete listeners[absolutePath]; |
{ | ||
"name": "chokidar", | ||
"description": "A neat wrapper around node.js fs.watch / fs.watchFile.", | ||
"version": "0.9.0", | ||
"description": "A neat wrapper around node.js fs.watch / fs.watchFile / fsevents.", | ||
"version": "0.10.0", | ||
"keywords": [ | ||
@@ -11,3 +11,4 @@ "fs", | ||
"watching", | ||
"file" | ||
"file", | ||
"fsevents" | ||
], | ||
@@ -30,4 +31,5 @@ "homepage": "https://github.com/paulmillr/chokidar", | ||
"scripts": { | ||
"test": "./node_modules/.bin/mocha" | ||
"test": "mocha" | ||
}, | ||
"files": ["index.js"], | ||
"devDependencies": { | ||
@@ -43,4 +45,4 @@ "mocha": "~1.7.3", | ||
"fsevents": "0.3.0", | ||
"recursive-readdir": "0.0.2" | ||
"readdirp": "~1.1.0" | ||
} | ||
} |
@@ -97,4 +97,4 @@ # Chokidar | ||
* `.on(event, callback)`: Listen for an FS event. | ||
Available events: `add`, `change`, `unlink`, `error`. | ||
Additionally `all` is available which gets emitted for every `add`, `change` and `unlink`. | ||
Available events: `add`, `addDir`, `change`, `unlink`, `unlinkDir`, `error`. | ||
Additionally `all` is available which gets emitted for every non-`error` event. | ||
* `.close()`: Removes all listeners from watched files. | ||
@@ -105,3 +105,3 @@ | ||
Copyright (c) 2013 Paul Miller (http://paulmillr.com) | ||
Copyright (c) 2014 Paul Miller (http://paulmillr.com) | ||
@@ -108,0 +108,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2
23490
3
454
2