flecks/packages/web/build/runtime.js

162 lines
4.9 KiB
JavaScript
Raw Normal View History

2024-01-16 00:28:20 -06:00
const {access, readFile} = require('fs/promises');
2022-02-26 11:58:54 -06:00
const {
2024-01-13 06:19:49 -06:00
basename,
2022-02-26 11:58:54 -06:00
dirname,
2024-01-13 06:19:49 -06:00
extname,
2022-02-26 11:58:54 -06:00
join,
} = require('path');
2022-02-25 04:58:08 -06:00
2024-01-16 00:28:20 -06:00
const Server = require('@flecks/core/build/server');
const {glob} = require('@flecks/core/server');
2022-02-25 04:58:08 -06:00
2023-11-30 21:41:42 -06:00
module.exports = async (config, env, argv, flecks) => {
2024-01-16 00:28:20 -06:00
const buildFlecks = await Server.from({
2024-01-20 03:40:20 -06:00
config: flecks.realiasedConfig,
2022-03-10 14:32:51 -06:00
platforms: ['client', '!server'],
});
2024-01-16 00:28:20 -06:00
const {resolver, flecks: webFlecks} = buildFlecks;
const paths = Object.keys(webFlecks)
.filter((fleck) => !['@flecks/server'].includes(fleck));
2022-03-17 15:15:39 -05:00
const styles = (
await Promise.all(
2024-01-20 02:03:50 -06:00
Object.keys(flecks.flecks)
.map(async (fleck) => {
// No root? How to infer?
const [root] = Object.entries(flecks.roots)
.find(([root]) => fleck.startsWith(root)) || [];
if (!root) {
return undefined;
}
2024-01-13 06:19:49 -06:00
// Compiled? It will be included with the compilation.
2024-01-20 03:40:20 -06:00
if (
Object.entries(flecks.compiled)
.some(([, {flecks}]) => flecks.includes(fleck))
) {
2024-01-13 06:19:49 -06:00
return undefined;
2022-04-02 07:29:28 -05:00
}
2022-03-17 15:15:39 -05:00
try {
2024-01-20 02:03:50 -06:00
const sub = fleck.slice(root.length + 1) || 'index';
const style = join(root, 'assets', `${basename(sub, extname(sub))}.css`);
await access(await flecks.resolver.resolve(style));
2024-01-13 06:19:49 -06:00
return style;
2022-03-17 15:15:39 -05:00
}
catch (error) {
return undefined;
}
}),
)
)
.filter((filename) => !!filename);
2024-01-16 00:28:20 -06:00
const runtime = await flecks.resolver.resolve(join('@flecks/web/runtime'));
2023-11-30 21:41:42 -06:00
const isProduction = 'production' === argv.mode;
2024-01-16 00:28:20 -06:00
const resolvedPaths = (await Promise.all(
paths.map(async (path) => [path, await flecks.resolver.resolve(path)]),
))
.filter(([, resolved]) => resolved)
.map(([path]) => path);
2023-11-30 21:41:42 -06:00
const source = [
'module.exports = (update) => (async () => ({',
" config: window[Symbol.for('@flecks/web.config')],",
' flecks: Object.fromEntries(await Promise.all([',
2024-01-16 00:28:20 -06:00
...resolvedPaths
.map((path) => [
2023-11-30 21:41:42 -06:00
' [',
` '${path}',`,
` import('${path}').then((M) => (update(${paths.length}, '${path}'), M)),`,
2024-01-16 00:28:20 -06:00
' ],',
]).flat(),
2023-11-30 21:41:42 -06:00
' ].map(async ([path, M]) => [path, await M]))),',
" platforms: ['client'],",
'}))();',
'',
];
2024-01-16 00:28:20 -06:00
// HMrequire.
2023-11-30 21:41:42 -06:00
source.push('if (module.hot) {');
2024-01-16 00:28:20 -06:00
resolvedPaths.forEach((path) => {
2023-11-30 21:41:42 -06:00
source.push(` module.hot.accept('${path}', async () => {`);
source.push(` const updatedFleck = require('${path}');`);
source.push(` window.flecks.refresh('${path}', updatedFleck);`);
source.push(` window.flecks.invoke('@flecks/core.hmr', '${path}', updatedFleck);`);
source.push(' });');
});
source.push('}');
source.push('');
// Create runtime.
config.module.rules.push({
test: runtime,
use: [
{
loader: runtime,
options: {
source: source.join('\n'),
},
},
],
});
config.resolve.alias['@flecks/web/runtime$'] = runtime;
2024-01-16 00:28:20 -06:00
// Stubs.
buildFlecks.stubs.forEach((stub) => {
config.module.rules.push(
{
test: stub,
use: 'null-loader',
},
);
});
2024-01-20 02:03:50 -06:00
await buildFlecks.runtimeCompiler('web', config);
2023-11-30 21:41:42 -06:00
// Styles.
config.entry.index.push(...styles);
// Tests.
if (!isProduction) {
2024-01-19 05:17:37 -06:00
const testEntries = (await Promise.all(
2024-01-16 00:28:20 -06:00
Object.entries(buildFlecks.roots)
.map(async ([parent, {request}]) => {
2024-01-19 05:17:37 -06:00
const tests = [];
const resolved = dirname(await resolver.resolve(join(request, 'package.json')));
const rootTests = await glob(join(resolved, 'test', '*.js'));
tests.push(
...rootTests
.map((test) => test.replace(resolved, parent)),
);
2024-01-16 00:28:20 -06:00
const platformTests = await Promise.all(
buildFlecks.platforms.map((platform) => (
2024-01-19 05:17:37 -06:00
glob(join(resolved, 'test', 'platforms', platform, '*.js'))
2024-01-16 00:28:20 -06:00
)),
);
2024-01-19 05:17:37 -06:00
tests.push(
...platformTests
.flat()
.map((test) => test.replace(resolved, parent)),
);
return [parent, tests];
2024-01-16 00:28:20 -06:00
}),
2024-01-19 05:17:37 -06:00
))
.filter(([, tests]) => tests.length > 0);
2024-01-16 00:28:20 -06:00
const tests = await resolver.resolve(
join('@flecks/web', 'server', 'build', 'tests'),
2024-01-13 06:19:49 -06:00
);
2023-11-30 21:41:42 -06:00
const testsSource = (await readFile(tests)).toString();
config.module.rules.push({
test: tests,
use: [
{
loader: runtime,
options: {
source: testsSource.replace(
2024-01-13 06:19:49 -06:00
" await import('@flecks/web/tests');",
testEntries
2024-01-19 05:17:37 -06:00
.map(([root, tests]) => (
2024-01-13 06:19:49 -06:00
[
` describe('${root}', () => {`,
2024-01-19 05:17:37 -06:00
` ${tests.map((test) => `require('${test}');`).join('\n ')}`,
2024-01-13 06:19:49 -06:00
' });',
].join('\n')
)).join('\n\n'),
2022-03-01 10:14:38 -06:00
),
2023-11-30 21:41:42 -06:00
},
},
],
});
}
2022-02-25 04:58:08 -06:00
};