Comparing version 2.2.1 to 2.3.0
## History | ||
- v2.3.0 December 17, 2012 | ||
- This is a backwards compatiblity break, however updating is easy, read the notes below. | ||
- We've updated the events we emit to be: | ||
- `log` for debugging, receives the arguments `logLevel ,args...` | ||
- `watching` for when watching of the path has completed, receives the arguments `err, isWatching` | ||
- `change` for listening to change events, receives the arguments `changeType, fullPath, currentStat, previousStat` | ||
- `error` for gracefully listening to error events, receives the arguments `err` | ||
- read the README to learn how to bind to these new events | ||
- The `changeType` argument for change listeners has been changed for better clarity and consitency: | ||
- `change` is now `update` | ||
- `new` is now `create` | ||
- `unlink` is now `delete` | ||
- We've updated the return arguments for `require('watchr).watch` for better consitency: | ||
- if you send the `paths` option, you will receive the arguments `err, results` where `results` is an array of watcher instances | ||
- if you send the `path` option, you receive the arguments `err, watcherInstance` | ||
- v2.2.1 December 16, 2012 | ||
@@ -4,0 +20,0 @@ - Fixed sub directory scans ignoring our ignore patterns |
@@ -17,13 +17,27 @@ // Generated by CoffeeScript 1.4.0 | ||
path: watchPath, | ||
listener: function() { | ||
listeners: { | ||
log: function() { | ||
var args; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
return console.log('a log message occured:', args); | ||
}, | ||
error: function(err) { | ||
return console.log('an error occured:', err); | ||
}, | ||
watching: function() { | ||
var args; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
return console.log('a new watcher instance finished setting up', args); | ||
}, | ||
change: function() { | ||
var args; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
return console.log('a change event occured:', args); | ||
} | ||
}, | ||
next: function() { | ||
var args; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
return console.log('a watch event occured:', ++changes, ':', args); | ||
}, | ||
next: function(err, watcher) { | ||
if (err) { | ||
throw err; | ||
} | ||
return console.log('now watching:', watchPath); | ||
return console.log('watching for all our paths has completed', args); | ||
} | ||
}); |
// Generated by CoffeeScript 1.4.0 | ||
/* | ||
Watchr is used to be nofitied when a change happens to, or within a directory. | ||
You will not be notified what file was changed, or how it was changed. | ||
It will track new files and their changes too, and remove listeners for deleted files appropriatly. | ||
The source code here is written as an experiment of literate programming | ||
Which means you would be able to understand it, without knowing code | ||
*/ | ||
var EventEmitter, Watcher, balUtil, createWatcher, debug, fsUtil, pathUtil, watch, watchers, | ||
var EventEmitter, Watcher, balUtil, createWatcher, fsUtil, pathUtil, watch, watchers, | ||
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, | ||
__hasProp = {}.hasOwnProperty, | ||
@@ -25,4 +17,15 @@ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, | ||
debug = false; | ||
/* | ||
Now to make watching files more convient and managed, we'll create a class which we can use to attach to each file. | ||
It'll provide us with the API and abstraction we need to accomplish difficult things like recursion. | ||
We'll also store a global store of all the watchers and their paths so we don't have multiple watchers going at the same time | ||
for the same file - as that would be quite ineffecient. | ||
Events: | ||
- `log` for debugging, receives the arguments `logLevel ,args...` | ||
- `error` for gracefully listening to error events, receives the arguments `err` | ||
- `watching` for when watching of the path has completed, receives the arguments `err, watcherInstance, isWatching` | ||
- `change` for listening to change events, receives the arguments `changeType, fullPath, currentStat, previousStat` | ||
*/ | ||
watchers = {}; | ||
@@ -51,62 +54,80 @@ | ||
function _Class(config, next) { | ||
var applyStat, listener, watcher, _base, _base1, _base2, _base3, _base4, _i, _len, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6, | ||
_this = this; | ||
_ref = balUtil.extractOptsAndCallback(config, next), config = _ref[0], next = _ref[1]; | ||
watcher = this; | ||
this.bubbler = __bind(this.bubbler, this); | ||
this.bubble = __bind(this.bubble, this); | ||
this.children = {}; | ||
applyStat = function(stat) { | ||
_this.stat = stat; | ||
_this.isDirectory = stat.isDirectory(); | ||
return _this.watch(function(err) { | ||
return typeof next === "function" ? next(err, watcher) : void 0; | ||
}); | ||
}; | ||
this.config = {}; | ||
if (config.next != null) { | ||
if (next == null) { | ||
next = config.next; | ||
} | ||
delete config.next; | ||
} | ||
if (config) { | ||
this.setup(config); | ||
} | ||
if (next) { | ||
this.watch(next); | ||
} | ||
this; | ||
} | ||
_Class.prototype.log = function() { | ||
var args; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
this.emit.apply(this, ['log'].concat(__slice.call(args))); | ||
return this; | ||
}; | ||
/* | ||
Setup our Instance | ||
config = | ||
- `path` a single path to watch | ||
- `listener` (optional, detaults to null) single change listener, forwared to @listen | ||
- `listeners` (optional, defaults to null) multiple event listeners, forwarded to @listen | ||
- `stat` (optional, defaults to `null`) a file stat object to use for the path, instead of fetching a new one | ||
- `ignoreHiddenFiles` (optional, defaults to `false`) whether or not to ignored files which filename starts with a `.` | ||
- `ignoreCommonPatterns` (optional, defaults to `true`) whether or not to ignore common undesirable file patterns (e.g. `.svn`, `.git`, `.DS_Store`, `thumbs.db`, etc) | ||
- `ignoreCustomPatterns` (optional, defaults to `null`) any custom ignore patterns that you would also like to ignore along with the common patterns | ||
- `interval` (optional, defaults to `100`) for systems that poll to detect file changes, how often should it poll in millseconds | ||
- `persistent` (optional, defaults to `true`) whether or not we should keep the node process alive for as long as files are still being watched | ||
*/ | ||
_Class.prototype.setup = function(config) { | ||
var _base, _base1, _base2, _base3, _base4, _ref, _ref1, _ref2, _ref3, _ref4; | ||
this.path = config.path; | ||
this.config = config; | ||
if ((_ref1 = (_base = this.config).ignoreHiddenFiles) == null) { | ||
if ((_ref = (_base = this.config).ignoreHiddenFiles) == null) { | ||
_base.ignoreHiddenFiles = false; | ||
} | ||
if ((_ref2 = (_base1 = this.config).ignoreCommonPatterns) == null) { | ||
if ((_ref1 = (_base1 = this.config).ignoreCommonPatterns) == null) { | ||
_base1.ignoreCommonPatterns = true; | ||
} | ||
if ((_ref3 = (_base2 = this.config).ignoreCustomPatterns) == null) { | ||
if ((_ref2 = (_base2 = this.config).ignoreCustomPatterns) == null) { | ||
_base2.ignoreCustomPatterns = null; | ||
} | ||
if ((_ref4 = (_base3 = this.config).interval) == null) { | ||
if ((_ref3 = (_base3 = this.config).interval) == null) { | ||
_base3.interval = 100; | ||
} | ||
if ((_ref5 = (_base4 = this.config).persistent) == null) { | ||
if ((_ref4 = (_base4 = this.config).persistent) == null) { | ||
_base4.persistent = true; | ||
} | ||
if (config.listener) { | ||
this.listen(config.listener); | ||
if (this.config.stat) { | ||
this.stat = this.config.stat; | ||
this.isDirectory = this.stat.isDirectory(); | ||
delete this.config.stat; | ||
} | ||
if (config.listeners) { | ||
_ref6 = config.listeners; | ||
for (_i = 0, _len = _ref6.length; _i < _len; _i++) { | ||
listener = _ref6[_i]; | ||
this.listen(listener); | ||
if (this.config.listener || this.config.listeners) { | ||
this.removeAllListeners(); | ||
if (this.config.listener) { | ||
this.listen(this.config.listener); | ||
delete this.config.listener; | ||
} | ||
if (this.config.listeners) { | ||
this.listen(this.config.listeners); | ||
delete this.config.listeners; | ||
} | ||
} | ||
if (config.stat) { | ||
applyStat(config.stat); | ||
} else { | ||
balUtil.stat(config.path, function(err, stat) { | ||
if (watcher.state !== 'pending') { | ||
return; | ||
} | ||
if (err) { | ||
throw err; | ||
} | ||
return applyStat(stat); | ||
}); | ||
} | ||
} | ||
_Class.prototype.listen = function(listener) { | ||
this.removeListener('changed', listener); | ||
this.on('changed', listener); | ||
if (debug) { | ||
console.log("added a listener: on " + this.path); | ||
} | ||
return this; | ||
@@ -116,13 +137,91 @@ }; | ||
_Class.prototype.bubble = function() { | ||
var args, currentStat, eventName, filename, previousStat; | ||
var args; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
eventName = args[0], filename = args[1], currentStat = args[2], previousStat = args[3]; | ||
if (debug) { | ||
console.log("bubble: " + eventName + ": " + filename + " on " + this.path); | ||
this.log('debug', "bubble on " + this.path + " with the args:", args); | ||
this.emit.apply(this, args); | ||
return this; | ||
}; | ||
_Class.prototype.bubbler = function(eventName) { | ||
var _this = this; | ||
return function() { | ||
var args; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
return _this.bubble.apply(_this, args); | ||
}; | ||
}; | ||
/* | ||
Listen | ||
Add listeners to our watcher instance. | ||
Overloaded to also accept the following: | ||
- `changeListener` a single change listener | ||
- `[changeListener]` an array of change listeners | ||
- `{eventName:eventListener}` an object keyed with the event names and valued with a single event listener | ||
- `{eventName:[eventListener]}` an object keyed with the event names and valued with an array of event listeners | ||
*/ | ||
_Class.prototype.listen = function(eventName, listener) { | ||
var listenerArray, listeners, _i, _j, _len, _len1; | ||
if (listener == null) { | ||
listeners = eventName; | ||
if (balUtil.isArray(listeners)) { | ||
for (_i = 0, _len = listeners.length; _i < _len; _i++) { | ||
listener = listeners[_i]; | ||
this.listen('change', listener); | ||
} | ||
} else if (balUtil.isPlainObject(listeners)) { | ||
for (eventName in listeners) { | ||
if (!__hasProp.call(listeners, eventName)) continue; | ||
listenerArray = listeners[eventName]; | ||
if (balUtil.isArray(listenerArray)) { | ||
for (_j = 0, _len1 = listenerArray.length; _j < _len1; _j++) { | ||
listener = listenerArray[_j]; | ||
this.listen(eventName, listener); | ||
} | ||
} else { | ||
this.listen(eventName, listenerArray); | ||
} | ||
} | ||
} else { | ||
this.listen('change', listeners); | ||
} | ||
} else { | ||
this.removeListener(eventName, listener); | ||
this.on(eventName, listener); | ||
this.log('debug', "added a listener: on " + this.path + " for event " + eventName); | ||
} | ||
this.emit('changed', eventName, filename, currentStat, previousStat); | ||
return this; | ||
}; | ||
_Class.prototype.changed = function() { | ||
/* | ||
Listener | ||
A change event has fired | ||
Things to note: | ||
- watchFile: | ||
- currentStat still exists even for deleted/renamed files | ||
- for deleted and updated files, it will fire on the file | ||
- for created files, it will fire on the directory | ||
- fsWatcher: | ||
- eventName is always 'change' | ||
- 'rename' is not yet implemented by node | ||
- currentStat still exists even for deleted/renamed files | ||
- previousStat is accurate, however we already have this | ||
- for deleted and changed files, it will fire on the file | ||
- for new files, it will fire on the directory | ||
Arguments for our change listener will be: | ||
- for updated files the arguments will be: `'update', fullPath, currentStat, previousStat` | ||
- for created files the arguments will be: `'create', fullPath, currentStat, null` | ||
- for deleted files the arguments will be: `'delete', fullPath, null, previousStat` | ||
In the future we will add: | ||
- for renamed files: 'rename', fullPath, currentStat, previousStat, newFullPath | ||
- rename is possible as the stat.ino is the same for the delete and create | ||
*/ | ||
_Class.prototype.listener = function() { | ||
var args, currentStat, determineTheChange, fileExists, fileFullPath, isTheSame, me, previousStat, | ||
@@ -136,5 +235,3 @@ _this = this; | ||
fileExists = null; | ||
if (debug) { | ||
console.log("watch event triggered on " + this.path + "\n", args); | ||
} | ||
this.log('debug', "watch event triggered on " + this.path + "\n", args); | ||
isTheSame = function() { | ||
@@ -150,11 +247,7 @@ if ((currentStat != null) && (previousStat != null)) { | ||
if (!fileExists) { | ||
if (debug) { | ||
console.log('determined unlink:', fileFullPath); | ||
} | ||
return _this.close('unlink'); | ||
_this.log('debug', 'determined delete:', fileFullPath); | ||
return _this.close('deleted'); | ||
} else { | ||
if (isTheSame()) { | ||
if (debug) { | ||
return console.log("determined same:", fileFullPath); | ||
} | ||
return _this.log('debug', "determined same:", fileFullPath); | ||
} else { | ||
@@ -165,3 +258,3 @@ if (_this.isDirectory) { | ||
if (err) { | ||
throw err; | ||
return _this.emit('error', err); | ||
} | ||
@@ -176,8 +269,6 @@ balUtil.each(newFileRelativePaths, function(newFileRelativePath) { | ||
if (err) { | ||
throw err; | ||
return _this.emit('error', err); | ||
} | ||
if (debug) { | ||
console.log('determined new:', newFileFullPath); | ||
} | ||
_this.emit('changed', 'new', newFileFullPath, newFileStat, null); | ||
_this.log('debug', 'determined create:', newFileFullPath); | ||
_this.emit('change', 'create', newFileFullPath, newFileStat, null); | ||
return _this.watchChild(newFileFullPath, newFileRelativePath, newFileStat); | ||
@@ -193,6 +284,4 @@ }); | ||
childFileFullPath = childFileWatcher.path; | ||
if (debug) { | ||
console.log('determined unlink:', childFileRelativePath); | ||
} | ||
return _this.closeChild(childFileRelativePath, 'unlink'); | ||
_this.log('debug', 'determined delete:', childFileRelativePath); | ||
return _this.closeChild(childFileRelativePath, 'deleted'); | ||
} | ||
@@ -203,6 +292,4 @@ }); | ||
} else { | ||
if (debug) { | ||
console.log('determined change:', fileFullPath); | ||
} | ||
return _this.emit('changed', 'change', fileFullPath, currentStat, previousStat); | ||
_this.log('debug', 'determined update:', fileFullPath); | ||
return _this.emit('change', 'update', fileFullPath, currentStat, previousStat); | ||
} | ||
@@ -217,3 +304,3 @@ } | ||
if (err) { | ||
throw err; | ||
return this.emit('error', err); | ||
} | ||
@@ -231,14 +318,19 @@ currentStat = stat; | ||
_Class.prototype.close = function(type) { | ||
var childRelativePath, watchr, _ref; | ||
/* | ||
Close | ||
We will need something to close our listener for removed or renamed files | ||
As renamed files are a bit difficult we will want to close and delete all the watchers for all our children too | ||
Essentially it is a self-destruct | ||
*/ | ||
_Class.prototype.close = function(reason) { | ||
var childRelativePath, _ref; | ||
if (this.state !== 'active') { | ||
return this; | ||
} | ||
if (debug) { | ||
console.log("close: " + this.path + " ", new Error('trace').stack); | ||
} | ||
this.log('debug', "close: " + this.path, (new Error()).stack); | ||
_ref = this.children; | ||
for (childRelativePath in _ref) { | ||
if (!__hasProp.call(_ref, childRelativePath)) continue; | ||
watchr = _ref[childRelativePath]; | ||
this.closeChild(childRelativePath, type); | ||
@@ -252,5 +344,5 @@ } | ||
} | ||
if (type === 'unlink') { | ||
this.emit('changed', 'unlink', this.path, null, this.stat); | ||
this.state = 'unlink'; | ||
if (reason === 'deleted') { | ||
this.state = 'deleted'; | ||
this.emit('change', 'delete', this.path, null, this.stat); | ||
} else { | ||
@@ -265,8 +357,8 @@ this.state = 'closed'; | ||
_Class.prototype.closeChild = function(fileRelativePath, type) { | ||
_Class.prototype.closeChild = function(fileRelativePath, reason) { | ||
var watcher; | ||
watcher = this.children[fileRelativePath]; | ||
if (watcher) { | ||
watcher.close(type); | ||
delete this.children[fileRelativePath]; | ||
watcher.close(reason); | ||
} | ||
@@ -276,16 +368,17 @@ return this; | ||
/* | ||
Watch Child | ||
Setup watching for a child | ||
Bubble events of the child into our instance | ||
Also instantiate the child with our instance's configuration where applicable | ||
*/ | ||
_Class.prototype.watchChild = function(fileFullPath, fileRelativePath, fileStat, next) { | ||
var config, me; | ||
var config, me, watcher, | ||
_this = this; | ||
me = this; | ||
config = this.config; | ||
return watch({ | ||
watcher = watch({ | ||
path: fileFullPath, | ||
listener: function() { | ||
var args; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
if (args.length > 3 && args[0] === 'changed' && args[1] === 'unlink' && args[2] === fileFullPath) { | ||
this.closeChild(fileRelativePath, 'unlink'); | ||
} | ||
return me.bubble.apply(me, args); | ||
}, | ||
stat: fileStat, | ||
@@ -295,3 +388,18 @@ ignoreHiddenFiles: config.ignoreHiddenFiles, | ||
ignoreCustomPatterns: config.ignoreCustomPatterns, | ||
next: function(err, watcher) { | ||
listeners: { | ||
'change': function() { | ||
var args, changeType, path; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
changeType = args[0], path = args[1]; | ||
if (changeType === 'delete' && path === fileFullPath) { | ||
_this.closeChild(fileRelativePath, 'deleted'); | ||
} | ||
return me.bubble.apply(me, ['change'].concat(__slice.call(args))); | ||
}, | ||
'error': me.bubbler('error') | ||
}, | ||
next: function() { | ||
var args, err; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
err = args[0]; | ||
if (err) { | ||
@@ -301,7 +409,19 @@ return typeof next === "function" ? next(err) : void 0; | ||
me.children[fileRelativePath] = watcher; | ||
return typeof next === "function" ? next() : void 0; | ||
return typeof next === "function" ? next.apply(null, args) : void 0; | ||
} | ||
}); | ||
return watcher; | ||
}; | ||
/* | ||
Watch | ||
Setup the native watching handlers for our path so we can receive updates on when things happen | ||
If the next argument has been received, then add it is a once listener for the watching event | ||
If we are already watching this path then let's start again (call close) | ||
If we are a directory, let's recurse | ||
If we are deleted, then don't error but return the isWatching argument of our completion callback as false | ||
Once watching has completed for this directory and all children, then emit the watching event | ||
*/ | ||
_Class.prototype.watch = function(next) { | ||
@@ -312,10 +432,25 @@ var config, me, startWatching, | ||
config = this.config; | ||
if ((this.stat != null) === false) { | ||
balUtil.stat(config.path, function(err, stat) { | ||
if (err) { | ||
return _this.emit('error', err); | ||
} | ||
_this.stat = stat; | ||
_this.isDirectory = stat.isDirectory(); | ||
return _this.watch(next); | ||
}); | ||
return this; | ||
} | ||
if (next != null) { | ||
this.listen('watching', next); | ||
} | ||
this.close(); | ||
if (debug) { | ||
console.log("watch: " + this.path, new Error('trace').stack); | ||
} | ||
this.log('debug', "watch: " + this.path); | ||
startWatching = function() { | ||
var tasks, watchFileOpts; | ||
tasks = new balUtil.Group(function(err) { | ||
return typeof next === "function" ? next(err) : void 0; | ||
if (err) { | ||
return _this.emit('watching', err, _this, false); | ||
} | ||
return _this.emit('watching', err, _this, true); | ||
}); | ||
@@ -350,3 +485,3 @@ tasks.total = 2; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
return me.changed.apply(me, args); | ||
return me.listener.apply(me, args); | ||
}); | ||
@@ -358,3 +493,3 @@ _this.method = 'watchFile'; | ||
args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; | ||
return me.changed.apply(me, args); | ||
return me.listener.apply(me, args); | ||
}); | ||
@@ -368,4 +503,3 @@ _this.method = 'watch'; | ||
if (!exists) { | ||
next(); | ||
return this; | ||
return _this.emit('watching', null, _this, false); | ||
} | ||
@@ -381,10 +515,25 @@ return startWatching(); | ||
/* | ||
Create Watcher | ||
Checks to see if the path actually exists, if it doesn't then exit gracefully | ||
If it does exist, then lets check our cache for an already existing watcher instance | ||
If we have an already existing watching instance, then just add our listeners to that | ||
If we don't, then create a watching instance | ||
Fire the next callback once done | ||
next(err,watcherInstance) | ||
*/ | ||
createWatcher = function(opts, next) { | ||
var listener, listeners, path, watcher, watchr, _i, _len, _listener, _ref; | ||
_ref = balUtil.extractOptsAndCallback(opts, next), opts = _ref[0], next = _ref[1]; | ||
var listener, listeners, path, watcher; | ||
path = opts.path, listener = opts.listener, listeners = opts.listeners; | ||
watchr = null; | ||
if (opts.next != null) { | ||
if (next == null) { | ||
next = opts.next; | ||
} | ||
delete opts.next; | ||
} | ||
if (!balUtil.existsSync(path)) { | ||
if (typeof next === "function") { | ||
next(null, watcher); | ||
next(null, null); | ||
} | ||
@@ -399,6 +548,3 @@ return; | ||
if (listeners) { | ||
for (_i = 0, _len = listeners.length; _i < _len; _i++) { | ||
_listener = listeners[_i]; | ||
watcher.listen(_listener); | ||
} | ||
watcher.listen(listeners); | ||
} | ||
@@ -409,3 +555,5 @@ if (typeof next === "function") { | ||
} else { | ||
watcher = new Watcher(opts); | ||
watcher = new Watcher(opts, function(err) { | ||
return typeof next === "function" ? next(err, watcher) : void 0; | ||
}); | ||
watchers[path] = watcher; | ||
@@ -416,29 +564,49 @@ } | ||
/* | ||
Watch | ||
Provides an abstracted API that supports multiple paths | ||
If you are passing in multiple paths then do not rely on the return result containing all of the watchers | ||
you must rely on the result inside the completion callback instead | ||
If you used the paths option, then your results will be an array of watcher instances, otherwise they will be a single watcher instance | ||
next(err,results) | ||
*/ | ||
watch = function(opts, next) { | ||
var path, paths, result, tasks, _i, _len, _ref; | ||
_ref = balUtil.extractOptsAndCallback(opts, next), opts = _ref[0], next = _ref[1]; | ||
paths = opts.paths; | ||
result = null; | ||
delete opts.paths; | ||
if (paths instanceof Array) { | ||
result = []; | ||
tasks = new balUtil.Group(function(err) { | ||
return typeof next === "function" ? next(err, result) : void 0; | ||
}); | ||
for (_i = 0, _len = paths.length; _i < _len; _i++) { | ||
path = paths[_i]; | ||
tasks.push({ | ||
path: path | ||
}, function(complete) { | ||
var localOpts, watchr; | ||
localOpts = balUtil.extend({}, opts); | ||
localOpts.path = this.path; | ||
localOpts.next = complete; | ||
watchr = createWatcher(localOpts); | ||
if (watchr) { | ||
return result.push(watchr); | ||
} | ||
var path, paths, result, tasks, _i, _len; | ||
result = []; | ||
if (opts.next != null) { | ||
if (next == null) { | ||
next = opts.next; | ||
} | ||
delete opts.next; | ||
} | ||
if (opts.paths) { | ||
paths = opts.paths; | ||
delete opts.paths; | ||
if (balUtil.isArray(paths)) { | ||
tasks = new balUtil.Group(function(err) { | ||
return typeof next === "function" ? next(err, result) : void 0; | ||
}); | ||
for (_i = 0, _len = paths.length; _i < _len; _i++) { | ||
path = paths[_i]; | ||
tasks.push({ | ||
path: path | ||
}, function(complete) { | ||
var localOpts, watcher; | ||
localOpts = balUtil.extend({}, opts); | ||
localOpts.path = this.path; | ||
watcher = createWatcher(localOpts, complete); | ||
if (watcher) { | ||
return result.push(watcher); | ||
} | ||
}); | ||
} | ||
tasks.async(); | ||
} else { | ||
opts.path = paths; | ||
result.push(createWatcher(opts, function(err) { | ||
return typeof next === "function" ? next(err, result) : void 0; | ||
})); | ||
} | ||
tasks.async(); | ||
} else { | ||
@@ -445,0 +613,0 @@ result = createWatcher(opts, next); |
{ | ||
"name": "watchr", | ||
"version": "2.2.1", | ||
"version": "2.3.0", | ||
"description": "Better file system watching for Node.js", | ||
@@ -5,0 +5,0 @@ "homepage": "https://github.com/bevry/watchr", |
@@ -1,12 +0,18 @@ | ||
## Watchr; better file system watching for Node.js | ||
## Watchr: better file system watching for Node.js | ||
Watchr provides a normalised API the file watching APIs of different node versions, nested/recursive file and directory watching, and accurate detailed events for file/directory changes, deletions and creations. | ||
Watchr provides a normalised API the file watching APIs of different node versions, nested/recursive file and directory watching, and accurate detailed events for file/directory creations, updates, and deletions. | ||
You install it via `npm istall watchr` and use it via `require('watchr').watch(config)`. Available configuration options are: | ||
You install it via `npm install watchr` and use it via `require('watchr').watch(config)`. Available configuration options are: | ||
- `path` a single path to watch | ||
- `paths` an array of paths to watch | ||
- `listener` a single listener to fire when a change occurs | ||
- `listeners` an array of listeners to fire when a change occurs | ||
- `next` (optional, defaults to `null`) a completion callback to fire once the watchers have been setup | ||
- `listener` a single change listener to fire when a change occurs | ||
- `listeners` an array of listeners to fire when a change occurs, overloaded to also accept | ||
- `changeListener` a single change listener | ||
- `[changeListener]` an array of change listeners | ||
- `{eventName:eventListener}` an object keyed with the event names and valued with a single event listener | ||
- `{eventName:[eventListener]}` an object keyed with the event names and valued with an array of event listeners | ||
- `next` (optional, defaults to `null`) a completion callback to fire once the watchers have been setup, arguments are: | ||
- when using the `path` configuration option: `err, watcherInstance` | ||
- when using the `paths` configuration option: `err, [watcherInstance,...]` | ||
- `stat` (optional, defaults to `null`) a file stat object to use for the path, instead of fetching a new one | ||
@@ -19,9 +25,12 @@ - `ignoreHiddenFiles` (optional, defaults to `false`) whether or not to ignored files which filename starts with a `.` | ||
Listeners will be triggered whenever a change is made on the directory or for anything inside it (including sub-directories and so on) and are in the following format `var listener = function(eventName,filePath,fileCurrentStat,filePreviousStat){}` | ||
There are three types of events for your listeners at your disposal: | ||
The following events are available to your via the listeners: | ||
- `change`: a file has been modified | ||
- `new`: a new file or directory has been created | ||
- `unlink`: a file or a directory has been removed | ||
- `log` for debugging, receives the arguments `logLevel ,args...` | ||
- `error` for gracefully listening to error events, receives the arguments `err` | ||
- `watching` for when watching of the path has completed, receives the arguments `err, isWatching` | ||
- `change` for listening to change events, receives the arguments `changeType, fullPath, currentStat, previousStat`, received arguments will be: | ||
- for updated files: `'update', fullPath, currentStat, previousStat` | ||
- for created files: `'create', fullPath, currentStat, null` | ||
- for deleted files: `'delete', fullPath, null, previousStat` | ||
@@ -36,9 +45,19 @@ To wrap it all together, it would look like this: | ||
watchr.watch({ | ||
path: path, | ||
listener: function(eventName,filePath,fileCurrentStat,filePreviousStat){ | ||
console.log('a watch event occured:',arguments); | ||
paths: ['path1','path2','path3'], | ||
listeners: { | ||
log: function(logLevel){ | ||
console.log('a log message occured:', arguments); | ||
}, | ||
error: function(err){ | ||
console.log('an error occured:', err); | ||
}, | ||
watching: function(err,watcherInstance,isWatching){ | ||
console.log('a new watcher instance finished setting up', arguments); | ||
}, | ||
change: function(changeType,filePath,fileCurrentStat,filePreviousStat){ | ||
console.log('a change event occured:',arguments); | ||
} | ||
}, | ||
next: function(err,watcher){ | ||
if (err) throw err; | ||
console.log('watching setup successfully'); | ||
next: function(err,watchers){ | ||
console.log('watching for all our paths has completed', arguments); | ||
} | ||
@@ -50,5 +69,3 @@ }); | ||
Thanks for using Watchr! | ||
## Support | ||
@@ -68,2 +85,2 @@ | ||
<br/>Copyright © 2012 [Bevry Pty Ltd](http://bevry.me) | ||
<br/>Copyright © 2011 [Benjamin Lupton](http://balupton.com) | ||
<br/>Copyright © 2011 [Benjamin Lupton](http://balupton.com) |
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
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
31813
577
83