UNPKG

gypwatcher

Version:

Automatically run node-gyp when your module's C++ files change

163 lines (127 loc) 3.53 kB
"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;