Skip to content

Instantly share code, notes, and snippets.

@pwang2
Created October 13, 2016 16:09
Show Gist options
  • Save pwang2/185e919a8c8f172f7477bc43d4544f79 to your computer and use it in GitHub Desktop.
Save pwang2/185e919a8c8f172f7477bc43d4544f79 to your computer and use it in GitHub Desktop.
var Promise = require('bluebird');
const path = require('path');
const j = path.join;
const fs = Promise.promisifyAll(require('fs-extra'));
const wiredep = require('wiredep');
const home = require('user-home');
const hashCacheFile = j(home, '.quikr', 'buildCache.json');
const hasher = require('folder-hash');
const _console_log_ = console.log;
const _console_info_ = console.info;
const _console_warn_ = console.warn;
var firstEmit = true;
function WatchdogPlugin(options) {
this.runner = options.runner;
this.runtime = options.runtime;
}
function reloadHashCache() {
var options = {
throws: false
};
return fs.ensureFileAsync(hashCacheFile)
.then(() => fs.readJsonAsync(hashCacheFile, options))
.then(obj => obj || {});
}
function getBuildHash(root, w) {
return hasher.hashElement(j(root, 'components', w))
.then(hashObj => {
return hashObj.hash;
});
}
function saveHashToFs(root, w) {
return reloadHashCache()
.then(hashCache => {
return getBuildHash(root, w)
.then(hash => {
if (hash) {
hashCache[w] = hash;
}
})
.then(() => fs.outputJsonAsync(hashCacheFile, hashCache));
});
}
function cacheDetected(root, w) {
return reloadHashCache()
.then(cacheObj => {
return getBuildHash(root, w)
.then(hash => cacheObj[w] === hash);
});
}
function getDepPkgs(root, bowerObj) {
var wires = wiredep({
directory: j(root, 'bower_components'),
devDependencies: true,
bowerJson: bowerObj
});
return wires.packages || [];
}
function runNestedBuild(runner, w, env, opts, exported) {
opts.s = false; //only entry component is preview mode
opts.w = true; //need watch mode to livereload
opts.__watchdog = true; //set to trigger nesting build use the shared livereload
opts._ = ['build', w]; //just make sure it is consistent
return runner(w, env, opts, exported, false)
.then(() => {
console.log('finish building ' + w);
return saveHashToFs(exported.root, w);
});
}
WatchdogPlugin.prototype.apply = function(compiler) {
var self = this;
var runner = self.runner;
var env = self.runtime.env;
var opts = self.runtime.opts;
var exported = self.runtime.exported;
var root = exported.root;
var delayBuilds = [];
var preBuilds = [];
//watch-run will be triggered only in preview mode
compiler.plugin("watch-run", function(compilation, callback) {
if (!firstEmit) {
callback();
return;
}
var options = compilation.compiler.options;
var namespace = options.namespace;
var context = options.context;
var bowerObj = require(j(context, 'bower.json'));
var pkgs = getDepPkgs(root, bowerObj);
var watches = Object.keys(pkgs)
.filter(n => n.indexOf(namespace) === 0)
.map(n => n.substr(namespace.length + 1));
//weird promise orchestration, but works
//tempt using reduce, Promise.reduce, Promise.all, but failed
//awful for using global states delayBuilds, preBuilds
var ps = watches.map(w => {
return cacheDetected(root, w)
.then(detected => {
if (detected) {
delayBuilds.push(w);
} else {
preBuilds.push(w);
}
});
});
Promise.all(ps) //resolve all will have the delayBuilds filled
.then(() => {
return Promise.mapSeries(preBuilds, w => {
console.log('===> prebuild ' + w + ' artifacts.');
return runNestedBuild(runner, w, env, opts, exported);
})
.then(() => {
console.log('===> finish prebuild all');
callback();
});
});
});
//after emitting files for current previewing component
//start the build for dependant component,
//this makes the dependant watched by webpack,
//then changes in dependant component will trigger livereload
compiler.plugin('after-emit', function(compilation, callback) {
if (firstEmit) {
firstEmit = false;
delayBuildsAsync(delayBuilds);
}
callback();
});
function delayBuildsAsync(delayBuilds) {
if (!delayBuilds || delayBuilds.length === 0) {
return;
}
setTimeout(function() {
console.log('start postponed build');
//we mute the output as it is useless,
//we just need to make it watched by webpack
//this is pure awful hack again, but works.
muteOutput();
//make it sequence, or the output log is messy
Promise.mapSeries(delayBuilds, w => {
return runNestedBuild(runner, w, env, opts, exported);
})
.then(() => {
restoreOutput();
console.log('finish postponed build');
delayBuilds = [];
});
}, 100);
}
};
function muteOutput() {
console.log = () => {};
console.info = () => {};
console.warn = () => {};
}
function restoreOutput() {
console.log = _console_log_;
console.info = _console_info_;
console.warn = _console_warn_;
}
module.exports = WatchdogPlugin;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment