diff --git a/crates/rspack_plugin_mf/src/manifest/mod.rs b/crates/rspack_plugin_mf/src/manifest/mod.rs index 17cb95e13ecd..2f8249443974 100644 --- a/crates/rspack_plugin_mf/src/manifest/mod.rs +++ b/crates/rspack_plugin_mf/src/manifest/mod.rs @@ -84,6 +84,31 @@ fn get_remote_entry_name(compilation: &Compilation, container_name: &str) -> Opt } None } +fn derive_module_name(alias: &str, internal_request: Option<&str>) -> String { + if let Some(req) = internal_request { + let trimmed = req.trim_start_matches("./"); + if trimmed.is_empty() { + req.to_string() + } else { + trimmed.to_string() + } + } else { + let seg = alias.rsplit('/').next().unwrap_or(""); + if seg.is_empty() || seg == alias { + ".".to_string() + } else { + seg.to_string() + } + } +} +fn normalize_used_in_entry(name: &str) -> String { + let base = name.split(" + ").next().unwrap_or(name); + std::path::Path::new(base) + .file_name() + .and_then(|f| f.to_str()) + .unwrap_or(base) + .to_string() +} #[plugin_hook(CompilationProcessAssets for ModuleFederationManifestPlugin)] async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { // Prepare entrypoint names @@ -141,7 +166,7 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { }, r#type: None, }; - let (exposes, shared, remote_list) = if self.options.disable_assets_analyze { + let (exposes, shared, mut remote_list) = if self.options.disable_assets_analyze { let exposes = self .options .exposes @@ -181,11 +206,12 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { } else { target.name.clone() }; + let module_name = derive_module_name(alias, None); StatsRemote { alias: alias.clone(), consumingFederationContainerName: container_name.clone(), federationContainerName: remote_container_name.clone(), - moduleName: remote_container_name, + moduleName: module_name, entry: target.entry.clone(), usedIn: vec!["UNKNOWN".to_string()], } @@ -464,14 +490,7 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { continue; }; let alias = remote_module.remote_key.clone(); - let module_name = { - let trimmed = remote_module.internal_request.trim_start_matches("./"); - if trimmed.is_empty() { - remote_module.internal_request.clone() - } else { - trimmed.to_string() - } - }; + let module_name = derive_module_name(&alias, Some(remote_module.internal_request.as_str())); let (entry, federation_container_name) = if let Some(target) = provided_remote_alias_map.get(&alias) { let remote_container_name = if target.name.is_empty() { @@ -487,7 +506,10 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { (None, alias.clone()) }; let used_in = - collect_usage_files_for_module(compilation, &module_graph, &module_id, &entry_point_names); + collect_usage_files_for_module(compilation, &module_graph, &module_id, &entry_point_names) + .into_iter() + .map(|s| normalize_used_in_entry(&s)) + .collect(); remote_list.push(StatsRemote { alias: alias.clone(), consumingFederationContainerName: container_name.clone(), @@ -502,6 +524,11 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { let shared = shared_map .into_values() .map(|mut v| { + v.usedIn = v + .usedIn + .into_iter() + .map(|s| normalize_used_in_entry(&s)) + .collect(); v.usedIn.sort(); v.usedIn.dedup(); v @@ -509,6 +536,24 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { .collect::>(); (exposes, shared, remote_list) }; + for (alias, target) in self.options.remote_alias_map.iter() { + let exists = remote_list.iter().any(|r| r.alias == *alias); + if !exists { + let remote_container_name = if target.name.is_empty() { + alias.clone() + } else { + target.name.clone() + }; + remote_list.push(StatsRemote { + alias: alias.clone(), + consumingFederationContainerName: container_name.clone(), + federationContainerName: remote_container_name.clone(), + moduleName: "UNKNOWN".to_string(), + entry: target.entry.clone().filter(|e| !e.is_empty()), + usedIn: vec!["UNKNOWN".to_string()], + }); + } + } let stats_root = StatsRoot { id: container_name.clone(), name: container_name.clone(), @@ -527,7 +572,7 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { ), ); // Build manifest from stats - let manifest = ManifestRoot { + let mut manifest = ManifestRoot { id: stats_root.id.clone(), name: stats_root.name.clone(), metaData: stats_root.metaData.clone(), @@ -562,6 +607,24 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { }) .collect(), }; + // Supplement manifest.remotes with registered options if missing + for (alias, target) in self.options.remote_alias_map.iter() { + let exists = manifest.remotes.iter().any(|r| r.alias == *alias); + if !exists { + let remote_container_name = if target.name.is_empty() { + alias.clone() + } else { + target.name.clone() + }; + let module_name = derive_module_name(alias, None); + manifest.remotes.push(ManifestRemote { + federationContainerName: remote_container_name.clone(), + moduleName: module_name, + alias: alias.clone(), + entry: target.entry.clone().filter(|e| !e.is_empty()), + }); + } + } let manifest_json: String = serde_json::to_string_pretty(&manifest).expect("serialize manifest"); compilation.emit_asset( self.options.manifest_file_name.clone(), diff --git a/tests/rspack-test/configCases/container-1-5/manifest/index.js b/tests/rspack-test/configCases/container-1-5/manifest/index.js index 8dd0de36735d..4d72691315e6 100644 --- a/tests/rspack-test/configCases/container-1-5/manifest/index.js +++ b/tests/rspack-test/configCases/container-1-5/manifest/index.js @@ -77,11 +77,41 @@ it("should record remote usage", () => { consumingFederationContainerName: "container", federationContainerName: "remote", moduleName: ".", - usedIn: expect.arrayContaining([ + usedIn: [ "module.js" - ]), + ], entry: 'http://localhost:8000/remoteEntry.js' - }) + }), + expect.objectContaining({ + alias: "@remote/alias", + consumingFederationContainerName: "container", + federationContainerName: "remote", + moduleName: "List", + usedIn: [ + "module.js" + ], + entry: 'http://localhost:8000/remoteEntry.js' + }), + expect.objectContaining({ + alias: "@dynamic-remote/alias", + consumingFederationContainerName: "container", + federationContainerName: "dynamic_remote", + moduleName: "UNKNOWN", + usedIn: [ + "UNKNOWN" + ], + entry: 'http://localhost:8001/remoteEntry.js' + }), + expect.objectContaining({ + alias: "@scope-scope/ui", + consumingFederationContainerName: "container", + federationContainerName: "ui", + moduleName: "Button", + usedIn: [ + "module.js" + ], + entry: 'http://localhost:8002/remoteEntry.js' + }), ]) ); }); @@ -92,7 +122,26 @@ it("should persist remote metadata in manifest", () => { expect.objectContaining({ alias: "@remote/alias", federationContainerName: "remote", - moduleName: "." + moduleName: ".", + entry: "http://localhost:8000/remoteEntry.js" + }), + expect.objectContaining({ + alias: "@remote/alias", + federationContainerName: "remote", + moduleName: "List", + entry: "http://localhost:8000/remoteEntry.js" + }), + expect.objectContaining({ + alias: "@dynamic-remote/alias", + federationContainerName: "dynamic_remote", + moduleName: "UNKNOWN", + entry: "http://localhost:8001/remoteEntry.js" + }), + expect.objectContaining({ + alias: "@scope-scope/ui", + federationContainerName: "ui", + moduleName: "Button", + entry: "http://localhost:8002/remoteEntry.js" }) ]) ); diff --git a/tests/rspack-test/configCases/container-1-5/manifest/module.js b/tests/rspack-test/configCases/container-1-5/manifest/module.js index 40112961dd5c..8f0f4b48f2a1 100644 --- a/tests/rspack-test/configCases/container-1-5/manifest/module.js +++ b/tests/rspack-test/configCases/container-1-5/manifest/module.js @@ -1,8 +1,14 @@ import react from 'react'; +import { loadRemote } from 'mf'; import remote from '@remote/alias'; +import List from '@remote/alias/List'; +import Button from '@scope-scope/ui/Button'; global.react = react; global.remote = remote; +global.dynamicRemote = loadRemote('@dynamic-remote/alias'); +global.Button = Button; +global.List = List import('./lazy-module').then(r=>{ console.log('lazy module: ',r) diff --git a/tests/rspack-test/configCases/container-1-5/manifest/node_modules/mf.js b/tests/rspack-test/configCases/container-1-5/manifest/node_modules/mf.js new file mode 100644 index 000000000000..cee4af956092 --- /dev/null +++ b/tests/rspack-test/configCases/container-1-5/manifest/node_modules/mf.js @@ -0,0 +1,3 @@ +export const loadRemote = async (name) => { + return name; +} diff --git a/tests/rspack-test/configCases/container-1-5/manifest/rspack.config.js b/tests/rspack-test/configCases/container-1-5/manifest/rspack.config.js index 00cf015bc7f0..d0b05a3ee8df 100644 --- a/tests/rspack-test/configCases/container-1-5/manifest/rspack.config.js +++ b/tests/rspack-test/configCases/container-1-5/manifest/rspack.config.js @@ -23,7 +23,9 @@ module.exports = { }, remoteType:'script', remotes: { - '@remote/alias': 'remote@http://localhost:8000/remoteEntry.js' + '@remote/alias': 'remote@http://localhost:8000/remoteEntry.js', + '@dynamic-remote/alias': 'dynamic_remote@http://localhost:8001/remoteEntry.js', + '@scope-scope/ui': 'ui@http://localhost:8002/remoteEntry.js' }, shared: { react: {}