@module-federation/manifest
Version:
Provide manifest/stats for webpack/rspack MF project .
536 lines (524 loc) • 23.6 kB
JavaScript
;
const __rslib_import_meta_url__ = /*#__PURE__*/ (function () {
return typeof document === 'undefined'
? new (require('url'.replace('', '')).URL)('file:' + __filename).href
: (document.currentScript && document.currentScript.src) ||
new URL('main.js', document.baseURI).href;
})();
;
// The require scope
var __webpack_require__ = {};
/************************************************************************/
// webpack/runtime/compat_get_default_export
(() => {
// getDefaultExport function for compatibility with non-ESM modules
__webpack_require__.n = (module) => {
var getter = module && module.__esModule ?
() => (module['default']) :
() => (module);
__webpack_require__.d(getter, { a: getter });
return getter;
};
})();
// webpack/runtime/define_property_getters
(() => {
__webpack_require__.d = (exports, definition) => {
for(var key in definition) {
if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
}
}
};
})();
// webpack/runtime/has_own_property
(() => {
__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
})();
// webpack/runtime/make_namespace_object
(() => {
// define __esModule on exports
__webpack_require__.r = (exports) => {
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};
})();
/************************************************************************/
var __webpack_exports__ = {};
// ESM COMPAT FLAG
__webpack_require__.r(__webpack_exports__);
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
StatsManager: () => (/* binding */ StatsManager)
});
;// CONCATENATED MODULE: external "@module-federation/sdk"
const sdk_namespaceObject = require("@module-federation/sdk");
;// CONCATENATED MODULE: external "./utils.js"
const external_utils_js_namespaceObject = require("./utils.js");
;// CONCATENATED MODULE: external "./logger.js"
const external_logger_js_namespaceObject = require("./logger.js");
var external_logger_js_default = /*#__PURE__*/__webpack_require__.n(external_logger_js_namespaceObject);
;// CONCATENATED MODULE: external "@module-federation/managers"
const managers_namespaceObject = require("@module-federation/managers");
;// CONCATENATED MODULE: external "./constants.js"
const external_constants_js_namespaceObject = require("./constants.js");
;// CONCATENATED MODULE: external "./ModuleHandler.js"
const external_ModuleHandler_js_namespaceObject = require("./ModuleHandler.js");
;// CONCATENATED MODULE: ./src/StatsManager.ts
/* eslint-disable max-lines-per-function */ /* eslint-disable @typescript-eslint/member-ordering */ /* eslint-disable max-depth */
class StatsManager {
getBuildInfo(context, target) {
const rootPath = context || process.cwd();
const pkg = this._pkgJsonManager.readPKGJson(rootPath);
const statsBuildInfo = {
buildVersion: managers_namespaceObject.utils.getBuildVersion(rootPath),
buildName: managers_namespaceObject.utils.getBuildName() || pkg['name']
};
if (this._sharedManager.enableTreeShaking) {
statsBuildInfo.target = target ? Array.isArray(target) ? target : [
target
] : [];
statsBuildInfo.plugins = this._options.treeShakingSharedPlugins || [];
statsBuildInfo.excludePlugins = this._options.treeShakingSharedExcludePlugins || [];
}
return statsBuildInfo;
}
get fileName() {
return (0,sdk_namespaceObject.getManifestFileName)(this._options.manifest).statsFileName;
}
setMetaDataPublicPath(metaData, compiler) {
if (this._options.getPublicPath) {
if ('publicPath' in metaData) {
// @ts-ignore
delete metaData.publicPath;
}
metaData.getPublicPath = this._options.getPublicPath;
} else {
metaData.publicPath = this.getPublicPath(compiler);
}
return metaData;
}
_getMetaData(compiler, compilation, extraOptions) {
var _this__options_library, _this__options;
const { context } = compiler.options;
const { _options: { name } } = this;
const buildInfo = this.getBuildInfo(context, compilation.options.target || '');
const type = this._pkgJsonManager.getExposeGarfishModuleType(context || process.cwd());
const getRemoteEntryName = ()=>{
if (!this._containerManager.enable) {
return '';
}
(0,external_utils_js_namespaceObject.assert)(name, 'name is required');
const remoteEntryPoint = compilation.entrypoints.get(name);
(0,external_utils_js_namespaceObject.assert)(remoteEntryPoint, 'Can not get remoteEntry entryPoint!');
const remoteEntryNameChunk = compilation.namedChunks.get(name);
(0,external_utils_js_namespaceObject.assert)(remoteEntryNameChunk, 'Can not get remoteEntry chunk!');
const files = Array.from(remoteEntryNameChunk.files).filter((f)=>!f.includes(external_constants_js_namespaceObject.HOT_UPDATE_SUFFIX) && !f.endsWith('.css'));
(0,external_utils_js_namespaceObject.assert)(files.length > 0, 'no files found for remoteEntry chunk');
(0,external_utils_js_namespaceObject.assert)(files.length === 1, `remoteEntry chunk should not have multiple files!, current files: ${files.join(',')}`);
const remoteEntryName = files[0];
return remoteEntryName;
};
const globalName = this._containerManager.globalEntryName;
(0,external_utils_js_namespaceObject.assert)(globalName, 'Can not get library.name, please ensure you have set library.name and the type is "string" !');
(0,external_utils_js_namespaceObject.assert)(this._pluginVersion, 'Can not get pluginVersion, please ensure you have set pluginVersion !');
const metaData = {
name: name,
type,
buildInfo,
remoteEntry: {
name: getRemoteEntryName(),
path: '',
// same as the types supported by runtime, currently only global/var/script is supported
type: ((_this__options = this._options) === null || _this__options === void 0 ? void 0 : (_this__options_library = _this__options.library) === null || _this__options_library === void 0 ? void 0 : _this__options_library.type) || 'global'
},
types: (0,external_utils_js_namespaceObject.getTypesMetaInfo)(this._options, compiler.context),
globalName: globalName,
pluginVersion: this._pluginVersion
};
return this.setMetaDataPublicPath(metaData, compiler);
}
_getFilteredModules(stats) {
const filteredModules = stats.modules.filter((module)=>{
if (!module || !module.name) {
return false;
}
const array = [
module.name.includes('container entry'),
module.name.includes('remote '),
module.name.includes('shared module '),
module.name.includes('provide module ')
];
return array.some((item)=>item);
});
return filteredModules;
}
_getModuleAssets(compilation, entryPointNames) {
const { chunks } = compilation;
const { exposeFileNameImportMap } = this._containerManager;
const assets = {};
chunks.forEach((chunk)=>{
if (typeof chunk.name !== 'string') return;
// Support split chunks caused by splitChunks.maxSize:
// A chunk named "__federation_expose_Foo" may be split into
// "__federation_expose_Foo-<hash>" chunks, so we match both exact
// and prefix+dash patterns.
const matchedKey = exposeFileNameImportMap[chunk.name] !== undefined ? chunk.name : Object.keys(exposeFileNameImportMap).find((key)=>chunk.name.startsWith(key + '-'));
if (!matchedKey) return;
// TODO: support multiple import
const exposeKey = exposeFileNameImportMap[matchedKey][0];
const assetKey = (0,external_utils_js_namespaceObject.getFileNameWithOutExt)(exposeKey);
const chunkAssets = (0,external_utils_js_namespaceObject.getAssetsByChunk)(chunk, entryPointNames);
if (!assets[assetKey]) {
assets[assetKey] = chunkAssets;
} else {
// Merge split chunk assets, deduplicating with Set
assets[assetKey] = {
js: {
sync: [
...new Set([
...assets[assetKey].js.sync,
...chunkAssets.js.sync
])
],
async: [
...new Set([
...assets[assetKey].js.async,
...chunkAssets.js.async
])
]
},
css: {
sync: [
...new Set([
...assets[assetKey].css.sync,
...chunkAssets.css.sync
])
],
async: [
...new Set([
...assets[assetKey].css.async,
...chunkAssets.css.async
])
]
}
};
}
});
return assets;
}
_getProvideSharedAssets(compilation, stats, entryPointNames) {
const sharedModules = stats.modules.filter((module)=>{
if (!module || !module.name) {
return false;
}
const array = [
module.name.includes('consume shared module ')
];
return array.some((item)=>item);
});
const manifestOverrideChunkIDMap = {};
const effectiveSharedModules = (0,external_utils_js_namespaceObject.getSharedModules)(stats, sharedModules);
effectiveSharedModules.forEach((item)=>{
const [sharedModuleName, sharedModule] = item;
if (!manifestOverrideChunkIDMap[sharedModuleName]) {
manifestOverrideChunkIDMap[sharedModuleName] = {
async: new Set(),
sync: new Set()
};
}
sharedModule.chunks.forEach((chunkID)=>{
const chunk = (0,external_utils_js_namespaceObject.findChunk)(chunkID, compilation.chunks);
manifestOverrideChunkIDMap[sharedModuleName].sync.add(chunkID);
if (!chunk) {
return;
}
[
...chunk.groupsIterable
].forEach((group)=>{
if (group.name && !entryPointNames.includes(group.name)) {
manifestOverrideChunkIDMap[sharedModuleName].sync.add(group.id);
}
});
});
});
const assets = {
js: {
async: [],
sync: []
},
css: {
async: [],
sync: []
}
};
Object.keys(manifestOverrideChunkIDMap).forEach((override)=>{
const asyncAssets = (0,external_utils_js_namespaceObject.getAssetsByChunkIDs)(compilation, {
[override]: manifestOverrideChunkIDMap[override].async
});
const syncAssets = (0,external_utils_js_namespaceObject.getAssetsByChunkIDs)(compilation, {
[override]: manifestOverrideChunkIDMap[override].sync
});
assets[override] = {
js: {
async: asyncAssets[override].js,
sync: syncAssets[override].js
},
css: {
async: asyncAssets[override].css,
sync: syncAssets[override].css
}
};
});
return assets;
}
async _generateStats(compiler, compilation, extraOptions) {
try {
const { name, manifest: manifestOptions = {}, exposes = {} } = this._options;
const metaData = this._getMetaData(compiler, compilation, extraOptions);
const stats = {
id: name,
name: name,
metaData,
shared: [],
remotes: [],
exposes: []
};
if (typeof manifestOptions === 'object' && manifestOptions.disableAssetsAnalyze) {
const remotes = this._remoteManager.statsRemoteWithEmptyUsedIn;
stats.remotes = remotes;
stats.exposes = Object.keys(exposes).map((exposeKey)=>{
return (0,external_ModuleHandler_js_namespaceObject.getExposeItem)({
exposeKey,
name: name,
file: {
import: exposes[exposeKey].import
}
});
});
stats.shared = Object.entries(this._sharedManager.normalizedOptions).reduce((sum, cur)=>{
const [pkgName, normalizedShareOptions] = cur;
sum.push((0,external_ModuleHandler_js_namespaceObject.getShareItem)({
pkgName,
normalizedShareOptions,
pkgVersion: normalizedShareOptions.version || managers_namespaceObject.UNKNOWN_MODULE_NAME,
hostName: name
}));
return sum;
}, []);
return stats;
}
const liveStats = compilation.getStats();
const statsOptions = {
all: false,
modules: true,
builtAt: true,
hash: true,
ids: true,
version: true,
entrypoints: true,
assets: false,
chunks: false,
reasons: true
};
if (this._bundler === 'webpack') {
statsOptions['cached'] = true;
}
statsOptions['cachedModules'] = true;
const webpackStats = liveStats.toJson(statsOptions);
const filteredModules = this._getFilteredModules(webpackStats);
const moduleHandler = new external_ModuleHandler_js_namespaceObject.ModuleHandler(this._options, filteredModules, {
bundler: this._bundler
});
const { remotes, exposesMap, sharedMap } = moduleHandler.collect();
const entryPointNames = [
...compilation.entrypoints.values()
].map((e)=>e.name).filter((v)=>!!v);
await Promise.all([
new Promise((resolve)=>{
const sharedAssets = this._getProvideSharedAssets(compilation, webpackStats, entryPointNames);
Object.keys(sharedMap).forEach((sharedKey)=>{
const assets = sharedAssets[sharedKey];
if (assets) {
sharedMap[sharedKey].assets = assets;
}
});
resolve();
}),
new Promise((resolve)=>{
const moduleAssets = this._getModuleAssets(compilation, entryPointNames);
Object.keys(exposesMap).forEach((exposeKey)=>{
const assets = moduleAssets[exposeKey];
if (assets) {
exposesMap[exposeKey].assets = assets;
}
exposesMap[exposeKey].requires = Array.from(new Set(exposesMap[exposeKey].requires));
});
resolve();
})
]);
await Promise.all([
new Promise((resolve)=>{
const remoteMemo = new Set();
stats.remotes = remotes.map((remote)=>{
remoteMemo.add(remote.federationContainerName);
return {
...remote,
usedIn: Array.from(remote.usedIn.values())
};
});
const statsRemoteWithEmptyUsedIn = this._remoteManager.statsRemoteWithEmptyUsedIn;
statsRemoteWithEmptyUsedIn.forEach((remoteInfo)=>{
if (!remoteMemo.has(remoteInfo.federationContainerName)) {
stats.remotes.push(remoteInfo);
}
});
resolve();
}),
new Promise((resolve)=>{
stats.shared = Object.values(sharedMap).map((shared)=>({
...shared,
usedIn: Array.from(shared.usedIn)
}));
resolve();
})
]);
await new Promise((resolve)=>{
const sharedAssets = stats.shared.reduce((sum, shared)=>{
const { js, css } = shared.assets;
[
...js.sync,
...js.async,
...css.async,
css.sync
].forEach((asset)=>{
sum.add(asset);
});
return sum;
}, new Set());
const { fileExposeKeyMap } = this._containerManager;
stats.exposes = [];
Object.entries(fileExposeKeyMap).forEach(([exposeFileWithoutExt, exposeKeySet])=>{
const expose = exposesMap[exposeFileWithoutExt] || {
assets: {
js: {
sync: [],
async: []
},
css: {
sync: [],
async: []
}
}
};
exposeKeySet.forEach((exposeKey)=>{
const { js, css } = expose.assets;
const exposeModuleName = (0,external_ModuleHandler_js_namespaceObject.getExposeName)(exposeKey);
stats.exposes.push({
...expose,
path: exposeKey,
id: (0,sdk_namespaceObject.composeKeyWithSeparator)(this._options.name, exposeModuleName),
name: exposeModuleName,
assets: {
js: {
sync: js.sync.filter((asset)=>!sharedAssets.has(asset)),
async: js.async.filter((asset)=>!sharedAssets.has(asset))
},
css: {
sync: css.sync.filter((asset)=>!sharedAssets.has(asset)),
async: css.async.filter((asset)=>!sharedAssets.has(asset))
}
}
});
});
});
Object.values(exposesMap).map((expose)=>{
const { js, css } = expose.assets;
return {
...expose,
assets: {
js: {
sync: js.sync.filter((asset)=>!sharedAssets.has(asset)),
async: js.async.filter((asset)=>!sharedAssets.has(asset))
},
css: {
sync: css.sync.filter((asset)=>!sharedAssets.has(asset)),
async: css.async.filter((asset)=>!sharedAssets.has(asset))
}
}
};
});
resolve();
});
return stats;
} catch (err) {
throw err;
}
}
getPublicPath(compiler) {
if (this._publicPath) {
return this._publicPath;
}
const { output: { publicPath: originalPublicPath } } = compiler.options;
let publicPath = originalPublicPath;
this._publicPath = publicPath;
return publicPath;
}
init(options, { pluginVersion, bundler }) {
this._options = options;
this._pluginVersion = pluginVersion;
this._bundler = bundler;
this._containerManager = new managers_namespaceObject.ContainerManager();
this._containerManager.init(options);
this._remoteManager = new managers_namespaceObject.RemoteManager();
this._remoteManager.init(options);
this._sharedManager = new managers_namespaceObject.SharedManager();
this._sharedManager.init(options);
}
updateStats(stats, compiler) {
const { metaData } = stats;
if (!metaData.types) {
metaData.types = (0,external_utils_js_namespaceObject.getTypesMetaInfo)(this._options, compiler.context);
}
if (!metaData.pluginVersion) {
metaData.pluginVersion = this._pluginVersion;
}
this.setMetaDataPublicPath(metaData, compiler);
return stats;
}
async generateStats(compiler, compilation) {
try {
const stats = await this._generateStats(compiler, compilation);
return stats;
} catch (err) {
throw err;
}
}
validate(compiler) {
const { output: { publicPath } } = compiler.options;
if (typeof publicPath !== 'string') {
external_logger_js_default().warn(`Manifest will not generate, because publicPath can only be string, but got '${publicPath}'`);
return false;
} else if (publicPath === 'auto') {
external_logger_js_default().warn(`Manifest will use absolute path resolution via its host at runtime, reason: publicPath='${publicPath}'`);
return true;
}
return true;
}
constructor(){
this._options = {};
this._bundler = 'webpack';
this._containerManager = new managers_namespaceObject.ContainerManager();
this._remoteManager = new managers_namespaceObject.RemoteManager();
this._sharedManager = new managers_namespaceObject.SharedManager();
this._pkgJsonManager = new managers_namespaceObject.PKGJsonManager();
}
}
exports.StatsManager = __webpack_exports__.StatsManager;
for(var __webpack_i__ in __webpack_exports__) {
if(["StatsManager"].indexOf(__webpack_i__) === -1) {
exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
}
}
Object.defineProperty(exports, '__esModule', { value: true });