diff --git a/packages/electron/src/server/index.js b/packages/electron/src/server/index.js index 618d121..84a30f6 100644 --- a/packages/electron/src/server/index.js +++ b/packages/electron/src/server/index.js @@ -1,7 +1,5 @@ -import cluster from 'cluster'; import {join} from 'path'; -import {require as R} from '@flecks/core/server'; import banner from '@neutrinojs/banner'; const { @@ -58,29 +56,14 @@ export const hooks = { url: undefined, }), '@flecks/core.webpack': (target, config) => { - const StartServerWebpackPlugin = R('start-server-webpack-plugin'); - const plugin = config.plugins.find((plugin) => plugin instanceof StartServerWebpackPlugin); + const plugin = config.plugins.find(({pluginName}) => pluginName === 'StartServerPlugin'); // Extremely hackish, c'est la vie. if (plugin) { - /* eslint-disable no-underscore-dangle */ - plugin._startServer = function _startServerHacked(callback) { - const execArgv = this._getArgs(); - const inspectPort = this._getInspectPort(execArgv); - const clusterOptions = { - args: [this._entryPoint], - exec: join(FLECKS_CORE_ROOT, 'node_modules', '.bin', 'electron'), - execArgv, - }; - if (inspectPort) { - clusterOptions.inspectPort = inspectPort; - } - cluster.setupMaster(clusterOptions); - cluster.on('online', (worker) => { - callback(worker); - }); - cluster.fork(); + const {exec} = plugin.options; + plugin.options.exec = (compilation) => { + plugin.options.args = [compilation.assets[exec].existsAt]; + return join(FLECKS_CORE_ROOT, 'node_modules', '.bin', 'electron'); }; - /* eslint-enable no-underscore-dangle */ } }, '@flecks/electron/server.initialize': async (electron, flecks) => { diff --git a/packages/server/src/server/build/server.neutrinorc.js b/packages/server/src/server/build/server.neutrinorc.js index aeb50b8..f4eab42 100644 --- a/packages/server/src/server/build/server.neutrinorc.js +++ b/packages/server/src/server/build/server.neutrinorc.js @@ -3,9 +3,9 @@ const {join} = require('path'); const {require: R} = require('@flecks/core/server'); const banner = require('@neutrinojs/banner'); const clean = require('@neutrinojs/clean'); -const startServer = require('@neutrinojs/start-server'); const runtime = require('./runtime'); +const startServer = require('./start'); const { FLECKS_CORE_ROOT = process.cwd(), @@ -69,23 +69,13 @@ module.exports = async (flecks) => { // Augment the application-starting configuration. const start = (neutrino) => { if (isStarting) { - neutrino.use(startServer({name: 'index.js'})); - // Really dumb that I can't just pass these in. - neutrino.config - .plugin('start-server') - .tap((args) => { - const options = args[0]; - options.keyboard = false; - // HMR. - options.signal = !!hot; - // Node args. - options.nodeArgs.push(...nodeArgs); - // Bail hard on unhandled rejections and report. - options.nodeArgs.push('--unhandled-rejections=strict'); - options.nodeArgs.push('--trace-uncaught'); - return args; - }); - + neutrino.use(startServer({ + exec: 'index.js', + // Bail hard on unhandled rejections and report. + nodeArgs: [...nodeArgs, '--unhandled-rejections=strict', '--trace-uncaught'], + // HMR. + signal: !!hot, + })); } }; diff --git a/packages/server/src/server/build/start.js b/packages/server/src/server/build/start.js new file mode 100644 index 0000000..755a4c3 --- /dev/null +++ b/packages/server/src/server/build/start.js @@ -0,0 +1,100 @@ +const cluster = require('cluster'); + +class StartServerPlugin { + + pluginName = 'StartServerPlugin'; + + worker = null; + + constructor(options = {}) { + this.options = 'string' === typeof options + ? {name: options} + : { + args: [], + nodeArgs: [], + signal: false, + ...options, + }; + } + + apply(compiler) { + const {options: {exec, signal}, pluginName} = this; + const logger = compiler.getInfrastructureLogger(pluginName); + compiler.hooks.afterEmit.tapAsync(pluginName, (compilation, callback) => { + if (this.worker && this.worker.isConnected()) { + if (signal) { + process.kill(this.worker.process.pid, true === signal ? 'SIGUSR2' : signal); + } + callback(); + return; + } + let entryPoint; + if (!exec) { + entryPoint = compilation.assets[Object.keys(compilation.assets)[0]].existsAt; + } + else if (compilation.assets[exec]) { + entryPoint = compilation.assets[exec].existsAt; + } + else if ('string' === typeof exec) { + entryPoint = exec; + } + else { + entryPoint = exec(compilation); + } + this.startServer( + entryPoint, + callback, + ); + }); + compiler.hooks.shouldEmit.tap(pluginName, (compilation) => { + const entryPoints = Object.keys(compilation.assets); + if ('string' === typeof exec) { + return true; + } + if (!exec && entryPoints.length > 1) { + logger.warn(`No entrypoint selected, defaulting to '${entryPoints[0]}'. Other entrypoints: '${entryPoints.slice(1).join("', '")}'`); + } + return true; + }); + } + + static inspectPortFromExecArgv(execArgv) { + const inspectArg = execArgv.find((arg) => arg.includes('--inspect')); + if (!inspectArg || !inspectArg.includes('=')) { + return undefined; + } + const [, hostPort] = inspectArg.split('='); + const port = hostPort.includes(':') ? hostPort.split(':')[1] : hostPort; + return parseInt(port, 10); + } + + startServer(exec, callback) { + const {args, nodeArgs} = this.options; + const execArgv = nodeArgs.concat(process.execArgv); + const inspectPort = this.constructor.inspectPortFromExecArgv(execArgv); + cluster.setupPrimary({ + exec, + execArgv, + args, + ...(inspectPort && {inspectPort}), + }); + cluster.on('online', () => callback()); + this.worker = cluster.fork(); + } + +} + +module.exports = ({ + nodeArgs = [], + pluginId = 'start-server', + ...pluginOptions +} = {}) => ( + ({config, options}) => { + config + .plugin(pluginId) + .use( + StartServerPlugin, + [{...pluginOptions, nodeArgs: nodeArgs.concat(options.debug ? ['--inspect'] : [])}], + ); + } +);