UNPKG

pp-parachute

Version:
184 lines (159 loc) 6.68 kB
var path = require('path'); var Promise = require('bluebird'); var fastbind = require('../util/fastbind'); var ParachuteBase = require('./parachute_base'); var toArray = require('../util/array').toArray; var inherits = require('util').inherits; var Bundler = require('../bundlers/bundler'); var assert = require('../util/assert'); var fs = require('fs'); var vfs = require('vinyl-fs'); var rename = require('gulp-rename'); //todo separate environment public facing api into a new object that has an environment //An environment is a collection of application configuration that has some smarts about //how to build itself. Environments can inherit from one another and all environments //inherit from Application. var Environment = function (name) { ParachuteBase.call(this, name); this.rootPath = null; //root path from which relative paths work this.buildPromise = null; //promise denoting completion of a build this.resolveBuildPromise = null; //build promise resolve this.serveAtUrls = []; //what urls this environment is served at this.application = null; this.listening = {}; }; inherits(Environment, ParachuteBase); Environment.prototype.extends = function(other) { //todo -- lazy look up }; //Writes a bundler to disk. All bundlers should resolve a vinyl file. This is what gets written Environment.prototype.writeBundlerToDisk = function (bundler) { var self = this; var write = function (resolve, reject) { var outputDirectoryRoot = path.join(self.getRootPath(), self.getOutputDirectory()); var bundlerProperties = self.getBundlerById(bundler.bundlerId); bundler.vinylStream.path = bundlerProperties.mountPath; var stream = vfs.dest(outputDirectoryRoot); stream.on('finish', resolve); //vinylStream can't be an actual vinyl file, it needs to be a stream WITH a vinyl file bundler.vinylStream.pipe(rename(bundlerProperties.mountPath)).pipe(stream); }; return new Promise(write).catch(function (e) { console.log('Write to disk', e.stack); }); }; //Watch a set of bundles and respond to the events they emit Environment.prototype.watch = function () { var bundlers = toArray(this.getActiveBundlers()); this.ensureBuildPromise(); this.emit('buildStarted', bundlers); for (var i = 0; i < bundlers.length; i++) { var bundler = bundlers[i]; if (!this.listening[bundler.bundlerId]) { this.listening[bundler.bundlerId] = true; bundler.on('bundleStarted', fastbind(this.onBundleStarted, this)); bundler.on('bundleCompleted', fastbind(this.onBundleCompleted, this)); } bundler.watch().catch(function () {}); } return this.buildPromise; }; //build a set of bundlers without watching. Environment.prototype.build = function () { var bundlers = toArray(this.getActiveBundlers()); this.ensureBuildPromise(); this.emit('buildStarted', bundlers); for (var i = 0; i < bundlers.length; i++) { var bundler = bundlers[i]; if (!this.listening[bundler.bundlerId]) { bundler.on('bundleStarted', fastbind(this.onBundleStarted, this)); bundler.on('bundleCompleted', fastbind(this.onBundleCompleted, this)); } bundler.build(); } return this.buildPromise; }; //make sure we have a valid build promise. A new build promise is created if we get a call //to build and we are not currently building. Environment.prototype.ensureBuildPromise = function () { var self = this; if (!this.buildPromise || !this.buildPromise.isPending()) { this.buildPromise = new Promise(function (resolve) { self.resolveBuildPromise = resolve; }); } }; //handle a bundlers bundleCompleted event, write that bundler to disk and //if no other bundlers are in progress, complete the build Environment.prototype.onBundleCompleted = function (bundler) { this.writeBundlerToDisk(bundler).then(fastbind(function () { if (!this.buildInProgress()) { this.finishBuild(); } }, this)).catch(function (e) { console.log('WriteBundlerToDisk -- Catch', e); }); }; //handle a bundler's bundleStarted event. Ensure a valid build promise and //emit buildStarted if no bundlers are currently building. Environment.prototype.onBundleStarted = function (bundler) { this.ensureBuildPromise(); if (!this.buildInProgress()) { this.emit('buildStarted', this.getActiveBundlers()); } }; Environment.prototype.buildInProgress = function () { return toArray(this.getActiveBundlers()).some(function (bundler) { return bundler.bundleStatus === Bundler.BundleStatus.Bundling; }); }; Environment.prototype.getFailedBundles = function () { return toArray(this.getActiveBundlers()).filter(function (bundler) { return bundler.bundleStatus === Bundler.BundleStatus.Failed; }); }; Environment.prototype.getPassedBundles = function () { return toArray(this.getActiveBundlers()).filter(function (bundler) { return bundler.bundleStatus === Bundler.BundleStatus.Succeeded; }); }; //emits build events if they have not yet been emitted Environment.prototype.finishBuild = function (buildStartTime) { //this might get called twice, make sure it doesn't emit two events if (this.buildPromise.isPending()) { var passedBundles = this.getPassedBundles(); var failedBundles = this.getFailedBundles(); var buildSucceeded = failedBundles.length === 0; var buildDuration = new Date().getTime() - buildStartTime; if (buildSucceeded) { this.emit('buildSucceeded', passedBundles, buildDuration); this.resolveBuildPromise(true); } else { this.emit('buildFailed', failedBundles, buildDuration); this.resolveBuildPromise(false); } this.emit('buildCompleted', passedBundles, failedBundles, buildDuration); } return this.buildPromise; }; Environment.prototype.serveAtUrl = function (url) { assert.isString(url); if (url.charAt(url.length - 1) === '/') { url = url.substring(0, url.length - 1); } this.serveAtUrls.push(url); return this; }; Environment.prototype.getServedUrls = function (url) { return this.serveAtUrls.slice(0); }; Environment.prototype.getApplication = function() { return this.application; }; //shut down all assetWatchers from the bundlers Environment.prototype.shutdown = function () { toArray(this.getActiveBundlers()).forEach(function (bundler) { bundler.shutdown(); }); }; module.exports = Environment;