bricks-cli
Version:
Command line tool for developing ambitious ember.js apps
113 lines (92 loc) • 3.2 kB
JavaScript
var fs = require('fs');
var EventEmitter = require('events').EventEmitter;
var sane = require('sane');
var Promise = require('rsvp').Promise;
var printSlowTrees = require('broccoli/lib/logging').printSlowTrees;
module.exports = Watcher;
function Watcher(builder, options) {
this.builder = builder;
this.options = options || {};
this.watched = {};
this.timeout = null;
this.sequence = this.build();
}
Watcher.prototype = Object.create(EventEmitter.prototype);
// gathers rapid changes as one build
Watcher.prototype.scheduleBuild = function () {
if (this.timeout) return;
// we want the timeout to start now before we wait for the current build
var timeout = new Promise(function (resolve, reject) {
this.timeout = setTimeout(resolve, this.options.debounce || 100);
}.bind(this));
var build = function() {
this.timeout = null;
return this.build();
}.bind(this);
// we want the build to wait first for the current build, then the timeout
function timoutThenBuild() {
return timeout.then(build);
}
// we want the current promise to be waiting for the current build regardless if it fails or not
// can't use finally because we want to be able to affect the result.
this.sequence = this.sequence.then(timoutThenBuild, timoutThenBuild);
};
Watcher.prototype.build = function Watcher_build() {
var addWatchDir = this.addWatchDir.bind(this);
var triggerChange = this.triggerChange.bind(this);
var triggerError = this.triggerError.bind(this);
return this.builder
.build(addWatchDir)
.then(triggerChange, triggerError)
.then(function(run) {
if (this.options.verbose) {
printSlowTrees(run.graph);
}
return run;
}.bind(this));
};
Watcher.prototype.addWatchDir = function Watcher_addWatchDir(dir) {
if (this.watched[dir]) return;
if (!fs.existsSync(dir)) {
throw new Error('Attempting to watch missing directory: ' + dir);
}
var watcher = new sane.Watcher(dir, {
poll: !!this.options.poll
});
watcher.on('change', this.onFileChanged.bind(this));
watcher.on('add', this.onFileAdded.bind(this));
watcher.on('delete', this.onFileDeleted.bind(this));
this.watched[dir] = watcher;
};
Watcher.prototype.onFileChanged = function (path) {
if (this.options.verbose) console.log('file changed', path);
this.scheduleBuild();
};
Watcher.prototype.onFileAdded = function (path) {
if (this.options.verbose) console.log('file added', path);
this.scheduleBuild();
};
Watcher.prototype.onFileDeleted = function (path) {
if (this.options.verbose) console.log('file deleted', path);
this.scheduleBuild();
};
Watcher.prototype.triggerChange = function (hash) {
this.emit('change', hash);
return hash;
};
Watcher.prototype.triggerError = function (error) {
this.emit('error', error);
throw error;
};
Watcher.prototype.close = function () {
clearTimeout(this.timeout);
var watched = this.watched;
for (var dir in watched) {
if (!watched.hasOwnProperty(dir)) continue;
watched[dir].close();
delete watched[dir];
}
};
Watcher.prototype.then = function(success, fail) {
return this.sequence.then(success, fail);
};