@esmx/rspack
Version:
A high-performance Rspack integration for Esmx microfrontend framework, providing module federation and SSR capabilities.
170 lines (166 loc) • 5.61 kB
text/typescript
import type { Esmx } from '@esmx/core';
import { moduleLinkPlugin } from '@esmx/rspack-module-link-plugin';
import {
type ExternalItem,
type Plugin,
type Plugins,
type RspackOptions,
rspack
} from '@rspack/core';
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';
/**
* Base configuration for building Client, Server, and Node targets
*/
export function createRspackConfig(
esmx: Esmx,
buildTarget: BuildTarget,
options: RspackAppOptions
): RspackOptions {
const isHot = buildTarget === 'client' && !esmx.isProd;
return {
/**
* Project root directory, cannot be modified
*/
context: esmx.root,
output: {
clean: esmx.isProd,
filename:
buildTarget !== 'node' && esmx.isProd
? 'exports/[name].[contenthash:8].final.mjs'
: 'exports/[name].mjs',
cssFilename: esmx.isProd
? 'exports/[name].[contenthash:8].final.css'
: 'exports/[name].css',
chunkFilename: esmx.isProd
? 'chunks/[name].[contenthash:8].final.mjs'
: 'chunks/[name].mjs',
cssChunkFilename: esmx.isProd
? 'chunks/[name].[contenthash:8].final.css'
: 'chunks/[name].css',
publicPath:
buildTarget === 'client'
? '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`,
path: ((): string => {
switch (buildTarget) {
case 'client':
return esmx.resolvePath('dist/client');
case 'server':
return esmx.resolvePath('dist/server');
case 'node':
return esmx.resolvePath('dist/node');
}
})()
},
plugins: ((): Plugins => {
return [
new rspack.ProgressPlugin({
prefix: buildTarget
}),
createModuleLinkPlugin(esmx, buildTarget),
isHot ? new rspack.HotModuleReplacementPlugin() : false
];
})(),
module: {
parser: {
javascript: {
// DEV hot update fix
dynamicImportMode: esmx.isProd ? 'lazy' : 'eager',
url: buildTarget === 'client' ? true : 'relative'
}
},
generator: {
asset: {
emit: buildTarget === 'client'
},
'asset/resource': {
emit: buildTarget === 'client'
}
},
rules: []
},
resolve: {
alias: {
[esmx.name]: esmx.root
}
},
optimization: {
minimize: options.minimize ?? esmx.isProd,
emitOnErrors: true,
// DEV hot update fix
splitChunks: esmx.isProd ? undefined : false,
// DEV hot update fix
runtimeChunk: esmx.isProd ? undefined : false
},
externalsPresets: {
web: buildTarget === 'client',
node: buildTarget === 'server' || buildTarget === 'node'
},
externalsType: 'module-import',
externals: ((): ExternalItem[] => {
if (buildTarget === 'node') {
return [
// @ts-ignore
nodeExternals({
// @ts-ignore
importType: 'module-import'
})
];
}
return [];
})(),
target: buildTarget === 'client' ? 'web' : 'node24',
mode: esmx.isProd ? 'production' : 'development',
cache: !esmx.isProd
};
}
function createModuleLinkPlugin(esmx: Esmx, buildTarget: BuildTarget): Plugin {
if (buildTarget === 'node') {
return moduleLinkPlugin({
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 (buildTarget === 'client' && !esmx.isProd) {
preEntries.push(
`${import.meta.resolve('webpack-hot-middleware/client.js')}?path=/${esmx.name}/hot-middleware`
);
}
return moduleLinkPlugin({
name: esmx.name,
injectChunkName: buildTarget === 'server',
imports: esmx.moduleConfig.imports,
deps: Object.keys(esmx.moduleConfig.links),
exports,
preEntries
});
}