@esmx/rspack
Version:
A high-performance Rspack integration for Esmx microfrontend framework, providing module federation and SSR capabilities.
194 lines (169 loc) • 5.67 kB
text/typescript
import type { Esmx } from '@esmx/core';
import { moduleLinkPlugin } from '@esmx/rspack-module-link-plugin';
import { rspack } from '@rspack/core';
import type { RspackOptions } from '@rspack/core';
import RspackChain from 'rspack-chain';
import nodeExternals from 'webpack-node-externals';
import type { RspackAppOptions } from './app';
import type { BuildTarget } from './build-target';
import { HMR_DIR, HMR_JSONP } from './hmr-config';
export function createChainConfig(
esmx: Esmx,
buildTarget: BuildTarget,
options: RspackAppOptions
): RspackChain {
const isHot = buildTarget === 'client' && !esmx.isProd;
const isClient = buildTarget === 'client';
const isServer = buildTarget === 'server';
const isNode = buildTarget === 'node';
const config = new RspackChain();
config.context(esmx.root);
config.mode(esmx.isProd ? 'production' : 'development');
config.target(isClient ? 'web' : 'node24');
config.cache(!esmx.isProd);
config.output
.clean(esmx.isProd)
.filename(
!isNode && esmx.isProd
? 'exports/[name].[contenthash:8].final.mjs'
: 'exports/[name].mjs'
)
.chunkFilename(
esmx.isProd
? 'chunks/[name].[contenthash:8].final.mjs'
: 'chunks/[name].mjs'
)
.publicPath(
isClient ? 'auto' : `${esmx.basePathPlaceholder}${esmx.basePath}`
)
.uniqueName(esmx.varName)
.hotUpdateGlobal(HMR_JSONP)
.chunkLoadingGlobal(`${HMR_JSONP}_chunk`)
.hotUpdateChunkFilename(`${HMR_DIR}/[id].[fullhash].hot-update.mjs`)
.hotUpdateMainFilename(
`${HMR_DIR}/[runtime].[fullhash].hot-update.json`
);
config.output.set(
'cssFilename',
esmx.isProd
? 'exports/[name].[contenthash:8].final.css'
: 'exports/[name].css'
);
config.output.set(
'cssChunkFilename',
esmx.isProd
? 'chunks/[name].[contenthash:8].final.css'
: 'chunks/[name].css'
);
const outputPath = (() => {
switch (buildTarget) {
case 'client':
return esmx.resolvePath('dist/client');
case 'server':
return esmx.resolvePath('dist/server');
case 'node':
return esmx.resolvePath('dist/node');
}
})();
config.output.path(outputPath);
config.plugin('progress').use(rspack.ProgressPlugin, [
{
prefix: buildTarget
}
]);
config
.plugin('module-link')
.use(moduleLinkPlugin, [createModuleLinkConfig(esmx, buildTarget)]);
if (isHot) {
config.plugin('hmr').use(rspack.HotModuleReplacementPlugin);
}
config.module.parser.set('javascript', {
dynamicImportMode: 'lazy',
url: isClient ? true : 'relative'
});
config.module.generator.set('asset', {
emit: isClient
});
config.module.generator.set('asset/resource', {
emit: isClient
});
config.resolve.alias.set(esmx.name, esmx.root);
config.optimization
.minimize(options.minimize ?? esmx.isProd)
.emitOnErrors(true);
config.externalsPresets({
web: isClient,
node: isServer || isNode
});
config.externalsType('module-import');
if (isNode) {
config.externals([
// @ts-ignore
nodeExternals({
// @ts-ignore
importType: 'module-import'
})
]);
}
// Temporary fix for development environment
// Related issue: https://github.com/esmnext/esmx/issues/109
// TODO: Remove when Rspack officially supports these features
if (!esmx.isProd) {
config.optimization.splitChunks(false).runtimeChunk(false);
config.module.parser.set('javascript', {
...config.module.parser.get('javascript'),
dynamicImportMode: 'eager'
});
}
return config;
}
function createModuleLinkConfig(esmx: Esmx, buildTarget: BuildTarget) {
const isClient = buildTarget === 'client';
const isServer = buildTarget === 'server';
const isNode = buildTarget === 'node';
if (isNode) {
return {
name: esmx.name,
exports: {
'src/entry.node': {
rewrite: false,
file: './src/entry.node'
}
}
};
}
const exports: Record<string, { rewrite: boolean; file: string }> = {};
for (const [name, item] of Object.entries(esmx.moduleConfig.exports)) {
if (item.inputTarget[buildTarget]) {
exports[name] = {
rewrite: item.rewrite,
file: item.inputTarget[buildTarget]
};
}
}
const preEntries: string[] = [];
if (isClient && !esmx.isProd) {
preEntries.push(
`${import.meta.resolve('webpack-hot-middleware/client.js')}?path=/${esmx.name}/hot-middleware`
);
}
return {
name: esmx.name,
injectChunkName: isServer,
imports: esmx.moduleConfig.imports,
deps: Object.keys(esmx.moduleConfig.links),
exports,
preEntries
};
}
export function createRspackConfig(
esmx: Esmx,
buildTarget: BuildTarget,
options: RspackAppOptions
): RspackOptions {
const chain = createChainConfig(esmx, buildTarget, options);
options.chain?.({ esmx, options, buildTarget, chain });
const config = chain.toConfig();
options.config?.({ esmx, options, buildTarget, config });
return config;
}