node-watch
Advanced tools
Comparing version 0.3.5 to 0.4.0
138
lib/watch.js
@@ -5,7 +5,8 @@ /** | ||
var fs = require('fs') | ||
, path = require('path'); | ||
, path = require('path') | ||
, events = require('events'); | ||
/** | ||
* Utility functions to synchronously test whether the giving path | ||
* Utility functions to synchronously test whether the giving path | ||
* is a file or a directory or a symbolic link. | ||
@@ -21,5 +22,5 @@ */ | ||
var stat = fs[method === 'sym' ? 'lstatSync' :'statSync']; | ||
ret[method] = function(fpath) { | ||
ret[method] = function(fpath) { | ||
try { | ||
var yes = stat(fpath)['is' + shortcuts[method]](); | ||
var yes = stat(fpath)['is' + shortcuts[method]](); | ||
memo.push(fpath, method); | ||
@@ -41,5 +42,5 @@ return yes; | ||
all && all.forEach(function(f) { | ||
var sdir = path.join(parent, f) | ||
var sdir = path.join(parent, f); | ||
if (is.dir(sdir)) { | ||
cb.call(null, sdir) | ||
cb.call(null, sdir) | ||
} | ||
@@ -53,7 +54,7 @@ }); | ||
/** | ||
* Mixing object properties. | ||
* Mixing object properties. | ||
*/ | ||
var mixin = function() { | ||
var mix = {}; | ||
[].forEach.call(arguments, function(arg) { | ||
var mix = {}; | ||
[].forEach.call(arguments, function(arg) { | ||
for (var name in arg) { | ||
@@ -76,3 +77,3 @@ if (arg.hasOwnProperty(name)) { | ||
memo[name] = type; | ||
}, | ||
}, | ||
has: function(name) { | ||
@@ -90,3 +91,3 @@ return {}.hasOwnProperty.call(memo, name); | ||
/** | ||
@@ -102,3 +103,3 @@ * A Container for storing unique and valid filenames. | ||
each: function() { | ||
var temp = Object.keys(cache).filter(function(name){ | ||
var temp = Object.keys(cache).filter(function(name){ | ||
return is.file(name) || memo.has(name) && memo.update(name); | ||
@@ -114,3 +115,3 @@ }); | ||
}; | ||
}({}); | ||
}({}); | ||
@@ -140,3 +141,3 @@ | ||
*/ | ||
var normalizeCall = function(fname, options, cb) { | ||
var normalizeCall = function(fname, options, cb, watcher) { | ||
// Store each name of the modifying or temporary files generated by an editor. | ||
@@ -146,14 +147,15 @@ fileNameCache.push(fname); | ||
worker.busydoing(function() { | ||
// A heuristic delay of the write-to-file process. | ||
// A heuristic delay of the write-to-file process. | ||
setTimeout(function() { | ||
// When the write-to-file process is done, send all filtered filenames | ||
// to the callback function and call it. | ||
fileNameCache | ||
.each(function(f) { | ||
.each(function(f) { | ||
// Watch new created directory. | ||
if (options.recursive && !memo.has(f) && is.dir(f)) { | ||
watch(f, options, cb); | ||
watch(f, options, cb, watcher); | ||
} | ||
cb.call(null, f); | ||
cb && cb.call(null, f); | ||
watcher.emit('change', f); | ||
}).clear(); | ||
@@ -169,7 +171,27 @@ | ||
/** | ||
* Catch exception on Windows when deleting a directory. | ||
* Watcher class to simulate FSWatcher | ||
*/ | ||
var catchException = function() {}; | ||
var Watcher = function Watcher() { | ||
this.watchers = []; | ||
this.closed = false; | ||
this.close = function() { | ||
this.watchers.forEach(function(watcher) { | ||
watcher.close(); | ||
}); | ||
this.watchers = []; | ||
this.closed = true; | ||
}; | ||
this.addWatcher = function(watcher, cb) { | ||
var self = this; | ||
this.watchers.push(watcher); | ||
watcher.on('error', function(err) { | ||
self.emit('error', err); | ||
}); | ||
}; | ||
}; | ||
Watcher.prototype.__proto__ = events.EventEmitter.prototype; | ||
/** | ||
@@ -181,2 +203,3 @@ * Option handler for the `watch` function. | ||
var args = [].slice.call(arguments); | ||
args[3] = new Watcher; | ||
if (Object.prototype.toString.call(args[1]) === '[object Function]') { | ||
@@ -189,7 +212,8 @@ args[2] = args[1]; | ||
//overwrite default options | ||
args[1] = mixin(defaultOptions, args[1]); | ||
args[1] = mixin(defaultOptions, args[1]); | ||
//handle multiple files. | ||
args[0].forEach(function(path) { | ||
origin.apply(null, [path].concat(args.slice(1))); | ||
}); | ||
origin.apply(null, [path].concat(args.slice(1))); | ||
}); | ||
return args[3]; | ||
} | ||
@@ -200,50 +224,59 @@ }; | ||
/** | ||
* Ignore the recursive option on platforms which natively support it, | ||
* or temporarily set it to false for optimization. | ||
*/ | ||
var noRecursive = function(option) { | ||
return mixin(option, { recursive: false }); | ||
}; | ||
/** | ||
* Watch a file or a directory (recursively by default). | ||
* | ||
* | ||
* @param {String} fpath | ||
* @options {Object} options | ||
* @param {Function} cb | ||
* | ||
* | ||
* Options: | ||
* `recursive`: Watch it recursively or not (defaults to true). | ||
* `followSymLinks`: Follow symbolic links or not (defaults to false). | ||
* `maxSymLevel`: The max number of following symbolic links (defaults to 1). | ||
* `maxSymLevel`: The max number of following symbolic links (defaults to 1). | ||
* `filter`: Filter function(fullPath:string) => boolean (defaults to () => true ). | ||
* | ||
* Example: | ||
* Example: | ||
* | ||
* watch('fpath', {recursive: true}, function(file) { | ||
* watch('fpath', { recursive: true }, function(file) { | ||
* console.log(file, ' changed'); | ||
* }); | ||
*/ | ||
function watch(fpath, options, cb) { | ||
if (is.sym(fpath) | ||
&& !(options.followSymLinks | ||
&& options.maxSymLevel--)) { | ||
return; | ||
} | ||
// Due to the unstable fs.watch(), if the `fpath` is a file then | ||
// switch to watch its parent directory instead of watch it directly. | ||
function watch(fpath, options, cb, watcher) { | ||
var skip = watcher.closed || !options.filter(fpath) || ( | ||
is.sym(fpath) && !(options.followSymLinks && options.maxSymLevel--) | ||
); | ||
if (skip) return; | ||
// Due to the unstable fs.watch(), if the `fpath` is a file then | ||
// switch to watch its parent directory instead of watch it directly. | ||
// Once the logged filename matches it then triggers the callback function. | ||
if (is.file(fpath)) { | ||
var parent = path.resolve(fpath, '..'); | ||
fs.watch(parent, options, function(evt, fname) { | ||
watcher.addWatcher(fs.watch(parent, noRecursive(options)).on('change', function(evt, fname) { | ||
if (path.basename(fpath) === fname) { | ||
normalizeCall(fpath, options, cb); | ||
normalizeCall(fname, options, cb, watcher); | ||
} | ||
}).on('error', catchException); | ||
} else if (is.dir(fpath)) { | ||
fs.watch(fpath, options, function(evt, fname) { | ||
if (fname) { | ||
normalizeCall(path.join(fpath, fname), options, cb); | ||
} | ||
}).on('error', catchException); | ||
}), cb); | ||
} | ||
else if (is.dir(fpath)) { | ||
watcher.addWatcher(fs.watch(fpath, noRecursive(options)).on('change', function(evt, fname) { | ||
normalizeCall(path.join(fpath, fname), options, cb, watcher); | ||
}), cb); | ||
if (options.recursive) { | ||
// Recursively watch its sub-directories. | ||
// Recursively watch its sub-directories. | ||
sub(fpath, function(dir) { | ||
watch(dir, options, cb); | ||
}); | ||
watch(dir, options, cb, watcher); | ||
}); | ||
} | ||
} | ||
}; | ||
} | ||
@@ -253,3 +286,3 @@ | ||
* Set default options and expose. | ||
*/ | ||
*/ | ||
module.exports = handleOptions(watch, { | ||
@@ -259,3 +292,4 @@ recursive: true | ||
, maxSymLevel: 1 | ||
, filter: function(fullPath) { return true; } | ||
}); | ||
@@ -14,3 +14,3 @@ { | ||
], | ||
"version": "0.3.5", | ||
"version": "0.4.0", | ||
"bugs": { | ||
@@ -24,4 +24,9 @@ "url": "https://github.com/yuanchuan/node-watch/issues" | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "mocha" | ||
}, | ||
"devDependencies": { | ||
"fs-extra": "^0.30.0", | ||
"mocha": "^2.5.3", | ||
"tmp": "0.0.28" | ||
} | ||
} |
@@ -1,5 +0,7 @@ | ||
#Node-watch | ||
A [fs.watch](http://nodejs.org/api/fs.html#fs_fs_watch_filename_options_listener) wrapper to watch files or directories(recursively by default). | ||
#Node-watchj | ||
A [fs.watch](http://nodejs.org/api/fs.html#fs_fs_watch_filename_options_listener) wrapper to watch files or directories(recursively by default). | ||
[![NPM](https://nodei.co/npm/node-watch.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/node-watch.png/) | ||
### Installation | ||
@@ -19,3 +21,3 @@ | ||
}); | ||
``` | ||
``` | ||
@@ -27,4 +29,4 @@ ### Why fs.watch wrapper | ||
* Missing an option to watch a directory recursively. | ||
### The difference | ||
@@ -36,9 +38,11 @@ This module **currently** does not differentiate event like `rename` or `delete`. Once there is a change, the callback function will be triggered. | ||
`recursive`:Watch it recursively or not (defaults to **true**). | ||
`recursive`:Watch it recursively or not (defaults to **true**). | ||
`followSymLinks`: Follow symbolic links or not (defaults to **false**). | ||
`maxSymLevel`: The max number of following symbolic links, in order to prevent circular links (defaults to **1**). | ||
`maxSymLevel`: The max number of following symbolic links, in order to prevent circular links (defaults to **1**). | ||
`filter`: node-watch will only watch elements that pass the test implemented by the provided function. The filter function is provided with a full path string argument(defaults to ```(fullPath)=>true``` ). | ||
```js | ||
@@ -50,2 +54,24 @@ watch('somedir', { recursive: false, followSymLinks: true }, function(filename) { | ||
### Watcher object | ||
Since v0.4.0 `watch()` will return a [fs.FSWatcher]([fs.FSWatcher](https://nodejs.org/api/fs.html#fs_class_fs_fswatcher) like object, | ||
so you can close the watcher or detect change by `change` event instead of the old callback function. | ||
```js | ||
var watcher = watch('./'); | ||
watcher.on('change', function(file) { | ||
// | ||
}); | ||
watcher.on('error', function(err) { | ||
}); | ||
// close | ||
watcher.close(); | ||
``` | ||
###FAQ | ||
@@ -63,3 +89,3 @@ | ||
Write your own filter function as a higher-order function. For example: | ||
You can write your own filter function as a higher-order function. For example: | ||
@@ -74,7 +100,20 @@ ```js | ||
} | ||
// only watch for js files | ||
watch('mydir', filter(/\.js$/, function(filename) { | ||
// | ||
// | ||
})); | ||
``` | ||
Alternatively, supply a filter function in the options object. For example: | ||
```js | ||
// don't watch node_modules folder | ||
var options = { | ||
filter : function(filename) { | ||
return !/node_modules/.test(filename); | ||
} | ||
}; | ||
watch('mydir', options, function(filename) { | ||
// | ||
})); | ||
``` | ||
The second approach helps avoiding the [max open files](http://stackoverflow.com/questions/3734932/max-open-files-for-working-process) limit |
Sorry, the diff of this file is not supported yet
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
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
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
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
19728
9
545
1
114
3
3
1