@esmx/rspack
Version:
A high-performance Rspack integration for Esmx microfrontend framework, providing Module Linking and SSR capabilities.
161 lines (160 loc) • 6.37 kB
JavaScript
import {
rspack
} from "@rspack/core";
import NodePolyfillPlugin from "node-polyfill-webpack-plugin";
import {
RSPACK_LOADER,
createRspackApp
} from "../rspack/index.mjs";
import { getTargetSetting } from "./target-setting.mjs";
export async function createRspackHtmlApp(esmx, options) {
options = {
...options,
css: options?.css ? options.css : esmx.isProd ? "css" : "js"
};
return createRspackApp(esmx, {
...options,
chain(context) {
const { chain, buildTarget, esmx: esmx2 } = context;
chain.stats("errors-warnings");
chain.devtool(false);
chain.cache(false);
configureAssetRules(chain, esmx2);
chain.module.rule("json").test(/\.json$/i).type("json");
configureWorkerRule(chain, esmx2, options);
configureTypeScriptRule(chain, buildTarget, options);
configureOptimization(chain, options);
chain.plugin("node-polyfill").use(NodePolyfillPlugin);
configureDefinePlugin(chain, buildTarget, options);
chain.resolve.extensions.clear().add("...").add(".ts");
configureCssRules(chain, esmx2, options);
options?.chain?.(context);
}
});
}
function configureAssetRules(chain, esmx) {
chain.module.rule("images").test(
/\.(png|jpg|jpeg|gif|svg|bmp|webp|ico|apng|avif|tif|tiff|jfif|pjpeg|pjp|cur)$/i
).type("asset/resource").set("generator", {
filename: filename(esmx, "images")
});
chain.module.rule("media").test(/\.(mp4|webm|ogg|mov)$/i).type("asset/resource").set("generator", {
filename: filename(esmx, "media")
});
chain.module.rule("audio").test(/\.(mp3|wav|flac|aac|m4a|opus)$/i).type("asset/resource").set("generator", {
filename: filename(esmx, "audio")
});
chain.module.rule("fonts").test(/\.(woff|woff2|eot|ttf|otf|ttc)(\?.*)?$/i).type("asset/resource").set("generator", {
filename: filename(esmx, "fonts")
});
}
function configureWorkerRule(chain, esmx, options) {
chain.module.rule("worker").test(/\.worker\.(c|m)?(t|j)s$/i).use("worker-loader").loader(
options.loaders?.workerRspackLoader ?? RSPACK_LOADER.workerRspackLoader
).options({
esModule: false,
filename: `${esmx.name}/workers/[name].[contenthash]${esmx.isProd ? ".final" : ""}.js`
});
}
function configureTypeScriptRule(chain, buildTarget, options) {
const targets = getTargetSetting(options?.target, buildTarget);
chain.module.rule("typescript").test(/\.(ts|mts)$/i).use("swc-loader").loader(
options.loaders?.builtinSwcLoader ?? RSPACK_LOADER.builtinSwcLoader
).options({
env: {
targets,
...options?.swcLoader?.env
},
jsc: {
parser: {
syntax: "typescript",
...options?.swcLoader?.jsc?.parser
},
...options?.swcLoader?.jsc
},
...options?.swcLoader
}).end().type("javascript/auto");
}
function configureOptimization(chain, options) {
chain.optimization.minimizer("swc-js-minimizer").use(rspack.SwcJsMinimizerRspackPlugin, [
{
minimizerOptions: {
format: {
comments: false
}
}
}
]);
chain.optimization.minimizer("lightningcss-minimizer").use(rspack.LightningCssMinimizerRspackPlugin, [
{
minimizerOptions: {
targets: getTargetSetting(options?.target, "client"),
errorRecovery: false
}
}
]);
}
function configureDefinePlugin(chain, buildTarget, options) {
if (options.definePlugin) {
const defineOptions = {};
Object.entries(options.definePlugin).forEach(([name, value]) => {
const targetValue = typeof value === "string" ? value : value[buildTarget];
if (typeof targetValue === "string" && name !== targetValue) {
defineOptions[name] = targetValue;
}
});
if (Object.keys(defineOptions).length) {
chain.plugin("define").use(rspack.DefinePlugin, [defineOptions]);
}
}
}
function configureCssRules(chain, esmx, options) {
if (options.css === false) {
return;
}
if (options.css === "js") {
configureCssInJS(chain, esmx, options);
return;
}
configureCssExtract(chain, options);
}
function configureCssInJS(chain, esmx, options) {
chain.module.rule("css").test(/\.css$/).use("style-loader").loader(options.loaders?.styleLoader ?? RSPACK_LOADER.styleLoader).options(options.styleLoader ?? {}).end().use("css-loader").loader(options.loaders?.cssLoader ?? RSPACK_LOADER.cssLoader).options(options.cssLoader ?? {}).end().use("lightning-css-loader").loader(
options.loaders?.lightningcssLoader ?? RSPACK_LOADER.lightningcssLoader
).options({
targets: getTargetSetting(options?.target, "client"),
minify: esmx.isProd
}).end().type("javascript/auto");
const lessRule = chain.module.rule("less").test(/\.less$/).use("style-loader").loader(options.loaders?.styleLoader ?? RSPACK_LOADER.styleLoader).options(options.styleLoader ?? {}).end().use("css-loader").loader(options.loaders?.cssLoader ?? RSPACK_LOADER.cssLoader).options(options.cssLoader ?? {}).end().use("lightning-css-loader").loader(
options.loaders?.lightningcssLoader ?? RSPACK_LOADER.lightningcssLoader
).options({
targets: getTargetSetting(options?.target, "client"),
minify: esmx.isProd
}).end().use("less-loader").loader(options.loaders?.lessLoader ?? RSPACK_LOADER.lessLoader).options(options.lessLoader ?? {}).end();
if (options.styleResourcesLoader) {
lessRule.use("style-resources-loader").loader(
options.loaders?.styleResourcesLoader ?? RSPACK_LOADER.styleResourcesLoader
).options(options.styleResourcesLoader);
}
lessRule.type("javascript/auto");
}
function configureCssExtract(chain, options) {
chain.set("experiments", {
...chain.get("experiments") ?? {},
css: true
});
const experiments = chain.get("experiments");
if (!experiments || !experiments.css) {
return;
}
const lessRule = chain.module.rule("less").test(/\.less$/).use("less-loader").loader(options.loaders?.lessLoader ?? RSPACK_LOADER.lessLoader).options(options.lessLoader ?? {}).end();
if (options.styleResourcesLoader) {
lessRule.use("style-resources-loader").loader(
options.loaders?.styleResourcesLoader ?? RSPACK_LOADER.styleResourcesLoader
).options(options.styleResourcesLoader);
}
lessRule.type("css");
}
function filename(esmx, name, ext = "[ext]") {
return esmx.isProd ? `${name}/[name].[contenthash:8].final${ext}` : `${name}/[path][name]${ext}`;
}