@roots/bud-server
Version:
Development server for @roots/bud
101 lines (100 loc) • 3.06 kB
JavaScript
/* eslint-disable no-console */
import { HotEventStream } from '@roots/bud-server/middleware/hot';
import loggerInstance from '@roots/bud-support/logger';
const middlewarePath = `/bud/hot`;
let latestStats = null;
let closed = false;
let logger;
export const factory = (app) => {
if (!app.compiler)
return;
logger = loggerInstance.scope(app.label, `hmr`);
return makeHandler(app.compiler.instance);
};
export const makeHandler = (compiler) => {
const stream = new HotEventStream();
const onInvalid = () => {
if (closed)
return;
stream.publish({ action: `building` });
};
const onDone = (stats) => {
if (closed)
return;
latestStats = stats;
publish(`built`, latestStats, stream);
};
compiler.hooks.invalid.tap(`bud-hot-middleware`, onInvalid);
compiler.hooks.done.tap(`bud-hot-middleware`, onDone);
const middleware = function (req, res, next) {
if (closed)
return next();
if (!req.url.endsWith(middlewarePath))
return next();
stream.handle(req, res);
if (latestStats) {
publish(`sync`, latestStats, stream);
}
};
// @ts-ignore
middleware.publish = function (payload) {
if (closed)
return;
stream.publish(payload);
};
// @ts-ignore
middleware.close = function () {
if (closed)
return;
closed = true;
stream.close();
// @ts-ignore https://github.com/webpack/tapable/issues/32#issuecomment-350644466
stream = null;
};
return middleware;
};
export const publish = (action, statsCompilation, stream) => {
const compilations = collectCompilations(statsCompilation.toJson({
all: false,
assets: true,
errorDetails: false,
errors: true,
hash: true,
timings: true,
warnings: true,
}));
compilations.forEach((stats) => {
const name = stats.name ?? statsCompilation.name ?? `unnamed`;
const modules = collectModules(stats.modules);
logger.log(`built`, name, `(${stats.hash})`, `in`, `${stats.time}ms`);
stream.publish({
action,
errors: stats.errors ?? [],
hash: stats.hash,
modules,
name,
time: stats.time,
warnings: stats.warnings ?? [],
});
});
};
export const collectModules = (modules) => {
if (!modules)
return {};
return modules?.reduce((modules, module) => {
if (!module.id || !module.name)
return modules;
return { ...modules, [module.id]: module.name };
}, {});
};
export const collectCompilations = (stats) => {
let collection = [];
// Stats has modules, single bundle
if (stats.modules)
collection.push(stats);
// Stats has children, multiple bundles
if (stats.children?.length)
collection.push(...stats.children);
// Not sure, assume single
return collection;
};