koa-es-modules-webpack
Version:
Koa middleware to serve ES6 Modules using Webpack bundler
115 lines (100 loc) • 2.86 kB
JavaScript
const Path = require('path');
const fs = require('fs-extra');
const webpack = require('webpack');
const _debug = require('debug')('koa-es-modules-webpack');
// const _debug = (...msg) => console.log('koa-es-modules-webpack', ...msg)
const mandate = reason => { throw new Error(reason) }
const cache = {};
const watcherCache = {};
const defaultWebpackConfig = {};
module.exports = ({
root = mandate('Root is required'),
cacheDir = Path.join(root, '.webpack-cache'),
ext = ['js'],
webpackConfig: webpackConfigArg = {},
warn = false,
watch = false,
} = {}) => async(ctx, next) => {
const debug = (...msg) => _debug(ctx.path, ...msg)
if (ctx.method !== 'GET') {
debug('Not processing', 'method!=GET');
return next();
}
if (ext.indexOf(ctx.path.substr(-2)) == -1) {
debug('Not processing', 'ext!=' + ext);
return next();
}
const path = Path.join(root, ctx.path);
const cachePath = Path.join(cacheDir, ctx.path);
if (!watch || watcherCache[path]) {
if (cache[path]) {
debug('Serving from mem-cache');
return ctx.body = cache[path];
}
try {
ctx.body = cache[path] = await fs.readFile(cachePath, 'utf8');
debug('Serving from file-cache');
return;
} catch (err) {
// debug(`Couldn't load from file-cache`, cachePath, err.message);
}
}
try {
await fs.access(path);
} catch (err) {
debug('Not processing, !exists', path, err.message);
return next();
}
debug('Processing', path);
const webpackConfig = Object.assign({
entry: path,
output: {
path: cacheDir,
filename: ctx.path.substr(1),
}
}, defaultWebpackConfig, webpackConfigArg);
const compiler = webpack(webpackConfig);
const run = cb => new Promise((ok, x) => {
let ret;
const pcb = (...args) => cb(...args).then(() => ok(ret)).catch(x)
ret = watch ? compiler.watch({}, pcb) : compiler.run(pcb);
});
let responded = false
const respond = (err, data) => {
if (err) {
console.error(err);
if (!responded) {
responded = true;
next(err);
}
} else if (responded) {
debug('Already responded for this request');
} else {
debug('Responding with generated code');
responded = true;
ctx.body = data;
}
}
watcherCache[path] = await run(async(err, stats) => {
debug('Bundle generation triggered');
try {
if (err) {
respond(err);
return;
}
const info = stats.toJson();
if (stats.hasErrors()) {
respond(info.errors);
return;
}
if (warn && stats.hasWarnings()) {
console.warn(info.warnings);
}
cache[path] = await fs.readFile(cachePath, 'utf8');
respond(null, cache[path]);
debug('Bundle generated');
} catch (error) {
respond(err);
}
});
};