@esmx/rspack
Version:
A high-performance Rspack integration for Esmx microfrontend framework, providing Module Linking and SSR capabilities.
142 lines (141 loc) • 4.2 kB
JavaScript
import upath from "upath";
export const RSPACK_PLUGIN_NAME = "rspack-module-link-plugin";
export class ManifestPlugin {
constructor(opts) {
this.opts = opts;
}
apply(compiler) {
const opts = this.opts;
const { Compilation } = compiler.rspack;
compiler.hooks.thisCompilation.tap(
RSPACK_PLUGIN_NAME,
(compilation) => {
let manifestJson = {
name: opts.name,
exports: {},
scopes: opts.scopes,
files: [],
chunks: {}
};
compilation.hooks.processAssets.tap(
{
name: RSPACK_PLUGIN_NAME,
stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
},
(assets) => {
const stats = compilation.getStats().toJson({
hash: true,
entrypoints: true
});
const exports = getExports(opts, stats);
const resources = Object.keys(assets).map(transFileName).filter((file) => !file.includes("hot-update"));
manifestJson = {
name: opts.name,
exports,
scopes: opts.scopes,
files: resources,
chunks: getChunks(opts, compilation)
};
const { RawSource } = compiler.rspack.sources;
compilation.emitAsset(
"manifest.json",
new RawSource(JSON.stringify(manifestJson, null, 4))
);
}
);
if (opts.injectChunkName) {
compilation.hooks.processAssets.tap(
{
name: RSPACK_PLUGIN_NAME,
stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
},
(assets) => {
const { RawSource } = compiler.rspack.sources;
for (const [key, value] of Object.entries(
manifestJson.chunks
)) {
const asset = assets[value.js];
if (!asset) {
return;
}
const source = new RawSource(
`import.meta.chunkName = import.meta.chunkName ?? ${JSON.stringify(key)};
${asset.source()}`
);
compilation.updateAsset(value.js, source);
}
}
);
}
}
);
}
}
function transFileName(fileName) {
return fileName.replace(/^.\//, "");
}
export function getExports(opts, stats) {
const entrypoints = stats.entrypoints || {};
const exports = {};
for (const [key, value] of Object.entries(entrypoints)) {
const asset = value.assets?.find((item) => {
return item.name.endsWith(opts.ext) && item.name.startsWith(key) && !item.name.includes("hot-update");
});
if (!asset) continue;
if (key in opts.exports) {
exports[key] = {
...opts.exports[key],
file: asset.name
};
}
}
return exports;
}
function getChunks(opts, compilation) {
const stats = compilation.getStats().toJson({
all: false,
chunks: true,
modules: true,
chunkModules: true,
ids: true
});
const buildChunks = {};
if (!stats.chunks) return buildChunks;
for (const chunk of stats.chunks) {
const module = chunk.modules?.sort((a, b) => {
return (a.index ?? -1) - (b?.index ?? -1);
})?.find((module2) => {
return module2.moduleType?.includes("javascript/");
});
if (!module?.nameForCondition) continue;
const js = chunk.files?.find((file) => file.endsWith(opts.ext));
if (!js) continue;
const root = compilation.options.context ?? process.cwd();
const name = generateIdentifier({
root,
name: opts.name,
filePath: module.nameForCondition
});
const css = chunk.files?.filter((file) => file.endsWith(".css")) ?? [];
const resources = chunk.auxiliaryFiles ?? [];
buildChunks[name] = {
name,
js,
css,
resources
};
}
return buildChunks;
}
export function generateIdentifier({
root,
name,
filePath
}) {
const unixFilePath = upath.toUnix(filePath);
if (!root) {
return `${name}@${unixFilePath}`;
}
const file = upath.relative(upath.toUnix(root), unixFilePath);
return `${name}@${file}`;
}