diff --git a/packages/core/src/build/eslintrc.js b/packages/core/src/build/eslintrc.js index f01cd66..8c9c294 100644 --- a/packages/core/src/build/eslintrc.js +++ b/packages/core/src/build/eslintrc.js @@ -2,13 +2,18 @@ const neutrino = require('neutrino'); const R = require('../bootstrap/require'); const {targetNeutrino} = require('../server/commands'); +const D = require('../debug'); const {default: Flecks} = require('../server/flecks'); +const debug = D('@flecks/core/.eslintrc.js'); + const { FLECKS_CORE_BUILD_TARGET = 'fleck', } = process.env; +debug('bootstrapping flecks...'); const flecks = Flecks.bootstrap(); +debug('bootstrapped'); const config = R(process.env[targetNeutrino(FLECKS_CORE_BUILD_TARGET)]); flecks.invokeFlat('@flecks/core/build', FLECKS_CORE_BUILD_TARGET, config); diff --git a/packages/core/src/build/webpack.config.js b/packages/core/src/build/webpack.config.js index aa9db41..cd06578 100644 --- a/packages/core/src/build/webpack.config.js +++ b/packages/core/src/build/webpack.config.js @@ -29,9 +29,11 @@ const buildList = FLECKS_CORE_BUILD_LIST .map((name) => name.trim()) .filter((e) => e); -const flecks = Flecks.bootstrap(); +export default (async () => { + debug('bootstrapping flecks...'); + const flecks = Flecks.bootstrap(); + debug('bootstrapped'); -const buildConfigs = async () => { debug('gathering configs'); let targets = flatten(flecks.invokeFlat('@flecks/core/targets')); if (buildList.length > 0) { @@ -68,6 +70,4 @@ const buildConfigs = async () => { await new Promise(() => {}); } return webpackConfigs; -}; - -export default buildConfigs(); +})(); diff --git a/packages/core/src/cli.js b/packages/core/src/cli.js index e29a960..5e7555c 100755 --- a/packages/core/src/cli.js +++ b/packages/core/src/cli.js @@ -79,31 +79,33 @@ else { const program = new Command(); program.enablePositionalOptions(); // Bootstrap. - debug('bootstrapping flecks...'); - const flecks = Flecks.bootstrap(); - debug('bootstrapped'); - // Register commands. - const commands = flecks.invokeReduce('@flecks/core/commands', undefined, undefined, program); - const keys = Object.keys(commands); - for (let i = 0; i < keys.length; ++i) { - const { - action, - args = [], - description, - name = keys[i], - options = [], - } = commands[keys[i]]; - debug('adding command %s...', name); - const cmd = program.command(name); - cmd.description(description); - for (let i = 0; i < args.length; ++i) { - cmd.addArgument(args[i]); + (async () => { + debug('bootstrapping flecks...'); + const flecks = Flecks.bootstrap(); + debug('bootstrapped'); + // Register commands. + const commands = flecks.invokeReduce('@flecks/core/commands', undefined, undefined, program); + const keys = Object.keys(commands); + for (let i = 0; i < keys.length; ++i) { + const { + action, + args = [], + description, + name = keys[i], + options = [], + } = commands[keys[i]]; + debug('adding command %s...', name); + const cmd = program.command(name); + cmd.description(description); + for (let i = 0; i < args.length; ++i) { + cmd.addArgument(args[i]); + } + for (let i = 0; i < options.length; ++i) { + cmd.option(...options[i]); + } + cmd.action(forwardProcessCode(action)); } - for (let i = 0; i < options.length; ++i) { - cmd.option(...options[i]); - } - cmd.action(forwardProcessCode(action)); - } - // Parse commandline. - program.parse(process.argv); + // Parse commandline. + program.parse(process.argv); + })(); } diff --git a/packages/core/src/debug.js b/packages/core/src/debug.js index 16636c4..b9b38dd 100644 --- a/packages/core/src/debug.js +++ b/packages/core/src/debug.js @@ -1,11 +1,11 @@ -import D from 'debug'; +const D = require('debug'); const { VSCODE_INSPECTOR_OPTIONS, } = process.env; let hasInitialized; -export default (name) => { +module.exports = (name) => { if (!hasInitialized) { // VSCode has a problem showing colors when formatting objects. if (VSCODE_INSPECTOR_OPTIONS) { diff --git a/packages/create-app/build/fleck.neutrinorc.js b/packages/create-app/build/fleck.neutrinorc.js index 4ab32ad..5cf70eb 100644 --- a/packages/create-app/build/fleck.neutrinorc.js +++ b/packages/create-app/build/fleck.neutrinorc.js @@ -5,41 +5,47 @@ const {join} = require('path'); const banner = require('@neutrinojs/banner'); const copy = require('@neutrinojs/copy'); -module.exports = require('@flecks/fleck/server/build/fleck.neutrinorc'); +module.exports = (async () => { + // eslint-disable-next-line global-require + const config = await require('@flecks/fleck/server/build/fleck.neutrinorc'); -module.exports.use.push(banner({ - banner: '#!/usr/bin/env node', - include: /^cli\.js$/, - pluginId: 'shebang', - raw: true, -})); + config.use.push(banner({ + banner: '#!/usr/bin/env node', + include: /^cli\.js$/, + pluginId: 'shebang', + raw: true, + })); -module.exports.use.push(({config}) => { - config - .plugin('executable') - .use(class Executable { + config.use.push(({config}) => { + config + .plugin('executable') + .use(class Executable { - // eslint-disable-next-line class-methods-use-this - apply(compiler) { - compiler.hooks.afterEmit.tapAsync( - 'Executable', - (compilation, callback) => { - chmod(join(__dirname, '..', 'dist', 'cli.js'), 0o755, callback); - }, - ); - } + // eslint-disable-next-line class-methods-use-this + apply(compiler) { + compiler.hooks.afterEmit.tapAsync( + 'Executable', + (compilation, callback) => { + chmod(join(__dirname, '..', 'dist', 'cli.js'), 0o755, callback); + }, + ); + } - }); -}); + }); + }); -module.exports.use.push( - copy({ - copyUnmodified: true, - patterns: [ - { - from: 'template', - to: 'template', - }, - ], - }), -); + config.use.push( + copy({ + copyUnmodified: true, + patterns: [ + { + from: 'template', + to: 'template', + }, + ], + }), + ); + + return config; + +})(); diff --git a/packages/create-fleck/build/fleck.neutrinorc.js b/packages/create-fleck/build/fleck.neutrinorc.js index 4ab32ad..5cf70eb 100644 --- a/packages/create-fleck/build/fleck.neutrinorc.js +++ b/packages/create-fleck/build/fleck.neutrinorc.js @@ -5,41 +5,47 @@ const {join} = require('path'); const banner = require('@neutrinojs/banner'); const copy = require('@neutrinojs/copy'); -module.exports = require('@flecks/fleck/server/build/fleck.neutrinorc'); +module.exports = (async () => { + // eslint-disable-next-line global-require + const config = await require('@flecks/fleck/server/build/fleck.neutrinorc'); -module.exports.use.push(banner({ - banner: '#!/usr/bin/env node', - include: /^cli\.js$/, - pluginId: 'shebang', - raw: true, -})); + config.use.push(banner({ + banner: '#!/usr/bin/env node', + include: /^cli\.js$/, + pluginId: 'shebang', + raw: true, + })); -module.exports.use.push(({config}) => { - config - .plugin('executable') - .use(class Executable { + config.use.push(({config}) => { + config + .plugin('executable') + .use(class Executable { - // eslint-disable-next-line class-methods-use-this - apply(compiler) { - compiler.hooks.afterEmit.tapAsync( - 'Executable', - (compilation, callback) => { - chmod(join(__dirname, '..', 'dist', 'cli.js'), 0o755, callback); - }, - ); - } + // eslint-disable-next-line class-methods-use-this + apply(compiler) { + compiler.hooks.afterEmit.tapAsync( + 'Executable', + (compilation, callback) => { + chmod(join(__dirname, '..', 'dist', 'cli.js'), 0o755, callback); + }, + ); + } - }); -}); + }); + }); -module.exports.use.push( - copy({ - copyUnmodified: true, - patterns: [ - { - from: 'template', - to: 'template', - }, - ], - }), -); + config.use.push( + copy({ + copyUnmodified: true, + patterns: [ + { + from: 'template', + to: 'template', + }, + ], + }), + ); + + return config; + +})(); diff --git a/packages/docker/src/start-container.js b/packages/docker/src/start-container.js index 8b1ce78..c07629c 100644 --- a/packages/docker/src/start-container.js +++ b/packages/docker/src/start-container.js @@ -1,4 +1,4 @@ -import {execSync, spawn} from 'child_process'; +import {exec, spawn} from 'child_process'; import {mkdir} from 'fs/promises'; import {tmpdir} from 'os'; import {join} from 'path'; @@ -7,23 +7,25 @@ import {D} from '@flecks/core'; const debug = D('@flecks/docker/container'); -const containerIsRunning = (name) => { - try { - const output = execSync( +const containerIsRunning = async (name) => ( + new Promise((r, e) => { + exec( `docker container inspect -f '{{.State.Running}}' ${name}`, {stdio: 'pipe'}, - ).toString(); - if ('true\n' === output) { - return true; - } - } - catch (e) { - if (1 !== e.status) { - throw e; - } - } - return false; -}; + (error, stdout) => { + if (error) { + if (1 !== e.status) { + e(error); + } + else { + r(false); + } + } + r('true\n' === stdout); + }, + ); + }) +); export default async (flecks, key, config) => { const {id} = flecks.get('@flecks/core'); diff --git a/packages/fleck/src/server/build/fleck.neutrinorc.js b/packages/fleck/src/server/build/fleck.neutrinorc.js index 9c18295..a97c1a7 100644 --- a/packages/fleck/src/server/build/fleck.neutrinorc.js +++ b/packages/fleck/src/server/build/fleck.neutrinorc.js @@ -12,52 +12,54 @@ const { const debug = D('@flecks/fleck/fleck.neutrino.js'); -debug('bootstrapping flecks...'); -const flecks = Flecks.bootstrap(); -debug('bootstrapped'); - const config = require('../../../../core/src/bootstrap/fleck.neutrinorc'); -const compiler = flecks.invokeFleck( - '@flecks/fleck/compiler', - flecks.get('@flecks/fleck.compiler'), -); -if (compiler) { - config.use.unshift(compiler); -} -else { - config.use.unshift((neutrino) => { - neutrino.config.plugins.delete('start-server'); +module.exports = (async () => { + debug('bootstrapping flecks...'); + const flecks = Flecks.bootstrap(); + debug('bootstrapped'); + + const compiler = flecks.invokeFleck( + '@flecks/fleck/compiler', + flecks.get('@flecks/fleck.compiler'), + ); + if (compiler) { + config.use.unshift(compiler); + } + else { + config.use.unshift((neutrino) => { + neutrino.config.plugins.delete('start-server'); + }); + const configFile = flecks.localConfig('babel.config.js', '@flecks/core'); + config.use.unshift(node({ + babel: {configFile}, + clean: { + cleanStaleWebpackAssets: false, + }, + })); + } + + // Augment the compiler with babel config from flecksrc. + config.use.push((neutrino) => { + const rcBabel = flecks.babel(); + debug('.flecksrc: babel: %O', rcBabel); + neutrino.config.module + .rule('compile') + .use('babel') + .tap((options) => babelmerge(options, ...rcBabel.map(([, babel]) => babel))); }); - const configFile = flecks.localConfig('babel.config.js', '@flecks/core'); - config.use.unshift(node({ - babel: {configFile}, - clean: { - cleanStaleWebpackAssets: false, - }, - })); -} -// Augment the compiler with babel config from flecksrc. -config.use.push((neutrino) => { - const rcBabel = flecks.babel(); - debug('.flecksrc: babel: %O', rcBabel); - neutrino.config.module - .rule('compile') - .use('babel') - .tap((options) => babelmerge(options, ...rcBabel.map(([, babel]) => babel))); -}); + config.use.push((neutrino) => { + // Test entrypoint. + const testPaths = glob.sync(join(FLECKS_CORE_ROOT, 'test/*.js')); + for (let i = 0; i < flecks.platforms.length; ++i) { + testPaths.push(...glob.sync(join(FLECKS_CORE_ROOT, `test/platforms/${flecks.platforms[i]}/*.js`))); + } + if (testPaths.length > 0) { + const testEntry = neutrino.config.entry('test').clear(); + testPaths.forEach((path) => testEntry.add(path)); + } + }); -config.use.push((neutrino) => { - // Test entrypoint. - const testPaths = glob.sync(join(FLECKS_CORE_ROOT, 'test/*.js')); - for (let i = 0; i < flecks.platforms.length; ++i) { - testPaths.push(...glob.sync(join(FLECKS_CORE_ROOT, `test/platforms/${flecks.platforms[i]}/*.js`))); - } - if (testPaths.length > 0) { - const testEntry = neutrino.config.entry('test').clear(); - testPaths.forEach((path) => testEntry.add(path)); - } -}); - -module.exports = config; + return config; +})(); diff --git a/packages/http/build/fleck.neutrinorc.js b/packages/http/build/fleck.neutrinorc.js index c49ceda..f4c57f6 100644 --- a/packages/http/build/fleck.neutrinorc.js +++ b/packages/http/build/fleck.neutrinorc.js @@ -1,22 +1,22 @@ -/* eslint-disable import/no-extraneous-dependencies */ -const config = require('@flecks/fleck/server/build/fleck.neutrinorc'); +// eslint-disable-next-line import/no-extraneous-dependencies const copy = require('@neutrinojs/copy'); -/* eslint-enable import/no-extraneous-dependencies */ -config.use.push(({config}) => { - config.entryPoints.delete('build/template'); -}); - -config.use.push( - copy({ - copyUnmodified: true, - patterns: [ - { - from: 'src/build/template.ejs', - to: 'build/template.ejs', - }, - ], - }), -); - -module.exports = config; +module.exports = (async () => { + // eslint-disable-next-line import/no-extraneous-dependencies, global-require + const config = await require('@flecks/fleck/server/build/fleck.neutrinorc'); + config.use.push(({config}) => { + config.entryPoints.delete('build/template'); + }); + config.use.push( + copy({ + copyUnmodified: true, + patterns: [ + { + from: 'src/build/template.ejs', + to: 'build/template.ejs', + }, + ], + }), + ); + return config; +})(); diff --git a/packages/http/src/server/build/http.neutrinorc.js b/packages/http/src/server/build/http.neutrinorc.js index 838d376..a0cf5f1 100644 --- a/packages/http/src/server/build/http.neutrinorc.js +++ b/packages/http/src/server/build/http.neutrinorc.js @@ -14,59 +14,59 @@ const { const debug = D('@flecks/http/http.neutrino.js'); -debug('bootstrapping flecks...'); -const flecks = Flecks.bootstrap(); -debug('bootstrapped'); - -// Neutrino configuration. -const config = { - options: { - output: 'dist', - root: FLECKS_CORE_ROOT, - }, - use: [ - ({config}) => { - config - .plugin('environment') - .use(EnvironmentPlugin, [{ - FLECKS_CORE_BUILD_TARGET: 'client', - }]); +module.exports = (async () => { + debug('bootstrapping flecks...'); + const flecks = Flecks.bootstrap(); + debug('bootstrapped'); + // Neutrino configuration. + const config = { + options: { + output: 'dist', + root: FLECKS_CORE_ROOT, }, - targets(flecks), - ], -}; -// Compile code. -const compiler = flecks.invokeFleck( - '@flecks/http/server/compiler', - flecks.get('@flecks/http/server.compiler'), -); -if (compiler) { - config.use.push(compiler); -} -else { - // Use neutrino's web middleware by default. - config.use.push(web({ - clean: false, - hot: false, - html: { - inject: false, - template: flecks.localConfig('template.ejs', '@flecks/http'), - }, - style: { - extract: { - enabled: false, + use: [ + ({config}) => { + config + .plugin('environment') + .use(EnvironmentPlugin, [{ + FLECKS_CORE_BUILD_TARGET: 'client', + }]); + }, + await targets(flecks), + ], + }; + // Compile code. + const compiler = flecks.invokeFleck( + '@flecks/http/server/compiler', + flecks.get('@flecks/http/server.compiler'), + ); + if (compiler) { + config.use.push(compiler); + } + else { + // Use neutrino's web middleware by default. + config.use.push(web({ + clean: false, + hot: false, + html: { + inject: false, + template: flecks.localConfig('template.ejs', '@flecks/http'), }, style: { - injectType: 'lazyStyleTag', + extract: { + enabled: false, + }, + style: { + injectType: 'lazyStyleTag', + }, }, - }, - })); -} -// Configure dev server. -config.use.push(devServer(flecks)); -// Build the client runtime. -config.use.push(runtime(flecks)); -// Output configuration. -config.use.push(outputs()); - -module.exports = config; + })); + } + // Configure dev server. + config.use.push(devServer(flecks)); + // Build the client runtime. + config.use.push(await runtime(flecks)); + // Output configuration. + config.use.push(outputs()); + return config; +})(); diff --git a/packages/http/src/server/build/runtime.js b/packages/http/src/server/build/runtime.js index c180218..a78283e 100644 --- a/packages/http/src/server/build/runtime.js +++ b/packages/http/src/server/build/runtime.js @@ -1,4 +1,4 @@ -const {realpathSync} = require('fs'); +const {realpath} = require('fs/promises'); const { dirname, join, @@ -10,116 +10,119 @@ const glob = require('glob'); const debug = D('@flecks/http/runtime'); -module.exports = (flecks) => (neutrino) => { +module.exports = async (flecks) => { debug('bootstrapping flecks...'); const httpFlecks = Flecks.bootstrap({platforms: ['client'], without: ['server']}); debug('bootstrapped'); - const {resolver} = httpFlecks; - const paths = Object.entries(resolver); - const source = [ - 'module.exports = (update) => (async () => ({', - " config: window[Symbol.for('@flecks/http/config')],", - ' flecks: Object.fromEntries(await Promise.all([', - paths - .map(([path]) => [ - ' [', - ` '${path}',`, - ` import('${path}').then((M) => (update(${paths.length}, '${path}'), M)),`, - ' ]', - ].join('\n')) - .join(',\n'), - ' ].map(async ([path, M]) => [path, await M]))),', - " platforms: ['client'],", - '}))();', - '', - ]; - // HMR. - source.push('if (module.hot) {'); - paths.forEach(([path]) => { - source.push(` module.hot.accept('${path}', () => {`); - source.push(` window.flecks.refresh('${path}', require('${path}'));`); - source.push(` window.flecks.invoke('@flecks/core/hmr', '${path}');`); - source.push(' });'); - }); - source.push('}'); - source.push(''); - // Create runtime. - const runtime = realpathSync(R.resolve(join(flecks.resolve('@flecks/http'), 'runtime'))); - neutrino.config.module - .rule(runtime) - .test(runtime) - .use('runtime/http') - .loader(runtime) - .options({ - source: source.join('\n'), + const runtime = await realpath(R.resolve(join(flecks.resolve('@flecks/http'), 'runtime'))); + const fullresolve = (fleck, path) => realpath(R.resolve(join(flecks.resolve(fleck), path))); + const entry = await fullresolve('@flecks/http', 'entry'); + const importLoader = await fullresolve('@flecks/http', 'import-loader'); + const tests = await realpath(R.resolve(join(flecks.resolve('@flecks/http'), 'tests'))); + return (neutrino) => { + const {resolver} = httpFlecks; + const paths = Object.entries(resolver); + const source = [ + 'module.exports = (update) => (async () => ({', + " config: window[Symbol.for('@flecks/http/config')],", + ' flecks: Object.fromEntries(await Promise.all([', + paths + .map(([path]) => [ + ' [', + ` '${path}',`, + ` import('${path}').then((M) => (update(${paths.length}, '${path}'), M)),`, + ' ]', + ].join('\n')) + .join(',\n'), + ' ].map(async ([path, M]) => [path, await M]))),', + " platforms: ['client'],", + '}))();', + '', + ]; + // HMR. + source.push('if (module.hot) {'); + paths.forEach(([path]) => { + source.push(` module.hot.accept('${path}', async () => {`); + source.push(` window.flecks.refresh('${path}', require('${path}'));`); + source.push(` window.flecks.invoke('@flecks/core/hmr', '${path}');`); + source.push(' });'); }); - neutrino.config.resolve.alias - .set('@flecks/http/runtime$', runtime); - flecks.runtimeCompiler('http', neutrino); - // Handle runtime import. - const fullresolve = (fleck, path) => realpathSync(R.resolve(join(flecks.resolve(fleck), path))); - const entry = fullresolve('@flecks/http', 'entry'); - neutrino.config.module - .rule(entry) - .test(entry) - .use('entry/http') - .loader(fullresolve('@flecks/http', 'import-loader')); - // Aliases. - const aliases = flecks.aliases(); - if (Object.keys(aliases).length > 0) { - Object.entries(aliases) - .forEach(([from, to]) => { - neutrino.config.resolve.alias - .set(from, to); + source.push('}'); + source.push(''); + // Create runtime. + neutrino.config.module + .rule(runtime) + .test(runtime) + .use('runtime/http') + .loader(runtime) + .options({ + source: source.join('\n'), }); - } - // Tests. - const testRoots = Array.from(new Set( - Object.keys(httpFlecks.resolver) - .map((fleck) => [fleck, httpFlecks.root(fleck)]), - )) - .map(([fleck, root]) => ( - [fleck, dirname(R.resolve(join(root, 'package.json')))] - )); - const testPaths = []; - testRoots.forEach(([fleck, root]) => { - testPaths.push(...( - glob.sync(join(root, 'test/*.js')) - .map((path) => [fleck, path]) - )); - for (let i = 0; i < httpFlecks.platforms.length; ++i) { - testPaths.push( - ...( - glob.sync(join(root, `test/platforms/${httpFlecks.platforms[i]}/*.js`)) - .map((path) => [fleck, path]) - ), - ); + neutrino.config.resolve.alias + .set('@flecks/http/runtime$', runtime); + flecks.runtimeCompiler('http', neutrino); + // Handle runtime import. + neutrino.config.module + .rule(entry) + .test(entry) + .use('entry/http') + .loader(importLoader); + // Aliases. + const aliases = flecks.aliases(); + if (Object.keys(aliases).length > 0) { + Object.entries(aliases) + .forEach(([from, to]) => { + neutrino.config.resolve.alias + .set(from, to); + }); } - }); - // Test entrypoint. - if (testPaths.length > 0) { - const testEntry = neutrino.config.entry('test').clear(); - testPaths.forEach(([, path]) => testEntry.add(path)); - } - const tests = realpathSync(R.resolve(join(flecks.resolve('@flecks/http'), 'tests'))); - neutrino.config.module - .rule(tests) - .test(tests) - .use('runtime/test') - .loader(runtime) - .options({ - source: Object.entries( - testPaths - .reduce( - (r, [fleck, path]) => ({ - ...r, - [fleck]: [...(r[fleck] || []), `require('${path}');`], - }), - {}, + // Tests. + const testRoots = Array.from(new Set( + Object.keys(httpFlecks.resolver) + .map((fleck) => [fleck, httpFlecks.root(fleck)]), + )) + .map(([fleck, root]) => ( + [fleck, dirname(R.resolve(join(root, 'package.json')))] + )); + const testPaths = []; + testRoots.forEach(([fleck, root]) => { + testPaths.push(...( + glob.sync(join(root, 'test/*.js')) + .map((path) => [fleck, path]) + )); + for (let i = 0; i < httpFlecks.platforms.length; ++i) { + testPaths.push( + ...( + glob.sync(join(root, `test/platforms/${httpFlecks.platforms[i]}/*.js`)) + .map((path) => [fleck, path]) ), - ) - .map( - ([original, paths]) => `describe('${original}', () => { ${paths.join(' ')} });`, - ).join(''), + ); + } }); + // Test entrypoint. + if (testPaths.length > 0) { + const testEntry = neutrino.config.entry('test').clear(); + testPaths.forEach(([, path]) => testEntry.add(path)); + } + neutrino.config.module + .rule(tests) + .test(tests) + .use('runtime/test') + .loader(runtime) + .options({ + source: Object.entries( + testPaths + .reduce( + (r, [fleck, path]) => ({ + ...r, + [fleck]: [...(r[fleck] || []), `require('${path}');`], + }), + {}, + ), + ) + .map( + ([original, paths]) => `describe('${original}', () => { ${paths.join(' ')} });`, + ).join(''), + }); + }; }; diff --git a/packages/http/src/server/build/targets.js b/packages/http/src/server/build/targets.js index be9a4e5..61da9d0 100644 --- a/packages/http/src/server/build/targets.js +++ b/packages/http/src/server/build/targets.js @@ -1,6 +1,6 @@ /* eslint-disable no-param-reassign */ const {dirname, join} = require('path'); -const {realpathSync} = require('fs'); +const {realpath} = require('fs/promises'); const {require: R} = require('@flecks/core/server'); @@ -8,19 +8,22 @@ const { FLECKS_CORE_ROOT = process.cwd(), } = process.env; -module.exports = (flecks) => (neutrino) => { - const {options} = neutrino; - const {output: originalOutput} = options; - neutrino.config.resolve.modules.merge([ - join(FLECKS_CORE_ROOT, 'node_modules'), - 'node_modules', - ]); - options.root = realpathSync(dirname(R.resolve(join(flecks.resolve('@flecks/http'), 'entry.js')))); - options.source = '.'; - options.mains.index = 'entry'; - options.mains.tests = { - entry: './client/tests', - title: 'Testbed', +module.exports = async (flecks) => { + const root = await realpath(dirname(R.resolve(join(flecks.resolve('@flecks/http'), 'entry.js')))); + return (neutrino) => { + const {options} = neutrino; + const {output: originalOutput} = options; + neutrino.config.resolve.modules.merge([ + join(FLECKS_CORE_ROOT, 'node_modules'), + 'node_modules', + ]); + options.root = root; + options.source = '.'; + options.mains.index = 'entry'; + options.mains.tests = { + entry: './client/tests', + title: 'Testbed', + }; + options.output = join(originalOutput, flecks.get('@flecks/http/server.output')); }; - options.output = join(originalOutput, flecks.get('@flecks/http/server.output')); }; diff --git a/packages/server/src/server/build/runtime.js b/packages/server/src/server/build/runtime.js index 7319c27..2f95ae6 100644 --- a/packages/server/src/server/build/runtime.js +++ b/packages/server/src/server/build/runtime.js @@ -1,79 +1,81 @@ -const {realpathSync} = require('fs'); +const {realpath} = require('fs/promises'); const {join} = require('path'); const {require: R} = require('@flecks/core/server'); -module.exports = (flecks) => (neutrino) => { - const {config, resolver} = flecks; - // Inject flecks configuration. - const paths = Object.keys(resolver); - const source = [ - "process.env.FLECKS_CORE_BUILD_TARGET = 'server';", - 'module.exports = (async () => ({', - ` config: ${JSON.stringify(config)},`, - ' flecks: Object.fromEntries(await Promise.all([', - paths.map((path) => ` ['${path}', import('${path}')]`).join(',\n'), - ' ].map(async ([path, M]) => [path, await M]))),', - " platforms: ['server']", - '}))();', - ]; - // HMR. - source.push('if (module.hot) {'); - // Keep HMR junk out of our output path. - source.push(' const {unlink} = require("fs/promises");'); - source.push(' const {join} = require("path");'); - source.push(' let previousHash = __webpack_hash__;'); - source.push(' module.hot.addStatusHandler((status) => {'); - source.push(' if ("idle" === status) {'); - source.push(' require("glob")('); - source.push(` join('${neutrino.options.output}', \`*\${previousHash}.hot-update.*\`),`); - source.push(' async (error, disposing) => {'); - source.push(' if (error) {'); - source.push(' throw error;'); - source.push(' return;'); - source.push(' }'); - source.push(' await Promise.all(disposing.map(unlink));'); - source.push(' },'); - source.push(' );'); - source.push(' previousHash = __webpack_hash__;'); - source.push(' }'); - source.push(' });'); - // Hooks for each fleck. - paths.forEach((path) => { - source.push(` module.hot.accept('${path}', () => {`); - source.push(` global.flecks.refresh('${path}', require('${path}'));`); - source.push(` global.flecks.invoke('@flecks/core/hmr', '${path}');`); +module.exports = async (flecks) => { + const runtime = await realpath(R.resolve(join(flecks.resolve('@flecks/server'), 'runtime'))); + return (neutrino) => { + const {config, resolver} = flecks; + // Inject flecks configuration. + const paths = Object.keys(resolver); + const source = [ + "process.env.FLECKS_CORE_BUILD_TARGET = 'server';", + 'module.exports = (async () => ({', + ` config: ${JSON.stringify(config)},`, + ' flecks: Object.fromEntries(await Promise.all([', + paths.map((path) => ` ['${path}', import('${path}')]`).join(',\n'), + ' ].map(async ([path, M]) => [path, await M]))),', + " platforms: ['server']", + '}))();', + ]; + // HMR. + source.push('if (module.hot) {'); + // Keep HMR junk out of our output path. + source.push(' const {unlink} = require("fs/promises");'); + source.push(' const {join} = require("path");'); + source.push(' let previousHash = __webpack_hash__;'); + source.push(' module.hot.addStatusHandler((status) => {'); + source.push(' if ("idle" === status) {'); + source.push(' require("glob")('); + source.push(` join('${neutrino.options.output}', \`*\${previousHash}.hot-update.*\`),`); + source.push(' async (error, disposing) => {'); + source.push(' if (error) {'); + source.push(' throw error;'); + source.push(' return;'); + source.push(' }'); + source.push(' await Promise.all(disposing.map(unlink));'); + source.push(' },'); + source.push(' );'); + source.push(' previousHash = __webpack_hash__;'); + source.push(' }'); source.push(' });'); - }); - source.push('}'); - // Create runtime. - const entries = neutrino.config.entry('index'); - const runtime = realpathSync(R.resolve(join(flecks.resolve('@flecks/server'), 'runtime'))); - neutrino.config.module - .rule(runtime) - .test(runtime) - .use('runtime') - .loader(runtime) - .options({ - source: source.join('\n'), + // Hooks for each fleck. + paths.forEach((path) => { + source.push(` module.hot.accept('${path}', async () => {`); + source.push(` global.flecks.refresh('${path}', require('${path}'));`); + source.push(` global.flecks.invoke('@flecks/core/hmr', '${path}');`); + source.push(' });'); }); - const allowlist = [ - '@flecks/server/entry', - '@flecks/server/runtime', - /^@babel\/runtime\/helpers\/esm/, - ]; - neutrino.config.resolve.alias - .set('@flecks/server/runtime$', runtime); - flecks.runtimeCompiler('server', neutrino, allowlist); - // Rewrite to signals for HMR. - if ('production' !== neutrino.config.get('mode')) { - allowlist.push(/^webpack/); - if (entries.has(`${R.resolve('webpack/hot/poll')}?1000`)) { - entries.delete(`${R.resolve('webpack/hot/poll')}?1000`); - entries.add('webpack/hot/signal'); + source.push('}'); + // Create runtime. + const entries = neutrino.config.entry('index'); + neutrino.config.module + .rule(runtime) + .test(runtime) + .use('runtime') + .loader(runtime) + .options({ + source: source.join('\n'), + }); + const allowlist = [ + '@flecks/server/entry', + '@flecks/server/runtime', + /^@babel\/runtime\/helpers\/esm/, + ]; + neutrino.config.resolve.alias + .set('@flecks/server/runtime$', runtime); + flecks.runtimeCompiler('server', neutrino, allowlist); + // Rewrite to signals for HMR. + if ('production' !== neutrino.config.get('mode')) { + allowlist.push(/^webpack/); + if (entries.has(`${R.resolve('webpack/hot/poll')}?1000`)) { + entries.delete(`${R.resolve('webpack/hot/poll')}?1000`); + entries.add('webpack/hot/signal'); + } } - } - // Externalize the rest. - const nodeExternals = R('webpack-node-externals'); - neutrino.config.externals(nodeExternals({allowlist})); + // Externalize the rest. + const nodeExternals = R('webpack-node-externals'); + neutrino.config.externals(nodeExternals({allowlist})); + }; }; diff --git a/packages/server/src/server/build/server.neutrinorc.js b/packages/server/src/server/build/server.neutrinorc.js index b2af31b..403f73d 100644 --- a/packages/server/src/server/build/server.neutrinorc.js +++ b/packages/server/src/server/build/server.neutrinorc.js @@ -14,121 +14,124 @@ const { const debug = D('@flecks/server/server.neutrino.js'); -debug('bootstrapping flecks...'); -const flecks = Flecks.bootstrap(); -debug('bootstrapped'); +module.exports = (async () => { -const { - hot, - inspect, - profile, - start: isStarting, -} = flecks.get('@flecks/server'); + debug('bootstrapping flecks...'); + const flecks = Flecks.bootstrap(); + debug('bootstrapped'); -const entry = (neutrino) => { - const entries = neutrino.config.entry('index'); - entries.delete(join(FLECKS_CORE_ROOT, 'src', 'index')); - entries.add('@flecks/server/entry'); -}; - -// Augment the application-starting configuration. -const start = (neutrino) => { - if (isStarting) { - neutrino.use(startServer({name: 'index.js'})); - } - if (!neutrino.config.plugins.has('start-server')) { - return; - } - neutrino.config - .plugin('start-server') - .tap((args) => { - const options = args[0]; - options.keyboard = false; - // HMR. - options.signal = !!hot; - // Debugging. - if (inspect) { - options.nodeArgs.push('--inspect'); - } - // Profiling. - if (profile) { - options.nodeArgs.push('--prof'); - } - // Bail hard on unhandled rejections and report. - options.nodeArgs.push('--unhandled-rejections=strict'); - options.nodeArgs.push('--trace-uncaught'); - return args; - }); -}; - -const compiler = flecks.invokeFleck( - '@flecks/server/compiler', - flecks.get('@flecks/server.compiler'), -); - -const config = { - options: { - output: 'dist', - root: FLECKS_CORE_ROOT, - }, - use: [ - entry, - start, - ], -}; - -if (compiler) { - config.use.unshift(compiler); -} -else { - config.use.unshift((neutrino) => { - // Default to not starting application on build. - neutrino.config.plugins.delete('start-server'); - }); - config.use.unshift(node({ - clean: false, + const { hot, - })); -} -// Stub out non-server-friendly modules on the server. -const stubs = flecks.stubs(); -if (stubs.length > 0) { - config.use.unshift(({config}) => { - stubs.forEach((path) => { - config.resolve.alias - .set(path, '@flecks/core/empty'); + inspect, + profile, + start: isStarting, + } = flecks.get('@flecks/server'); + + const entry = (neutrino) => { + const entries = neutrino.config.entry('index'); + entries.delete(join(FLECKS_CORE_ROOT, 'src', 'index')); + entries.add('@flecks/server/entry'); + }; + + // Augment the application-starting configuration. + const start = (neutrino) => { + if (isStarting) { + neutrino.use(startServer({name: 'index.js'})); + } + if (!neutrino.config.plugins.has('start-server')) { + return; + } + neutrino.config + .plugin('start-server') + .tap((args) => { + const options = args[0]; + options.keyboard = false; + // HMR. + options.signal = !!hot; + // Debugging. + if (inspect) { + options.nodeArgs.push('--inspect'); + } + // Profiling. + if (profile) { + options.nodeArgs.push('--prof'); + } + // Bail hard on unhandled rejections and report. + options.nodeArgs.push('--unhandled-rejections=strict'); + options.nodeArgs.push('--trace-uncaught'); + return args; + }); + }; + + const compiler = flecks.invokeFleck( + '@flecks/server/compiler', + flecks.get('@flecks/server.compiler'), + ); + + const config = { + options: { + output: 'dist', + root: FLECKS_CORE_ROOT, + }, + use: [ + entry, + start, + ], + }; + + if (compiler) { + config.use.unshift(compiler); + } + else { + config.use.unshift((neutrino) => { + // Default to not starting application on build. + neutrino.config.plugins.delete('start-server'); }); + config.use.unshift(node({ + clean: false, + hot, + })); + } + // Stub out non-server-friendly modules on the server. + const stubs = flecks.stubs(); + if (stubs.length > 0) { + config.use.unshift(({config}) => { + stubs.forEach((path) => { + config.resolve.alias + .set(path, '@flecks/core/empty'); + }); + }); + } + // Hardcore hax for module aliasing. + const aliases = flecks.aliases(); + if (Object.keys(aliases).length > 0) { + const code = [ + `const aliases = ${JSON.stringify(aliases)};`, + 'const {Module} = require("module");', + 'const {require: Mr} = Module.prototype;', + 'Module.prototype.require = function hackedRequire(request, options) {', + ' if (aliases[request]) {', + ' return Mr.call(this, aliases[request], options);', + ' }', + ' return Mr.call(this, request, options);', + '};', + ].join('\n'); + config.use.push(banner({ + banner: code, + pluginId: 'aliases-banner', + })); + } + + // Build the server runtime. + config.use.push(await runtime(flecks)); + + // Give the resolver a helping hand. + config.use.push((neutrino) => { + neutrino.config.resolve.modules.merge([ + join(FLECKS_CORE_ROOT, 'node_modules'), + 'node_modules', + ]); }); -} -// Hardcore hax for module aliasing. -const aliases = flecks.aliases(); -if (Object.keys(aliases).length > 0) { - const code = [ - `const aliases = ${JSON.stringify(aliases)};`, - 'const {Module} = require("module");', - 'const {require: Mr} = Module.prototype;', - 'Module.prototype.require = function hackedRequire(request, options) {', - ' if (aliases[request]) {', - ' return Mr.call(this, aliases[request], options);', - ' }', - ' return Mr.call(this, request, options);', - '};', - ].join('\n'); - config.use.push(banner({ - banner: code, - pluginId: 'aliases-banner', - })); -} -// Build the server runtime. -config.use.push(runtime(flecks)); - -// Give the resolver a helping hand. -config.use.push((neutrino) => { - neutrino.config.resolve.modules.merge([ - join(FLECKS_CORE_ROOT, 'node_modules'), - 'node_modules', - ]); -}); - -module.exports = config; + return config; +})();