import { logger } from '@spine/logger'; import { notify } from '@erect/server/utils/notify'; import { config } from '@erect/core/config/index'; import { HotNodeServer } from './hotNodeServer'; import { HotClientServer } from './hotClientServer'; import { buildVendorDll, registerVendorDll, usesVendorDll, } from '../webpack/buildVendorDll'; import { createBuildOptions } from '../webpack/createBuildOptions'; import { webpackConfigFactory, getBundle } from '../webpack/configFactory'; import { plugin } from './plugin'; const log = logger('erect:develop'); const initializeBundle = (name: string) => { const bundleConfig = getBundle('server'); const createCompiler = () => { try { const webpackFactory = webpackConfigFactory( createBuildOptions({ target: name, development: true, }), log.logger.child(name), ); if (usesVendorDll(bundleConfig)) { registerVendorDll(name, webpackFactory); } webpackFactory.register((webpackConfig) => { webpackConfig.bail = true; return webpackConfig; }); return webpackFactory; } catch (err) { log.error('Webpack config is invalid.'); notify('✖️ Webpack config is invalid, please check the console for more information.'); console.error(err); throw err; } }; return { name, bundleConfig, createCompiler }; }; export class HotDevelopment { hotClientServer: HotClientServer | void = undefined; hotNodeServers: HotNodeServer[]; constructor(public restarted = false) { this.hotNodeServers = []; const clientBundleConfig = getBundle('client'); const clientBundle = initializeBundle('client'); const nodeBundles = [initializeBundle('server')].concat( Object.keys(config.additionalNodeBundles).map(name => initializeBundle(name), ), ); Promise.resolve( usesVendorDll(clientBundleConfig) ? buildVendorDll({ target: 'client', development: true }) : true, ) // Then start the client development server. .then( () => new Promise((resolve, reject) => { const { createCompiler } = clientBundle; const webpackFactory = createCompiler(); const compiler = webpackFactory.compile(); compiler.hooks.done.tap(plugin, (stats) => { if (stats.hasErrors()) { reject(stats.compilation.errors[0]); } else { resolve(compiler); } }); this.hotClientServer = new HotClientServer(compiler, webpackFactory.log); }), ) // Then start the node development server(s). .then((clientCompiler: any) => { this.hotNodeServers = nodeBundles.map( ({ name, createCompiler }) => { // $FlowFixMe const webpackFactory = createCompiler(); return new HotNodeServer(name, webpackFactory.compile(), clientCompiler, webpackFactory.log, restarted); }, ); }) .catch(error => { console.error(error.stack); // process.exit(0); }); } async dispose(): Promise { const safeDisposer = (server: HotNodeServer | HotClientServer | void): Promise => ( server ? server.dispose() : Promise.resolve() ); // First the hot client server. await safeDisposer(this.hotClientServer); // Then dispose the hot node server(s). await Promise.all(this.hotNodeServers.map((server) => safeDisposer(server))); } }