blob: e8d4a6824955594461a98ffefeba8d27b715535d [file] [log] [blame]
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('memory-fs')
var logger = require('../../../util/logger')
var lifecycle = require('../../../util/lifecycle')
var globalOptions = require('../../../../webpack.config').webpack
// 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(localOptions) {
var log = logger.createLogger('middleware:webpack')
var options = _.defaults(localOptions || {}, 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()
}
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)
}
}