UNPKG

pp-parachute

Version:
250 lines (222 loc) 7.83 kB
var EventEmitter = require('events').EventEmitter; var Promise = require('bluebird'); var concat = require('gulp-concat'); var inherits = require('util').inherits; var fastbind = require('../util/fastbind'); var globby = require('globby'); var vfs = require('vinyl-fs'); var path = require('path'); var AssetWatcher = require('./asset_watcher'); var idGenerator = 0; var Stream = require('stream').Stream; var SourceStream = require('vinyl-source-stream'); var debounceHead = require('../util/debounce').debounceHead; var BundleStatus = { None: 1, Succeeded: 2, Failed: 3, Bundling: 4 }; var Bundler = function (assetGlobs, name, rootPath) { EventEmitter.call(this); this.bundlerId = idGenerator++; //used by environments to uniquely identify bundlers this.assetGlobs = assetGlobs; this.pipes = []; this.bundlePromise = null; this.bundleError = null; this.vinylStream = null; this.rootPath = rootPath || findFilePath(); this.name = name || 'bundler' + this.bundlerId; this.bundleStatus = BundleStatus.None; this.dirtyFileList = true; this.rawOutput = null; this.debouncedBundleFn = debounceHead(this, this._bundle, 0); }; Bundler.BundleStatus = BundleStatus; inherits(Bundler, EventEmitter); Bundler.prototype.setRootPath = function (rootPath) { this.rootPath = rootPath; return this; }; Bundler.prototype.getRootPath = function () { return this.rootPath; }; Bundler.prototype.setDebugName = function (name) { this.name = name; return this; }; Bundler.prototype.pipe = function (stream) { this.pipes.push(stream); return this; }; Bundler.prototype.isWatched = function () { return this.assetWatcher !== null; }; Bundler.prototype.build = function () { if (!this.bundlePromise) { this.buildPromise = this.debouncedBundleFn(); } else if (this.bundlePromise.isPending()) { return this.bundlePromise; } else { this.emit('bundleCompleted', this, this.vinylStream, 0); if (this.bundleStatus === BundleStatus.Succeeded) { this.emit('bundleSucceeded', this, this.vinylStream, 0) } else if (this.bundleStatus === BundleStatus.Failed) { this.emit('bundleFailed', this, this.bundleError, 0); } } return this.bundlePromise; }; Bundler.prototype.watch = function () { var self = this; if (!this.assetWatcher) { return this.watchFiles().then(function () { return self.build(); }); } return this.build(); }; //bundle resolves w/ vinyl or rejects w/ error Bundler.prototype._bundle = function () { var self = this; this.bundleError = null; var start = new Date(); this.bundlePromise = this.getFilePaths().then(function (filePaths) { self.bundleStatus = BundleStatus.Bundling; self.emit('bundleStarted', self, filePaths); return self.bundle(filePaths); }).then(function (file) { self.rawOutput = file; var stream = Bundler.Streamify(file); self.vinylStream = Bundler.Streamify(self.applyPipes(stream)); self.bundleStatus = BundleStatus.Succeeded; self.emit('bundleSucceeded', self, self.bundleError, new Date() - start); self.emit('bundleCompleted', self, self.vinylStream, new Date() - start); return Promise.resolve(self); }).catch(function (bundleError) { self.vinylStream = Bundler.Streamify(bundleError); self.rawOutput = bundleError; self.bundleError = bundleError; self.bundleStatus = BundleStatus.Failed; self.emit('bundleFailed', self, self.bundleError, new Date() - start); self.emit('bundleCompleted', self, self.vinylStream,new Date() - start); return Promise.resolve(self); }); return this.bundlePromise; }; Bundler.prototype.bundle = function (filePaths) { return vfs.src(filePaths).pipe(concat(this.name)); }; Bundler.prototype.watchFiles = function () { this.assetWatcher = new AssetWatcher(); this.assetWatcher.on('fileAdded', fastbind(this._onFileAdded, this)); this.assetWatcher.on('fileChanged', fastbind(this._onFileChanged, this)); this.assetWatcher.on('fileRemoved', fastbind(this._onFileRemoved, this)); return this.assetWatcher.watch(this.getWatchGlobs()); }; Bundler.prototype.getFilePaths = function () { var globs = this.getWatchGlobs(); return new Promise(fastbind(function (resolve, reject) { globby(globs, function (err, files) { err && reject(err) || resolve(files); }); }, this)); }; Bundler.prototype.getWatchGlobs = function () { if (!Array.isArray(this.assetGlobs)) this.assetGlobs = [this.assetGlobs]; return this.assetGlobs.map(function (glob) { //we need to handle negated paths explicitly because we prepend application root var negated = false; if (glob.indexOf('!') === 0) { negated = true; glob = glob.slice(1); } var joined = path.join(this.rootPath, glob); if (negated) { joined = '!' + joined; } return joined; }, this); }; Bundler.prototype.applyPipes = function (stream) { for (var i = 0; i < this.pipes.length; i++) { stream = stream.pipe(this.pipes[i]); } return stream; }; Bundler.prototype.showBundleErrorJson = function() { return JSON.stringify(this.bundleError); }; Bundler.prototype.showBundleErrorHtml = function() { console.log(this.bundleError); return this.bundleError.toString(); }; Bundler.prototype._onFileAdded = function (filePath) { this.dirtyFileList = true; this.onFileAdded && this.onFileAdded(filePath); this.debouncedBundleFn(); }; Bundler.prototype._onFileChanged = function (filePath) { this.emit('fileChanged', filePath); this.onFileChanged && this.onFileChanged(filePath); this.debouncedBundleFn(); }; Bundler.prototype._onFileRemoved = function (filePath) { this.dirtyFileList = true; this.emit('fileRemoved', filePath); this.onFileRemoved && this.onFileRemoved(filePath); this.debouncedBundleFn(); }; Bundler.prototype.onFileAdded = function () {}; Bundler.prototype.onFileChanged = function () {}; Bundler.prototype.onFileRemoved = function () {}; Bundler.prototype.shutdown = function () { this.assetWatcher && this.assetWatcher.shutdown(); }; Bundler.Streamify = function (input) { var stream = null; if (!input) { stream = SourceStream('file'); stream.write(new Buffer('')); stream.end(); } else if (input instanceof Stream) { return input; } else if (input instanceof Buffer) { stream = SourceStream('file'); stream.write(input); stream.end(); } else if (typeof input === 'string') { stream = SourceStream('file'); stream.write(new Buffer(input)); stream.end(); } else if (typeof input === 'object') { stream = SourceStream('file'); stream.write(new Buffer(JSON.stringify(input))); stream.end(); } else { stream = SourceStream('file'); stream.write(new Buffer(input.toString())); stream.end(); } return stream; }; //gets the path to the file that the constructor was called in //by parsing a stack trace. function findFilePath() { try { throw new Error('give me a stack'); } catch (e) { var fileLines = e.stack.split('\n'); for (var i = 0; i < fileLines.length; i++) { if (fileLines[i].indexOf('at new') !== -1) { break; } } var fileLine = fileLines[i + 1]; var start = fileLine.indexOf('(') + 1; var end = fileLine.lastIndexOf('/'); return fileLine.substring(start, end); } } module.exports = Bundler;