UNPKG

lasso

Version:

Lasso.js is a build tool and runtime library for building and bundling all of the resources needed by a web application

282 lines (221 loc) 10 kB
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } var transforms = require('./transforms'); var through = require('through'); var logger = require('raptor-logging').logger(module); var ok = require('assert').ok; var fs = require('fs'); var CombinedStream = require('./util/CombinedStream'); var DeferredReadable = require('./util/DeferredReadable'); var nodePath = require('path'); var AsyncValue = require('raptor-async/AsyncValue'); function createReadDependencyStream(dependency, lassoContext, transformerAsyncValue) { var deferredReadable = new DeferredReadable(); transformerAsyncValue.done(function (err, transformer) { if (err) { deferredReadable.emit('error', err); return; } var contentType = dependency.getContentType(); var readContext = Object.create(lassoContext || {}); readContext.contentType = contentType; readContext.dependency = dependency; readContext.transformer = transformer; readContext.dir = dependency.getDir ? dependency.getDir(lassoContext) : null; if (dependency.getSourceFile) { readContext.path = dependency.getSourceFile(); } function createReadStream() { var err; var readStream = dependency.read(readContext); if (!readStream) { err = new Error('Dependency did not return read stream: ' + dependency); } if (typeof readStream.pipe !== 'function') { err = new Error('Dependency returned invalid stream: ' + dependency); } if (err) { return new DeferredReadable(function () { this.emit('error', err); this.push(null); }); } return readStream; } function createTransformedStream(readStream) { if (!transformer.hasTransforms()) { // simply return the dependency read stream if there are no transforms return readStream; } return transformer.transform(readStream, readContext); } var cache = lassoContext.cache; var cacheKey = dependency.getReadCacheKey(); if (cache && dependency.shouldCache(lassoContext) && cacheKey) { var readCache = cache.readCache; dependency.getLastModified(lassoContext).then(lastModified => { if (!lastModified || lastModified <= 0) { // This dependency does not support caching // so don't go through the caching layer deferredReadable.setWrappedStream(createTransformedStream(createReadStream())); return; } var cachedReadStream = readCache.createReadStream(cacheKey, { lastModified: lastModified, builder: function (callback) { // The read dependency has not been cached callback(null, createReadStream); } }); deferredReadable.setWrappedStream(createTransformedStream(cachedReadStream)); }); } else { deferredReadable.setWrappedStream(createTransformedStream(createReadStream())); } }); return deferredReadable; } function createReadBundleStream(bundle, lassoContext, transformerAsyncValue) { var combinedStream = new CombinedStream({ separator: '\n' }); if (!bundle.hasContent()) { return combinedStream; } var curIndex; var timeoutId; var timeout = lassoContext.config.getBundleReadTimeout(); if (timeout == null) { timeout = exports.DEFAULT_READ_TIMEOUT; } logger.info('Bundle read timeout value: ' + timeout); var dependencies = bundle.getDependencies(); var len = dependencies.length; combinedStream.on('beginStream', function (event) { curIndex = event.index; var dependency = event.stream._dependency; logger.debug('(' + (curIndex + 1) + ' of ' + len + ')', 'Begin reading dependency: ', dependency.toString()); if (timeout > 0) { timeoutId = setTimeout(onTimeout, timeout); } }); combinedStream.on('error', function () { if (timeoutId) { clearTimeout(timeoutId); } }); combinedStream.on('endStream', function (event) { if (timeoutId) { clearTimeout(timeoutId); } var dependency = event.stream._dependency; logger.debug('(' + (curIndex + 1) + ' of ' + len + ')', 'Completed reading dependency: ', dependency.toString()); }); logger.debug('Reading bundle: ' + bundle.getKey()); for (var i = 0; i < len; i++) { var dependency = dependencies[i]; if (dependency && dependency.hasContent() && !dependency.isExternalResource(lassoContext)) { // Each transform needs its own lassoContext since we update the lassoContext with the // current dependency and each dependency is transformed in parallel var readContext = Object.create(lassoContext || {}); readContext.dependency = dependency; readContext.bundle = bundle; var stream = createReadDependencyStream(dependency, readContext, transformerAsyncValue); // tag the stream with the dependency stream._dependency = dependency; combinedStream.addStream(stream); } } function onTimeout() { var dependency = dependencies[curIndex]; var message = 'Reading dependency timed out after ' + timeout + 'ms: ' + dependency.toString(); combinedStream.emit('error', new Error(message)); combinedStream.forEachStream(function (stream) { if (stream.end) { stream.end(); } }); } return combinedStream; } function createBundleReader(bundle, lassoContext) { ok(bundle, 'bundle is required'); ok(lassoContext, 'lassoContext is required'); var transformContext = Object.create(lassoContext || {}); transformContext.contentType = bundle.contentType; // TODO: Change to fully use async/await var transformerAsyncValue = new AsyncValue(); transforms.createTransformer(lassoContext.config.getTransforms(), transformContext).then(transformer => { transformerAsyncValue.resolve(transformer); }).catch(err => { transformerAsyncValue.reject(err); }); return { readBundle: function () { return createReadBundleStream(bundle, lassoContext, transformerAsyncValue); }, readDependency: function (dependency) { ok(dependency, 'dependency is required'); ok(typeof dependency.read === 'function', 'Invalid dependency'); return createReadDependencyStream(dependency, lassoContext, transformerAsyncValue); }, readBundleFully() { var _this = this; return _asyncToGenerator(function* () { if (!bundle.hasContent()) return ''; return new Promise(function (resolve, reject) { var hasError = false; function handleError(e) { if (hasError) { return; } hasError = true; reject(e); } var input = _this.readBundle(); var code = ''; var captureStream = through(function write(data) { code += data; }, function end() { if (hasError) { return; } resolve(code); }); input.on('error', handleError); captureStream.on('error', handleError); input.pipe(captureStream); }); })(); } }; } function createResourceReader(path, lassoContext) { return { readResource(options) { var readStream = fs.createReadStream(path, options); var filename = nodePath.basename(path); // Use the file extension as the content type var contentType = filename.substring(filename.lastIndexOf('.') + 1); var transformContext = Object.create(lassoContext || {}); transformContext.contentType = contentType; transformContext.path = path; transformContext.dir = nodePath.dirname(path); var readable = new DeferredReadable(); transforms.createTransformer(lassoContext.config.getTransforms(), transformContext).then(transformer => { if (transformer.hasTransforms() === false) { // simply use the input stream since there are no transforms after the filtering readable.setWrappedStream(readStream); return; } readable.setWrappedStream(transformer.transform(readStream, transformContext)); }).catch(err => { readable.emit('error', err); }); return readable; } }; } exports.DEFAULT_READ_TIMEOUT = 10000; exports.readBundle = createReadBundleStream; exports.createBundleReader = createBundleReader; exports.createResourceReader = createResourceReader;