gypwatcher
Version:
Automatically run node-gyp when your module's C++ files change
163 lines (127 loc) • 3.53 kB
JavaScript
"use strict";
const Path = require('path');
const EventEmitter = require('events').EventEmitter;
const ChildProcess = require('child_process');
const ch = require('chokidar');
const fs = require('fs');
const _ = require('lodash');
class GypWatcher extends EventEmitter {
constructor(config) {
super();
this.cwd = process.cwd();
this.config = config || this.loadConfig();
this.watchFiles = this.loadWatchFiles();
this.nodeGypScript = this.loadNodeGypScript();
this.postExec = this.loadNodeGypPost();
this.watcher = this.loadWatcher();
}
loadConfig() {
let config = {};
try {
let userConfig = require(this.cwd + '/gypwatcher');
_.defaults(config, userConfig);
} catch (e) {}
let defaultConfig = require('./config');
_.defaults(config, defaultConfig);
return config;
}
loadWatchFiles() {
return this.config.watchFiles.map(file => {
if (/^\.\//.test(file)) {
file = Path.join(this.cwd, file);
}
return file;
});
}
loadNodeGypScript() {
let config = this.config;
if (_.isString(config.nodeGypScript)) {
return config.nodeGypScript;
} else if (_.isArray(config.nodeGypScript)) {
for (let path of config.nodeGypScript) {
if (/^\.\//.test(path)) {
path = Path.join(this.cwd, path);
} else if (!/^\//.test(path)) {
continue;
}
try {
fs.accessSync(path, fs.R_OK | fs.X_OK);
} catch (e) {
console.error('node-gyp not found at ' + path);
continue;
}
console.log('using node-gyp at ' + path);
return path;
}
}
console.warn('node-gyp not found at configured locations. Falling back to node-gyp');
return 'node-gyp';
}
loadNodeGypPost() {
let self = this;
let config = this.config;
try {
let postExec = config.nodeGypPost;
if (_.isString(postExec)) {
let path = postExec;
if (/^\.\//.test(path)) {
path = Path.join(this.cwd, path);
}
postExec = require(path)
}
if (!_.isFunction(postExec)) {
throw new Error('postExec is not a function: "' + (typeof postExec) + '" found');
}
return postExec;
} catch (e) {
if (!/Cannot find module/.test(e.message)) {
console.error(e);
} else {
throw e;
}
}
}
loadWatcher() {
return ch.watch(this.watchFiles, this.config.chokidarOptions);
}
onNodeGypComplete() {
let self = this;
console.log('node-gyp complete');
try {
if (!this.postExec.length) {
this.postExec();
this.isRunning = false;
emitComplete();
} else {
this.postExec(function() {
self.isRunning = false;
emitComplete();
});
}
} catch(e) {
emitComplete();
throw e;
}
function emitComplete() {
self.emit('finished', self);
}
}
watch() {
this.watcher.on('change', function(file) {
file = Path.join(this.cwd, file);
if (this.isRunning) {
return;
}
this.isRunning = true;
let proc = ChildProcess.spawn(this.nodeGypScript, this.config.nodeGypOptions, {cwd: this.cwd});
proc.stdout.pipe(process.stdout);
proc.stderr.pipe(process.stderr);
proc.on('exit', this.onNodeGypComplete.bind(this));
}.bind(this));
return this;
}
stop() {
this.watcher.close();
}
}
module.exports = GypWatcher;