var path = require('path') var url = require('url') var webpack = require('webpack') var mime = require('mime') var Promise = require('bluebird') var _ = require('lodash') var MemoryFileSystem = require('webpack/node_modules/memory-fs') var logger = require('../../../util/logger') var lifecycle = require('../../../util/lifecycle') var globalOptions = require('../../../../webpack.config') // Similar to webpack-dev-middleware, but integrates with our custom // lifecycle, behaves more like normal express middleware, and removes // all unnecessary features. module.exports = function(options) { var log = logger.createLogger('middleware:webpack') options = _.defaults(options || {}, globalOptions) var compiler = webpack(options) var fs = compiler.outputFileSystem = new MemoryFileSystem() var valid = false var queue = [] log.info('Creating bundle') var watching = compiler.watch(options.watchDelay, function(err) { if (err) { log.fatal('Webpack had an error', err.stack) lifecycle.fatal() } }) lifecycle.observe(function() { if (watching.watcher) { watching.watcher.close() } }) function doneListener(stats) { process.nextTick(function() { if (valid) { log.info(stats.toString(options.stats)) if (stats.hasErrors()) { log.error('Bundle has errors') } else if (stats.hasWarnings()) { log.warn('Bundle has warnings') } else { log.info('Bundle is now valid') } queue.forEach(function(resolver) { resolver.resolve() }) } }) valid = true } function invalidate() { if (valid) { log.info('Bundle is now invalid') valid = false } } compiler.plugin('done', doneListener) compiler.plugin('invalid', invalidate) compiler.plugin('compile', invalidate) function bundle() { if (valid) { return Promise.resolve() } else { log.info('Waiting for bundle to finish') var resolver = Promise.defer() queue.push(resolver) return resolver.promise } } return function(req, res, next) { var parsedUrl = url.parse(req.url) var target = path.join( compiler.outputPath , parsedUrl.pathname ) bundle() .then(function() { try { var body = fs.readFileSync(target) res.set('Content-Type', mime.lookup(target)) res.end(body) } catch (err) { return next() } }) .catch(next) } }