Comparing version 0.1.6 to 0.2.0
141
lib/gaze.js
@@ -29,2 +29,3 @@ /* | ||
fs.existsSync = fs.existsSync || path.existsSync; | ||
path.sep = path.sep || path.normalize('/'); | ||
@@ -69,3 +70,5 @@ // CoffeeScript's __extends utility | ||
// Delay for events called in succession for the same file/event | ||
debounceDelay: 500 | ||
debounceDelay: 500, | ||
// Use a desired watch method over the other | ||
forceWatchMethod: false | ||
}); | ||
@@ -113,13 +116,19 @@ | ||
// Actually emit the event and `all` event | ||
var emit = function() { | ||
// If not added/deleted/changed/renamed then just emit the event | ||
if (e.slice(-2) !== 'ed') { | ||
Gaze.__super__.emit.apply(_this, args); | ||
if (e === 'added' || e === 'changed' || e === 'deleted') { | ||
Gaze.__super__.emit.apply(_this, ['all', e].concat([].slice.call(args, 1))); | ||
} | ||
return _this; | ||
}; | ||
return this; | ||
} | ||
// If no filepath just emit the event | ||
if (typeof filepath !== 'string') { return emit(); } | ||
// Detect rename event, if added and previous deleted is in the cache | ||
if (e === 'added') { | ||
Object.keys(this._cached).forEach(function(oldFile) { | ||
if (_.indexOf(_this._cached[oldFile], 'deleted') !== -1) { | ||
args[0] = e = 'renamed'; | ||
[].push.call(args, oldFile); | ||
delete _this._cached[oldFile]; | ||
return false; | ||
} | ||
}); | ||
} | ||
@@ -135,3 +144,5 @@ // If cached doesnt exist, create a delay before running the next | ||
}, this.options.debounceDelay); | ||
return emit(); | ||
// Emit the event and `all` event | ||
Gaze.__super__.emit.apply(_this, args); | ||
Gaze.__super__.emit.apply(_this, ['all', e].concat([].slice.call(args, 1))); | ||
} | ||
@@ -209,25 +220,59 @@ | ||
// Returns `watched` files with relative paths to process.cwd() | ||
Gaze.prototype.relative = function(dir) { | ||
Gaze.prototype.relative = function(dir, unixify) { | ||
var _this = this; | ||
var relative = Object.create(null); | ||
var relDir; | ||
var relDir, relFile, unixRelDir; | ||
var cwd = this.options.cwd || process.cwd(); | ||
dir = this._markDir(dir); | ||
unixify = unixify || false; | ||
Object.keys(this._watched).forEach(function(dir) { | ||
relDir = path.relative(cwd, dir); | ||
if (relDir === '') { relDir = '.'; } | ||
relative[relDir] = _this._watched[dir].map(function(file) { | ||
return path.relative(path.join(cwd, relDir), file); | ||
relDir = path.relative(cwd, dir) + path.sep; | ||
if (relDir === path.sep) { relDir = '.'; } | ||
unixRelDir = unixify ? _this._unixifyPathSep(relDir) : relDir; | ||
relative[unixRelDir] = _this._watched[dir].map(function(file) { | ||
relFile = path.relative(path.join(cwd, relDir), file); | ||
if (_this._isDir(file)) { | ||
relFile = _this._markDir(relFile); | ||
} | ||
if (unixify) { | ||
relFile = _this._unixifyPathSep(relFile); | ||
} | ||
return relFile; | ||
}); | ||
}); | ||
if (dir && unixify) { | ||
dir = this._unixifyPathSep(dir); | ||
} | ||
return dir ? relative[dir] || [] : relative; | ||
}; | ||
// Ensures the dir is marked with path.sep | ||
Gaze.prototype._markDir = function(dir) { | ||
if (typeof dir === 'string' && dir.slice(-(path.sep.length)) !== path.sep) { | ||
dir += path.sep; | ||
} | ||
return dir; | ||
}; | ||
// Returns boolean whether filepath is dir terminated | ||
Gaze.prototype._isDir = function(dir) { | ||
if (typeof dir !== 'string') { return false; } | ||
return (dir.slice(-(path.sep.length)) === path.sep); | ||
}; | ||
// Changes path.sep to unix ones for testing | ||
Gaze.prototype._unixifyPathSep = function(filepath) { | ||
return (process.platform === 'win32') ? String(filepath).replace(/\\/g, '/') : filepath; | ||
}; | ||
// Adds files and dirs to watched | ||
Gaze.prototype._addToWatched = function(files) { | ||
var _this = this; | ||
var dir; | ||
var dir, parent; | ||
var cwd = _this.options.cwd || process.cwd(); | ||
files.forEach(function(file) { | ||
var filepath = path.resolve(cwd, file); | ||
// if mark false, use stat to figure the isDir | ||
// the '/' mark comes from node-glob but we convert it to path.sep | ||
if (_this.options.mark === false) { | ||
@@ -238,11 +283,14 @@ if (fs.statSync(filepath).isDirectory()) { | ||
} | ||
// is a dir if marked with / at the end from glob | ||
if (file.slice(-1) === '/') { | ||
dir = filepath; | ||
filepath = null; | ||
// is dir, init key and sub folder | ||
filepath = _this._markDir(filepath); | ||
_this._objectPush(_this._watched, filepath); | ||
parent = path.dirname(filepath) + path.sep; | ||
_this._objectPush(_this._watched, parent, filepath); | ||
} else { | ||
dir = path.dirname(filepath); | ||
// is file, add to dir | ||
dir = path.dirname(filepath) + path.sep; | ||
_this._objectPush(_this._watched, dir, filepath); | ||
} | ||
// Create a dir key on `watched` if doesnt exist and init array | ||
_this._objectPush(_this._watched, dir, filepath); | ||
}); | ||
@@ -252,3 +300,3 @@ return this; | ||
// Create a `key`:[] if doesnt exist on `obj` then push or concat the `val` | ||
// Create a `key:[]` if doesnt exist on `obj` then push or concat the `val` | ||
Gaze.prototype._objectPush = function(obj, key, val) { | ||
@@ -281,16 +329,20 @@ if (obj[key] == null) { obj[key] = []; } | ||
try { | ||
_this._watchers[file] = fs.watch(file, opts, function(event) { | ||
if (typeof watchOne === 'function') { | ||
watchTwo = null; | ||
watchOne(); | ||
} | ||
}); | ||
fs.watchFile(file, opts, function(curr, prev) { | ||
if (curr.mtime.getTime() !== prev.mtime.getTime()) { | ||
if (typeof watchTwo === 'function') { | ||
watchOne = null; | ||
watchTwo(); | ||
if (_this.options.forceWatchMethod === false || _this.options.forceWatchMethod === 'new') { | ||
_this._watchers[file] = fs.watch(file, opts, function(event) { | ||
if (typeof watchOne === 'function') { | ||
watchTwo = null; | ||
watchOne(); | ||
} | ||
} | ||
}); | ||
}); | ||
} | ||
if (_this.options.forceWatchMethod === false || _this.options.forceWatchMethod === 'old') { | ||
fs.watchFile(file, opts, function(curr, prev) { | ||
if (curr.mtime.getTime() !== prev.mtime.getTime()) { | ||
if (typeof watchTwo === 'function') { | ||
watchOne = null; | ||
watchTwo(); | ||
} | ||
} | ||
}); | ||
} | ||
} catch (err) { | ||
@@ -327,4 +379,6 @@ if (err.code === 'EMFILE') { | ||
var filepath = path.join(dir, file); | ||
_this.remove(filepath); | ||
_this.emit('deleted', filepath); | ||
if (!_this._isDir(file)) { | ||
_this.remove(filepath); | ||
_this.emit('deleted', filepath); | ||
} | ||
}); | ||
@@ -350,7 +404,8 @@ | ||
files.forEach(function(file) { | ||
_this._watchFile(file, function(e, filepath) { | ||
// TODO: If event rename, update the watched index | ||
_this._watchFile(file, function(err, filepath) { | ||
// Only emit changed if the file still exists | ||
// Prevents changed/deleted duplicate events | ||
if (fs.existsSync(filepath)) { | ||
// TODO: This ignores changed events on folders, maybe support this? | ||
// When a file is added, a folder changed event emits first | ||
if (fs.existsSync(filepath) && !_this._isDir(file)) { | ||
_this.emit('changed', filepath); | ||
@@ -357,0 +412,0 @@ } |
{ | ||
"name": "gaze", | ||
"description": "A globbing fs.watch wrapper built from the best parts of other fine watch libs.", | ||
"version": "0.1.6", | ||
"version": "0.2.0", | ||
"homepage": "https://github.com/shama/gaze", | ||
@@ -6,0 +6,0 @@ "author": { |
# gaze [![Build Status](https://secure.travis-ci.org/shama/gaze.png?branch=master)](http://travis-ci.org/shama/gaze) | ||
A globbing fs.watch wrapper built from the best parts of other fine watch libs. | ||
A globbing fs.watch wrapper built from the best parts of other fine watch libs. | ||
Compatible with NodeJS v0.8/0.6, Windows, OSX and Linux. | ||
@@ -103,2 +104,9 @@ | ||
* `options` The options object passed in. | ||
* `interval` {integer} Interval to pass to fs.watchFile | ||
* `debounceDelay` {integer} Delay for events called in succession for the same | ||
file/event | ||
* `forceWatchMethod` {'new'|'old'|false} Defaults to `false` to pick the first | ||
triggered watch method. Set to `'new'` to only use `fs.watch` or `'old'` to | ||
only use `fs.watchFile`. `'old'` is recommended if watch isn't firing or | ||
you're watching files over a network. | ||
@@ -110,4 +118,5 @@ #### Events | ||
* `added(filepath)` When a file has been added to a watch directory. | ||
* `changed(filepath)` When a file or directory has been changed. | ||
* `deleted(filepath)` When a file or directory has been deleted. | ||
* `changed(filepath)` When a file has been changed. | ||
* `deleted(filepath)` When a file has been deleted. | ||
* `renamed(newPath, oldPath)` When a file has been renamed. | ||
* `end()` When the watcher is closed and watches have been removed. | ||
@@ -125,6 +134,9 @@ * `error(err)` When an error occurs. | ||
* `watched()` Returns the currently watched files. | ||
* `relative()` Returns the currently watched files with relative paths. | ||
* `relative([dir, unixify])` Returns the currently watched files with relative paths. | ||
* `dir` {string} Only return relative files for this directory. | ||
* `unixify` {boolean} Return paths with `/` instead of `\\` if on Windows. | ||
## FAQs | ||
## Why Another `fs.watch` Wrapper? | ||
### Why Another `fs.watch` Wrapper? | ||
I liked parts of other `fs.watch` wrappers but none had all the features I | ||
@@ -138,2 +150,9 @@ needed. This lib combines the features I needed from other fine watch libs: | ||
### How do I fix the error `EMFILE: Too many opened files.`? | ||
This is because of your system's max opened file limit. For OSX the default is | ||
very low (256). Increase your limit with `ulimit -n 10480`, the number being the | ||
new max limit. If you're still running into issues then consider setting the | ||
option `forceWatchMethod: 'old'` to use the old and slower stat polling watch | ||
method. | ||
## Contributing | ||
@@ -145,2 +164,3 @@ In lieu of a formal styleguide, take care to maintain the existing coding style. | ||
## Release History | ||
* 0.2.0 - Support and mark folders with `path.sep`. Add `forceWatchMethod` option. Support `renamed` events. | ||
* 0.1.6 - Recognize the `cwd` option properly | ||
@@ -147,0 +167,0 @@ * 0.1.5 - Catch too many open file errors |
@@ -14,5 +14,5 @@ 'use strict'; | ||
new gaze.Gaze('**/*', {}, function() { | ||
var result = this.relative(); | ||
test.deepEqual(result['.'], ['one.js']); | ||
test.deepEqual(result['sub'], ['one.js', 'two.js']); | ||
var result = this.relative(null, true); | ||
test.deepEqual(result['.'], ['Project (LO)/', 'nested/', 'one.js', 'sub/']); | ||
test.deepEqual(result['sub/'], ['one.js', 'two.js']); | ||
this.close(); | ||
@@ -25,3 +25,3 @@ test.done(); | ||
var g = gaze('**/*', function(err, watcher) { | ||
test.deepEqual(watcher.relative('sub'), ['one.js', 'two.js']); | ||
test.deepEqual(watcher.relative('sub', true), ['one.js', 'two.js']); | ||
g.close(); | ||
@@ -35,3 +35,3 @@ test.done(); | ||
g.on('ready', function(watcher) { | ||
test.deepEqual(watcher.relative('sub'), ['one.js', 'two.js']); | ||
test.deepEqual(watcher.relative('sub', true), ['one.js', 'two.js']); | ||
this.close(); | ||
@@ -38,0 +38,0 @@ test.done(); |
@@ -14,5 +14,5 @@ 'use strict'; | ||
gaze('**/*', function() { | ||
var result = this.relative(); | ||
test.deepEqual(result['.'], ['one.js']); | ||
test.deepEqual(result['sub'], ['one.js', 'two.js']); | ||
var result = this.relative(null, true); | ||
test.deepEqual(result['.'], ['Project (LO)/', 'nested/', 'one.js', 'sub/']); | ||
test.deepEqual(result['sub/'], ['one.js', 'two.js']); | ||
this.close(); | ||
@@ -25,3 +25,3 @@ test.done(); | ||
gaze('**/*', function() { | ||
test.deepEqual(this.relative('sub'), ['one.js', 'two.js']); | ||
test.deepEqual(this.relative('sub', true), ['one.js', 'two.js']); | ||
this.close(); | ||
@@ -34,5 +34,5 @@ test.done(); | ||
gaze(['*.js', 'sub/*.js'], function() { | ||
var result = this.relative(); | ||
var result = this.relative(null, true); | ||
test.deepEqual(result['.'], ['one.js']); | ||
test.deepEqual(result['sub'], ['one.js', 'two.js']); | ||
test.deepEqual(result['sub/'], ['one.js', 'two.js']); | ||
this.close(); | ||
@@ -45,4 +45,4 @@ test.done(); | ||
gaze(['Project (LO)/*.js'], function() { | ||
var result = this.relative(); | ||
test.deepEqual(result['Project (LO)'], ['one.js']); | ||
var result = this.relative(null, true); | ||
test.deepEqual(result['Project (LO)/'], ['one.js']); | ||
this.close(); | ||
@@ -49,0 +49,0 @@ test.done(); |
@@ -14,3 +14,7 @@ 'use strict'; | ||
'sub/tmp.js', | ||
'sub/tmp' | ||
'sub/tmp', | ||
'sub/renamed.js', | ||
'added.js', | ||
'nested/added.js', | ||
'nested/sub/added.js' | ||
].forEach(function(d) { | ||
@@ -34,4 +38,4 @@ var p = path.resolve(__dirname, 'fixtures', d); | ||
this.remove(path.resolve(__dirname, 'fixtures')); | ||
var result = this.relative(); | ||
test.deepEqual(result['sub'], ['one.js']); | ||
var result = this.relative(null, true); | ||
test.deepEqual(result['sub/'], ['one.js']); | ||
test.notDeepEqual(result['.'], ['one.js']); | ||
@@ -167,3 +171,47 @@ this.close(); | ||
}); | ||
}, | ||
addedEmitInSubFolders: function(test) { | ||
test.expect(3); | ||
var times = 0; | ||
gaze('**/*', {debounceDelay:100}, function(err, watcher) { | ||
watcher.on('added', function(filepath) { | ||
test.equal('added.js', path.basename(filepath)); | ||
fs.unlinkSync(filepath); | ||
times++; | ||
if (times > 2) { | ||
watcher.close(); | ||
test.done(); | ||
} | ||
}); | ||
fs.writeFileSync(path.resolve(__dirname, 'fixtures', 'nested', 'sub', 'added.js'), 'var added = true;'); | ||
setTimeout(function() { | ||
fs.writeFileSync(path.resolve(__dirname, 'fixtures', 'added.js'), 'var added = true;'); | ||
}, 500); | ||
setTimeout(function() { | ||
fs.writeFileSync(path.resolve(__dirname, 'fixtures', 'nested', 'added.js'), 'var added = true;'); | ||
}, 1000); | ||
}); | ||
}, | ||
forceWatchMethodOld: function(test) { | ||
test.expect(1); | ||
gaze('**/*', {forceWatchMethod:'old'}, function(err, watcher) { | ||
watcher.on('all', function(e, filepath) { | ||
test.ok(true); | ||
watcher.close(); | ||
test.done(); | ||
}); | ||
fs.writeFileSync(path.resolve(__dirname, 'fixtures', 'sub', 'two.js'), 'var two = true;'); | ||
}); | ||
}, | ||
forceWatchMethodNew: function(test) { | ||
test.expect(1); | ||
gaze('**/*', {forceWatchMethod:'new'}, function(err, watcher) { | ||
watcher.on('all', function(e, filepath) { | ||
test.ok(true); | ||
watcher.close(); | ||
test.done(); | ||
}); | ||
fs.writeFileSync(path.resolve(__dirname, 'fixtures', 'sub', 'two.js'), 'var two = true;'); | ||
}); | ||
} | ||
}; |
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
33773
21
818
172
3