127 lines
2.5 KiB
JavaScript
127 lines
2.5 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
var fs = require('fs');
|
||
|
var path = require('path');
|
||
|
var common = require('./common');
|
||
|
var EventEmitter = require('events').EventEmitter;
|
||
|
var fsevents;
|
||
|
|
||
|
try {
|
||
|
fsevents = require('fsevents');
|
||
|
} catch (e) {
|
||
|
// Ignore.
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Constants
|
||
|
*/
|
||
|
|
||
|
var CHANGE_EVENT = common.CHANGE_EVENT;
|
||
|
var DELETE_EVENT = common.DELETE_EVENT;
|
||
|
var ADD_EVENT = common.ADD_EVENT;
|
||
|
var ALL_EVENT = common.ALL_EVENT;
|
||
|
|
||
|
/**
|
||
|
* Export `FSEventsWatcher` class.
|
||
|
*/
|
||
|
|
||
|
module.exports = FSEventsWatcher;
|
||
|
|
||
|
/**
|
||
|
* Watches `dir`.
|
||
|
*
|
||
|
* @class FSEventsWatcher
|
||
|
* @param String dir
|
||
|
* @param {Object} opts
|
||
|
* @public
|
||
|
*/
|
||
|
|
||
|
function FSEventsWatcher(dir, opts) {
|
||
|
if (!fsevents) {
|
||
|
throw new Error(
|
||
|
'`fsevents` unavailable (this watcher can only be used on Darwin)'
|
||
|
);
|
||
|
}
|
||
|
|
||
|
common.assignOptions(this, opts);
|
||
|
|
||
|
this.root = path.resolve(dir);
|
||
|
this.watcher = fsevents(this.root);
|
||
|
|
||
|
this.watcher.start().on('change', this.handleEvent.bind(this));
|
||
|
this._tracked = Object.create(null);
|
||
|
|
||
|
common.recReaddir(
|
||
|
this.root,
|
||
|
filepath => (this._tracked[filepath] = true),
|
||
|
filepath => (this._tracked[filepath] = true),
|
||
|
this.emit.bind(this, 'ready'),
|
||
|
this.emit.bind(this, 'error'),
|
||
|
this.ignored
|
||
|
);
|
||
|
}
|
||
|
|
||
|
FSEventsWatcher.prototype.__proto__ = EventEmitter.prototype;
|
||
|
|
||
|
FSEventsWatcher.prototype.handleEvent = function(filepath) {
|
||
|
var relativePath = path.relative(this.root, filepath);
|
||
|
if (
|
||
|
!common.isFileIncluded(this.globs, this.dot, this.doIgnore, relativePath)
|
||
|
) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
fs.lstat(
|
||
|
filepath,
|
||
|
function(error, stat) {
|
||
|
if (error && error.code !== 'ENOENT') {
|
||
|
this.emit('error', error);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (error) {
|
||
|
// Ignore files that aren't tracked and don't exist.
|
||
|
if (!this._tracked[filepath]) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this._emit(DELETE_EVENT, relativePath);
|
||
|
delete this._tracked[filepath];
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (this._tracked[filepath]) {
|
||
|
this._emit(CHANGE_EVENT, relativePath, stat);
|
||
|
} else {
|
||
|
this._tracked[filepath] = true;
|
||
|
this._emit(ADD_EVENT, relativePath, stat);
|
||
|
}
|
||
|
}.bind(this)
|
||
|
);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* End watching.
|
||
|
*
|
||
|
* @public
|
||
|
*/
|
||
|
|
||
|
FSEventsWatcher.prototype.close = function(callback) {
|
||
|
this.watcher.stop();
|
||
|
this.removeAllListeners();
|
||
|
if (typeof callback === 'function') {
|
||
|
process.nextTick(callback.bind(null, null, true));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Emit events.
|
||
|
*
|
||
|
* @private
|
||
|
*/
|
||
|
|
||
|
FSEventsWatcher.prototype._emit = function(type, file, stat) {
|
||
|
this.emit(type, file, this.root, stat);
|
||
|
this.emit(ALL_EVENT, type, file, this.root, stat);
|
||
|
};
|