use rspack_cacheable::cacheable;
use rspack_core::{
DependencyId, RuntimeModule, RuntimeModuleGenerateContext, RuntimeModuleStage, RuntimeTemplate,
impl_runtime_module,
};
use rspack_error::Result;
use super::module_federation_runtime_plugin::ModuleFederationRuntimeExperimentsOptions;
#[cacheable]
#[derive(Debug, Default, Clone, Hash, PartialEq, Eq)]
pub struct EmbedFederationRuntimeModuleOptions {
pub collected_dependency_ids: Vec<DependencyId>,
pub experiments: ModuleFederationRuntimeExperimentsOptions,
}
#[impl_runtime_module]
#[derive(Debug)]
pub struct EmbedFederationRuntimeModule {
options: EmbedFederationRuntimeModuleOptions,
}
impl EmbedFederationRuntimeModule {
pub fn new(
runtime_template: &RuntimeTemplate,
options: EmbedFederationRuntimeModuleOptions,
) -> Self {
Self::with_name(runtime_template, "embed_federation_runtime", options)
}
}
enum TemplateId {
Async,
Sync,
}
impl EmbedFederationRuntimeModule {
fn template_id(&self, template_id: TemplateId) -> String {
match template_id {
TemplateId::Async => format!("{}_async", self.id),
TemplateId::Sync => format!("{}_sync", self.id),
}
}
}
#[async_trait::async_trait]
impl RuntimeModule for EmbedFederationRuntimeModule {
fn template(&self) -> Vec<(String, String)> {
vec![
(
self.template_id(TemplateId::Async),
include_str!("./embed_federation_runtime_async.ejs").to_string(),
),
(
self.template_id(TemplateId::Sync),
include_str!("./embed_federation_runtime_sync.ejs").to_string(),
),
]
}
async fn generate(&self, context: &RuntimeModuleGenerateContext<'_>) -> Result<String> {
let compilation = context.compilation;
let chunk_ukey = self
.chunk
.expect("Chunk should be attached to RuntimeModule");
let collected_deps = &self.options.collected_dependency_ids;
let module_graph = compilation.get_module_graph();
let mut federation_runtime_modules = Vec::new();
if !collected_deps.is_empty() {
for dep_id in collected_deps.iter() {
if let Some(module_dyn) = module_graph.get_module_by_dependency_id(dep_id) {
let is_in_chunk = compilation
.build_chunk_graph_artifact
.chunk_graph
.is_module_in_chunk(&module_dyn.identifier(), chunk_ukey);
if is_in_chunk {
federation_runtime_modules.push(*dep_id);
}
}
}
}
let mut module_executions = String::with_capacity(federation_runtime_modules.len() * 64);
let mut runtime_template = compilation.runtime_template.create_module_code_template();
for dep_id in federation_runtime_modules {
let module_str = runtime_template.module_raw(compilation, &dep_id, "", false);
module_executions.push_str("\t\t");
module_executions.push_str(&module_str);
module_executions.push('\n');
}
if self.options.experiments.async_startup {
let entry_chunk_ids = compilation
.build_chunk_graph_artifact
.chunk_by_ukey
.expect_get(&chunk_ukey)
.get_all_initial_chunks(&compilation.build_chunk_graph_artifact.chunk_group_by_ukey)
.into_iter()
.map(|chunk_ukey| {
compilation
.build_chunk_graph_artifact
.chunk_by_ukey
.expect_get(&chunk_ukey)
.expect_id()
.clone()
})
.collect::<Vec<_>>();
let entry_chunk_ids_literal =
serde_json::to_string(&entry_chunk_ids).expect("Invalid json to string");
Ok(context.runtime_template.render(
&self.template_id(TemplateId::Async),
Some(serde_json::json!({
"_module_executions": module_executions,
"_entry_chunk_ids": entry_chunk_ids_literal,
})),
)?)
} else {
if module_executions.is_empty() {
return Ok("// Federation runtime entry modules not found in this chunk.".into());
}
Ok(context.runtime_template.render(
&self.template_id(TemplateId::Sync),
Some(serde_json::json!({
"_module_executions": module_executions,
})),
)?)
}
}
fn stage(&self) -> RuntimeModuleStage {
RuntimeModuleStage::Trigger }
}