UNPKG

@roots/bud-server

Version:

Development server for @roots/bud

101 lines (100 loc) 3.06 kB
/* 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; };