diff --git a/crates/rspack_core/src/compiler/compilation.rs b/crates/rspack_core/src/compiler/compilation.rs index 1243850b623..2901465c31b 100644 --- a/crates/rspack_core/src/compiler/compilation.rs +++ b/crates/rspack_core/src/compiler/compilation.rs @@ -22,7 +22,7 @@ use tracing::instrument; use super::{ hmr::CompilationRecords, - make::{update_module_graph, MakeParam}, + make::{update_module_graph, MakeArtifact, MakeParam}, module_executor::ModuleExecutor, }; use crate::{ @@ -137,7 +137,6 @@ pub struct Compilation { pub options: Arc, pub entries: Entry, pub global_entry: EntryData, - make_module_graph: ModuleGraphPartial, other_module_graph: Option, pub dependency_factories: HashMap>, pub make_failed_dependencies: HashSet, @@ -186,6 +185,8 @@ pub struct Compilation { import_var_map: DashMap, pub module_executor: Option, + + make_artifact: MakeArtifact, } impl Compilation { @@ -224,7 +225,6 @@ impl Compilation { hot_index: 0, records, options, - make_module_graph: Default::default(), other_module_graph: None, dependency_factories: Default::default(), make_failed_dependencies: HashSet::default(), @@ -272,6 +272,8 @@ impl Compilation { import_var_map: DashMap::new(), module_executor, + + make_artifact: Default::default(), } } @@ -279,26 +281,38 @@ impl Compilation { self.id } - pub fn swap_make_module_graph_with_compilation(&mut self, other: &mut Compilation) { - std::mem::swap(&mut self.make_module_graph, &mut other.make_module_graph); + pub fn swap_make_artifact_with_compilation(&mut self, other: &mut Compilation) { + std::mem::swap(&mut self.make_artifact, &mut other.make_artifact); } - pub fn swap_make_module_graph(&mut self, module_graph_partial: &mut ModuleGraphPartial) { - std::mem::swap(&mut self.make_module_graph, module_graph_partial); + pub fn swap_make_artifact(&mut self, make_artifact: &mut MakeArtifact) { + std::mem::swap(&mut self.make_artifact, make_artifact); } pub fn get_module_graph(&self) -> ModuleGraph { if let Some(other_module_graph) = &self.other_module_graph { - ModuleGraph::new(vec![&self.make_module_graph, other_module_graph], None) + ModuleGraph::new( + vec![ + self.make_artifact.get_module_graph_partial(), + other_module_graph, + ], + None, + ) } else { - ModuleGraph::new(vec![&self.make_module_graph], None) + ModuleGraph::new(vec![self.make_artifact.get_module_graph_partial()], None) } } pub fn get_module_graph_mut(&mut self) -> ModuleGraph { if let Some(other) = &mut self.other_module_graph { - ModuleGraph::new(vec![&self.make_module_graph], Some(other)) + ModuleGraph::new( + vec![self.make_artifact.get_module_graph_partial()], + Some(other), + ) } else { - ModuleGraph::new(vec![], Some(&mut self.make_module_graph)) + ModuleGraph::new( + vec![], + Some(self.make_artifact.get_module_graph_partial_mut()), + ) } } diff --git a/crates/rspack_core/src/compiler/hmr.rs b/crates/rspack_core/src/compiler/hmr.rs index 3db53ecb0d4..d6eca817681 100644 --- a/crates/rspack_core/src/compiler/hmr.rs +++ b/crates/rspack_core/src/compiler/hmr.rs @@ -89,7 +89,7 @@ where // make stage used self .compilation - .swap_make_module_graph_with_compilation(&mut new_compilation); + .swap_make_artifact_with_compilation(&mut new_compilation); new_compilation.make_failed_dependencies = std::mem::take(&mut self.compilation.make_failed_dependencies); new_compilation.make_failed_module = diff --git a/crates/rspack_core/src/compiler/make/cutout/clean_isolated_module.rs b/crates/rspack_core/src/compiler/make/cutout/clean_isolated_module.rs new file mode 100644 index 00000000000..8dc69133c01 --- /dev/null +++ b/crates/rspack_core/src/compiler/make/cutout/clean_isolated_module.rs @@ -0,0 +1,53 @@ +use std::collections::VecDeque; + +use rustc_hash::FxHashSet as HashSet; + +use super::super::MakeArtifact; +use crate::ModuleIdentifier; + +#[derive(Debug, Default)] +pub struct CleanIsolatedModule { + need_check_isolated_module_ids: HashSet, +} + +impl CleanIsolatedModule { + pub fn analyze_force_build_module( + &mut self, + artifact: &MakeArtifact, + module_identifier: &ModuleIdentifier, + ) { + let module_graph = artifact.get_module_graph(); + for connection in module_graph.get_outgoing_connections(module_identifier) { + self + .need_check_isolated_module_ids + .insert(*connection.module_identifier()); + } + } + + pub fn fix_artifact(self, artifact: &mut MakeArtifact) { + let mut module_graph = artifact.get_module_graph_mut(); + let mut queue = VecDeque::from( + self + .need_check_isolated_module_ids + .into_iter() + .collect::>(), + ); + while let Some(module_identifier) = queue.pop_front() { + let Some(mgm) = module_graph.module_graph_module_by_identifier(&module_identifier) else { + tracing::trace!("Module is cleaned: {}", module_identifier); + continue; + }; + if !mgm.incoming_connections().is_empty() { + tracing::trace!("Module is used: {}", module_identifier); + continue; + } + + for connection in module_graph.get_outgoing_connections(&module_identifier) { + // clean child module + queue.push_back(*connection.module_identifier()); + } + module_graph.revoke_module(&module_identifier); + tracing::trace!("Module is cleaned: {}", module_identifier); + } + } +} diff --git a/crates/rspack_core/src/compiler/make/cutout/fix_issuers.rs b/crates/rspack_core/src/compiler/make/cutout/fix_issuers.rs new file mode 100644 index 00000000000..c6d8102361f --- /dev/null +++ b/crates/rspack_core/src/compiler/make/cutout/fix_issuers.rs @@ -0,0 +1,34 @@ +use rustc_hash::FxHashMap as HashMap; + +use super::super::MakeArtifact; +use crate::{ModuleIdentifier, ModuleIssuer}; + +#[derive(Debug, Default)] +pub struct FixIssuers { + origin_module_issuers: HashMap, +} + +impl FixIssuers { + pub fn analyze_force_build_module( + &mut self, + artifact: &MakeArtifact, + module_identifier: &ModuleIdentifier, + ) { + let module_graph = artifact.get_module_graph(); + let mgm = module_graph + .module_graph_module_by_identifier(module_identifier) + .expect("should have module graph module"); + self + .origin_module_issuers + .insert(*module_identifier, mgm.get_issuer().clone()); + } + + pub fn fix_artifact(self, artifact: &mut MakeArtifact) { + let mut module_graph = artifact.get_module_graph_mut(); + for (id, issuer) in self.origin_module_issuers.into_iter() { + if let Some(mgm) = module_graph.module_graph_module_by_identifier_mut(&id) { + mgm.set_issuer(issuer); + } + } + } +} diff --git a/crates/rspack_core/src/compiler/make/cutout/has_module_graph_change.rs b/crates/rspack_core/src/compiler/make/cutout/has_module_graph_change.rs new file mode 100644 index 00000000000..9a308dafb05 --- /dev/null +++ b/crates/rspack_core/src/compiler/make/cutout/has_module_graph_change.rs @@ -0,0 +1,76 @@ +use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet}; + +use super::super::MakeArtifact; +use crate::{AsyncDependenciesBlockIdentifier, GroupOptions, ModuleGraph, ModuleIdentifier}; + +#[derive(Debug, Default, Eq, PartialEq)] +struct ModuleDeps { + // child module identifier of current module + child_modules: HashSet, + // blocks in current module + module_blocks: HashSet<(AsyncDependenciesBlockIdentifier, Option)>, +} + +impl ModuleDeps { + fn from_module(module_graph: &ModuleGraph, module_identifier: &ModuleIdentifier) -> Self { + let mut res = Self::default(); + for connection in module_graph.get_outgoing_connections(module_identifier) { + res.child_modules.insert(*connection.module_identifier()); + } + let module = module_graph + .module_by_identifier(module_identifier) + .expect("should have module"); + for block_id in module.get_blocks() { + let block = module_graph + .block_by_id(block_id) + .expect("should have block"); + res + .module_blocks + .insert((*block_id, block.get_group_options().cloned())); + } + + res + } +} + +#[derive(Debug, Default)] +pub struct HasModuleGraphChange { + origin_module_deps: HashMap, +} + +impl HasModuleGraphChange { + pub fn analyze_force_build_module( + &mut self, + artifact: &MakeArtifact, + module_identifier: &ModuleIdentifier, + ) { + let module_graph = &artifact.get_module_graph(); + self.origin_module_deps.insert( + *module_identifier, + ModuleDeps::from_module(module_graph, module_identifier), + ); + } + + pub fn fix_artifact(self, artifact: &mut MakeArtifact) { + let module_graph = &artifact.get_module_graph(); + if self.origin_module_deps.is_empty() { + // origin_module_deps empty means no force_build_module and no file changed + // this only happens when build from entry + artifact.has_module_graph_change = true; + return; + } + // if artifact.has_module_graph_change is true, no need to recalculate + if !artifact.has_module_graph_change { + for (module_identifier, module_deps) in self.origin_module_deps { + if module_graph + .module_by_identifier(&module_identifier) + .is_none() + || ModuleDeps::from_module(module_graph, &module_identifier) != module_deps + { + artifact.has_module_graph_change = true; + return; + } + } + } + } +} diff --git a/crates/rspack_core/src/compiler/make/cutout/mod.rs b/crates/rspack_core/src/compiler/make/cutout/mod.rs new file mode 100644 index 00000000000..d18c17344e6 --- /dev/null +++ b/crates/rspack_core/src/compiler/make/cutout/mod.rs @@ -0,0 +1,112 @@ +mod clean_isolated_module; +mod fix_issuers; +mod has_module_graph_change; + +use rustc_hash::FxHashSet as HashSet; + +use self::{ + clean_isolated_module::CleanIsolatedModule, fix_issuers::FixIssuers, + has_module_graph_change::HasModuleGraphChange, +}; +use super::{MakeArtifact, MakeParam}; +use crate::BuildDependency; + +#[derive(Debug, Default)] +pub struct Cutout { + fix_issuers: FixIssuers, + clean_isolated_module: CleanIsolatedModule, + has_module_graph_change: HasModuleGraphChange, +} + +impl Cutout { + pub fn cutout_artifact( + &mut self, + artifact: &mut MakeArtifact, + params: Vec, + ) -> HashSet { + let module_graph = artifact.get_module_graph(); + + let mut force_build_modules = HashSet::default(); + let mut force_build_deps = HashSet::default(); + + for item in params { + match item { + MakeParam::ModifiedFiles(files) => { + force_build_modules.extend(module_graph.modules().values().filter_map(|module| { + // check has dependencies modified + if !module.is_available(&files) { + Some(module.identifier()) + } else { + None + } + })) + } + MakeParam::DeletedFiles(files) => { + force_build_modules.extend(module_graph.modules().values().flat_map(|module| { + let mut res = vec![]; + + // check has dependencies modified + if !module.is_available(&files) { + // add module id + res.push(module.identifier()); + // add parent module id + res.extend( + module_graph + .get_incoming_connections(&module.identifier()) + .iter() + .filter_map(|connect| connect.original_module_identifier), + ) + } + res + })) + } + MakeParam::ForceBuildDeps(deps) => { + for item in deps { + let (dependency_id, _) = &item; + // add deps bindings module to force_build_modules + if let Some(mid) = module_graph.module_identifier_by_dependency_id(dependency_id) { + force_build_modules.insert(*mid); + } + force_build_deps.insert(item); + } + } + MakeParam::ForceBuildModules(modules) => { + force_build_modules.extend(modules); + } + }; + } + + for module_identifier in &force_build_modules { + self + .fix_issuers + .analyze_force_build_module(artifact, module_identifier); + self + .clean_isolated_module + .analyze_force_build_module(artifact, module_identifier); + self + .has_module_graph_change + .analyze_force_build_module(artifact, module_identifier); + } + + let mut module_graph = artifact.get_module_graph_mut(); + // do revoke module and collect deps + force_build_deps.extend( + force_build_modules + .iter() + .flat_map(|id| module_graph.revoke_module(id)), + ); + + force_build_deps + } + + pub fn fix_artifact(self, artifact: &mut MakeArtifact) { + let Self { + fix_issuers, + clean_isolated_module, + has_module_graph_change, + } = self; + fix_issuers.fix_artifact(artifact); + clean_isolated_module.fix_artifact(artifact); + has_module_graph_change.fix_artifact(artifact); + } +} diff --git a/crates/rspack_core/src/compiler/make/mod.rs b/crates/rspack_core/src/compiler/make/mod.rs index d9e6647d3de..fa0cc36356f 100644 --- a/crates/rspack_core/src/compiler/make/mod.rs +++ b/crates/rspack_core/src/compiler/make/mod.rs @@ -1,23 +1,81 @@ -mod rebuild_deps_builder; -mod tasks; +mod cutout; +mod repair; -use std::path::PathBuf; +use std::{hash::BuildHasherDefault, path::PathBuf}; +use indexmap::IndexSet; use rayon::prelude::*; -use rspack_error::Result; -use rspack_identifier::Identifier; -use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet}; +use rspack_error::{Diagnostic, Result}; +use rspack_identifier::{IdentifierMap, IdentifierSet}; +use rustc_hash::{FxHashSet as HashSet, FxHasher}; -pub use self::rebuild_deps_builder::RebuildDepsBuilder; -use self::tasks::{clean::CleanTask, factorize::FactorizeTask, MakeTaskContext}; +use self::{cutout::Cutout, repair::repair}; use crate::{ - tree_shaking::BailoutFlag, - utils::task_loop::{run_task_loop, Task}, - AsyncDependenciesBlockIdentifier, BuildDependency, Compilation, Context, DependencyId, - DependencyType, GroupOptions, Module, ModuleGraphPartial, ModuleIdentifier, ModuleIssuer, - ModuleProfile, NormalModuleSource, Resolve, + tree_shaking::{visitor::OptimizeAnalyzeResult, BailoutFlag}, + BuildDependency, Compilation, DependencyId, DependencyType, ModuleGraph, ModuleGraphPartial, + ModuleIdentifier, }; +#[derive(Debug, Default)] +pub struct MakeArtifact { + module_graph_partial: ModuleGraphPartial, + make_failed_dependencies: HashSet, + make_failed_module: HashSet, + diagnostics: Vec, + + entry_module_identifiers: IdentifierSet, + optimize_analyze_result_map: IdentifierMap, + file_dependencies: IndexSet>, + context_dependencies: IndexSet>, + missing_dependencies: IndexSet>, + build_dependencies: IndexSet>, + + has_module_graph_change: bool, +} + +impl MakeArtifact { + fn get_module_graph(&self) -> ModuleGraph { + ModuleGraph::new(vec![&self.module_graph_partial], None) + } + fn get_module_graph_mut(&mut self) -> ModuleGraph { + ModuleGraph::new(vec![], Some(&mut self.module_graph_partial)) + } + // TODO remove it + pub fn get_module_graph_partial(&self) -> &ModuleGraphPartial { + &self.module_graph_partial + } + // TODO remove it + pub fn get_module_graph_partial_mut(&mut self) -> &mut ModuleGraphPartial { + &mut self.module_graph_partial + } + + // TODO remove it + fn move_data_from_compilation(&mut self, compilation: &mut Compilation) { + self.entry_module_identifiers = std::mem::take(&mut compilation.entry_module_identifiers); + self.optimize_analyze_result_map = std::mem::take(&mut compilation.optimize_analyze_result_map); + self.file_dependencies = std::mem::take(&mut compilation.file_dependencies); + self.context_dependencies = std::mem::take(&mut compilation.context_dependencies); + self.missing_dependencies = std::mem::take(&mut compilation.missing_dependencies); + self.build_dependencies = std::mem::take(&mut compilation.build_dependencies); + self.has_module_graph_change = compilation.has_module_import_export_change; + } + + // TODO remove it + fn move_data_to_compilation(&mut self, compilation: &mut Compilation) { + compilation.entry_module_identifiers = std::mem::take(&mut self.entry_module_identifiers); + compilation.optimize_analyze_result_map = std::mem::take(&mut self.optimize_analyze_result_map); + compilation.file_dependencies = std::mem::take(&mut self.file_dependencies); + compilation.context_dependencies = std::mem::take(&mut self.context_dependencies); + compilation.missing_dependencies = std::mem::take(&mut self.missing_dependencies); + compilation.build_dependencies = std::mem::take(&mut self.build_dependencies); + compilation.has_module_import_export_change = self.has_module_graph_change; + + compilation.push_batch_diagnostic(std::mem::take(&mut self.diagnostics)); + compilation.make_failed_module = std::mem::take(&mut self.make_failed_module); + compilation.make_failed_dependencies = std::mem::take(&mut self.make_failed_dependencies); + } +} + #[derive(Debug)] pub enum MakeParam { ModifiedFiles(HashSet), @@ -38,297 +96,56 @@ pub async fn update_module_graph( compilation: &mut Compilation, params: Vec, ) -> Result<()> { - let mut builder = UpdateModuleGraph::default(); - let build_dependencies = builder.cutout(compilation, params)?; - builder.repair(compilation, build_dependencies) -} - -type ModuleDeps = ( - Vec, - Vec<(AsyncDependenciesBlockIdentifier, Option)>, -); - -#[derive(Default)] -struct UpdateModuleGraph { - origin_module_deps: HashMap, - /// Rebuild module issuer mappings - origin_module_issuers: HashMap, - - need_check_isolated_module_ids: HashSet, -} - -impl UpdateModuleGraph { - fn cutout( - &mut self, - compilation: &mut Compilation, - params: Vec, - ) -> Result> { - let deps_builder = RebuildDepsBuilder::new(params, &compilation.get_module_graph()); - - self.origin_module_deps = HashMap::from_iter( - deps_builder - .get_force_build_modules() - .iter() - .map(|module_identifier| { - ( - *module_identifier, - Self::module_deps(compilation, module_identifier), - ) - }), - ); - - let module_graph = compilation.get_module_graph(); - // calc need_check_isolated_module_ids & regen_module_issues - for id in deps_builder.get_force_build_modules() { - if let Some(mgm) = compilation - .get_module_graph() - .module_graph_module_by_identifier(id) - { - let depended_modules = module_graph - .get_module_all_depended_modules(id) - .expect("module graph module not exist") - .into_iter() - .copied(); - self.need_check_isolated_module_ids.extend(depended_modules); - self - .origin_module_issuers - .insert(*id, mgm.get_issuer().clone()); - } - } - - Ok(deps_builder.revoke_modules(&mut compilation.get_module_graph_mut())) - } - - fn repair( - &mut self, - compilation: &mut Compilation, - build_dependencies: HashSet, - ) -> Result<()> { - let module_graph = compilation.get_module_graph(); - let init_tasks = build_dependencies - .into_iter() - .filter_map(|(id, parent_module_identifier)| { - let dependency = module_graph - .dependency_by_id(&id) - .expect("dependency not found"); - if dependency.as_module_dependency().is_none() - && dependency.as_context_dependency().is_none() + let mut artifact = MakeArtifact::default(); + compilation.swap_make_artifact(&mut artifact); + artifact.move_data_from_compilation(compilation); + let mut cutout = Cutout::default(); + let build_dependencies = cutout.cutout_artifact(&mut artifact, params); + artifact = repair(compilation, artifact, build_dependencies)?; + cutout.fix_artifact(&mut artifact); + + // Avoid to introduce too much overhead, + // until we find a better way to align with webpack hmr behavior + + // add context module and context element module to bailout_module_identifiers + if compilation.options.builtins.tree_shaking.enable() { + let module_graph = artifact.get_module_graph(); + compilation.bailout_module_identifiers = module_graph + .dependencies() + .values() + .par_bridge() + .filter_map(|dep| { + if dep.as_context_dependency().is_some() + && let Some(module) = module_graph.get_module_by_dependency_id(dep.id()) { - return None; - } - - let parent_module = - parent_module_identifier.and_then(|id| module_graph.module_by_identifier(&id)); - if parent_module_identifier.is_some() && parent_module.is_none() { - return None; - } - Some( - self.handle_module_creation( - compilation, - parent_module_identifier, - parent_module.and_then(|m| m.get_context()), - vec![id], - parent_module_identifier.is_none(), - parent_module.and_then(|module| module.get_resolve_options()), - parent_module - .and_then(|m| m.as_normal_module()) - .and_then(|module| module.name_for_condition()), - ), - ) - }) - .collect::>(); - - let mut make_module_graph = ModuleGraphPartial::default(); - compilation.swap_make_module_graph(&mut make_module_graph); - let mut ctx = MakeTaskContext::new(compilation, make_module_graph); - let res = run_task_loop(&mut ctx, init_tasks); - - tracing::debug!("All task is finished"); - - // clean isolated module - let mut clean_tasks: Vec>> = - Vec::with_capacity(self.need_check_isolated_module_ids.len()); - for module_identifier in &self.need_check_isolated_module_ids { - clean_tasks.push(Box::new(CleanTask { - module_identifier: *module_identifier, - })); - } - run_task_loop(&mut ctx, clean_tasks)?; - - ctx.emit_data_to_compilation(compilation); - - tracing::debug!("All clean task is finished"); - // set origin module issues - for (id, issuer) in self.origin_module_issuers.drain() { - if let Some(mgm) = compilation - .get_module_graph_mut() - .module_graph_module_by_identifier_mut(&id) - { - mgm.set_issuer(issuer); - } - } - - // calc has_module_import_export_change - compilation.has_module_import_export_change = if self.origin_module_deps.is_empty() { - true - } else { - compilation.has_module_import_export_change - || !self.origin_module_deps.drain().all(|(module_id, deps)| { - if compilation - .get_module_graph_mut() - .module_by_identifier(&module_id) - .is_none() - { - false - } else { - let (now_deps, mut now_blocks) = Self::module_deps(compilation, &module_id); - let (origin_deps, mut origin_blocks) = deps; - if now_deps.len() != origin_deps.len() || now_blocks.len() != origin_blocks.len() { - false - } else { - for index in 0..origin_deps.len() { - if origin_deps[index] != now_deps[index] { - return false; - } - } - - now_blocks.sort_unstable(); - origin_blocks.sort_unstable(); - - for index in 0..origin_blocks.len() { - if origin_blocks[index].0 != now_blocks[index].0 { - return false; - } - if origin_blocks[index].1 != now_blocks[index].1 { - return false; - } - } - - true - } - } - }) - }; - - // Avoid to introduce too much overhead, - // until we find a better way to align with webpack hmr behavior - - // add context module and context element module to bailout_module_identifiers - if compilation.options.builtins.tree_shaking.enable() { - compilation.bailout_module_identifiers = compilation - .get_module_graph() - .dependencies() - .values() - .par_bridge() - .filter_map(|dep| { - if dep.as_context_dependency().is_some() - && let Some(module) = compilation - .get_module_graph() - .get_module_by_dependency_id(dep.id()) + let mut values = vec![(module.identifier(), BailoutFlag::CONTEXT_MODULE)]; + if let Some(dependencies) = module_graph.get_module_all_dependencies(&module.identifier()) { - let mut values = vec![(module.identifier(), BailoutFlag::CONTEXT_MODULE)]; - if let Some(dependencies) = compilation - .get_module_graph() - .get_module_all_dependencies(&module.identifier()) - { - for dependency in dependencies { - if let Some(dependency_module) = compilation - .get_module_graph() - .module_identifier_by_dependency_id(dependency) - { - values.push((*dependency_module, BailoutFlag::CONTEXT_MODULE)); - } + for dependency in dependencies { + if let Some(dependency_module) = + module_graph.module_identifier_by_dependency_id(dependency) + { + values.push((*dependency_module, BailoutFlag::CONTEXT_MODULE)); } } - - Some(values) - } else if matches!( - dep.dependency_type(), - DependencyType::ContainerExposed | DependencyType::ProvideModuleForShared - ) && let Some(module) = compilation - .get_module_graph() - .get_module_by_dependency_id(dep.id()) - { - Some(vec![(module.identifier(), BailoutFlag::CONTAINER_EXPOSED)]) - } else { - None } - }) - .flatten() - .collect(); - } - res - } - - #[allow(clippy::too_many_arguments)] - fn handle_module_creation( - &mut self, - compilation: &Compilation, - original_module_identifier: Option, - original_module_context: Option>, - dependencies: Vec, - is_entry: bool, - resolve_options: Option>, - issuer: Option>, - ) -> Box> { - let current_profile = compilation - .options - .profile - .then(Box::::default); - let dependency = compilation - .get_module_graph() - .dependency_by_id(&dependencies[0]) - .expect("should have dependency") - .clone(); - let module_graph = compilation.get_module_graph(); - let original_module_source = original_module_identifier - .and_then(|i| module_graph.module_by_identifier(&i)) - .and_then(|m| m.as_normal_module()) - .and_then(|m| { - if let NormalModuleSource::BuiltSucceed(s) = m.source() { - Some(s.clone()) + Some(values) + } else if matches!( + dep.dependency_type(), + DependencyType::ContainerExposed | DependencyType::ProvideModuleForShared + ) && let Some(module) = module_graph.get_module_by_dependency_id(dep.id()) + { + Some(vec![(module.identifier(), BailoutFlag::CONTAINER_EXPOSED)]) } else { None } - }); - Box::new(FactorizeTask { - module_factory: compilation.get_dependency_factory(&dependency), - original_module_identifier, - original_module_source, - issuer, - original_module_context, - dependency, - dependencies, - is_entry, - resolve_options, - resolver_factory: compilation.resolver_factory.clone(), - loader_resolver_factory: compilation.loader_resolver_factory.clone(), - options: compilation.options.clone(), - plugin_driver: compilation.plugin_driver.clone(), - cache: compilation.cache.clone(), - current_profile, - }) - } - - fn module_deps(compilation: &Compilation, module_identifier: &ModuleIdentifier) -> ModuleDeps { - let module_graph = compilation.get_module_graph(); - let (deps, blocks) = module_graph.get_module_dependencies_modules_and_blocks(module_identifier); - - let blocks_with_option: Vec<_> = blocks - .iter() - .map(|block| { - ( - *block, - compilation - .get_module_graph() - .block_by_id(block) - .expect("block muse be exist") - .get_group_options() - .cloned(), - ) }) + .flatten() .collect(); - (deps, blocks_with_option) } + + artifact.move_data_to_compilation(compilation); + compilation.swap_make_artifact(&mut artifact); + Ok(()) } diff --git a/crates/rspack_core/src/compiler/make/rebuild_deps_builder.rs b/crates/rspack_core/src/compiler/make/rebuild_deps_builder.rs deleted file mode 100644 index 7dc038c12c9..00000000000 --- a/crates/rspack_core/src/compiler/make/rebuild_deps_builder.rs +++ /dev/null @@ -1,100 +0,0 @@ -use rustc_hash::FxHashSet as HashSet; - -use super::MakeParam; -use crate::{BuildDependency, ModuleGraph, ModuleIdentifier}; - -#[derive(Debug, Default)] -pub struct RebuildDepsBuilder { - /// the modules that need to be built - force_build_modules: HashSet, - /// the deps that need to be built - force_build_deps: HashSet, -} - -impl RebuildDepsBuilder { - pub fn new(params: Vec, module_graph: &ModuleGraph) -> Self { - let mut builder = Self::default(); - - for item in params { - match item { - MakeParam::ModifiedFiles(files) => { - builder.extend_force_build_modules(module_graph.modules().values().filter_map(|module| { - // check has dependencies modified - if !module.is_available(&files) { - Some(module.identifier()) - } else { - None - } - })) - } - MakeParam::DeletedFiles(files) => { - builder.extend_force_build_modules(module_graph.modules().values().flat_map(|module| { - let mut res: Vec = vec![]; - - // check has dependencies modified - if !module.is_available(&files) { - // module id - res.push(module.identifier()); - // parent module id - res.extend( - module_graph - .get_incoming_connections(&module.identifier()) - .iter() - .filter_map(|connect| connect.original_module_identifier), - ) - } - res - })) - } - MakeParam::ForceBuildDeps(deps) => { - builder.extend_force_build_deps(module_graph, deps); - } - MakeParam::ForceBuildModules(modules) => { - builder.extend_force_build_modules(modules); - } - }; - } - - builder - } - - pub fn extend_force_build_modules>( - &mut self, - modules: I, - ) { - self.force_build_modules.extend(modules); - } - - pub fn extend_force_build_deps>( - &mut self, - module_graph: &ModuleGraph, - deps: I, - ) { - for item in deps { - let (dependency_id, _) = &item; - // add deps bindings module to force_build_modules - if let Some(mid) = module_graph.module_identifier_by_dependency_id(dependency_id) { - self.force_build_modules.insert(*mid); - } - self.force_build_deps.insert(item); - } - } - - pub fn get_force_build_modules(&self) -> &HashSet { - &self.force_build_modules - } - - // pub fn get_force_build_deps(&self) -> &HashSet { - // &self.force_build_deps - // } - - pub fn revoke_modules(mut self, module_graph: &mut ModuleGraph) -> HashSet { - self.force_build_deps.extend( - self - .force_build_modules - .iter() - .flat_map(|id| module_graph.revoke_module(id)), - ); - self.force_build_deps - } -} diff --git a/crates/rspack_core/src/compiler/make/tasks/add.rs b/crates/rspack_core/src/compiler/make/repair/add.rs similarity index 96% rename from crates/rspack_core/src/compiler/make/tasks/add.rs rename to crates/rspack_core/src/compiler/make/repair/add.rs index 756d5b8f91d..8ece49a7d09 100644 --- a/crates/rspack_core/src/compiler/make/tasks/add.rs +++ b/crates/rspack_core/src/compiler/make/repair/add.rs @@ -28,7 +28,8 @@ impl Task for AddTask { let module_identifier = self.module.identifier(); let is_new_treeshaking = context.compiler_options.is_new_tree_shaking(); - let module_graph = &mut MakeTaskContext::get_module_graph(&mut context.module_graph_partial); + let module_graph = + &mut MakeTaskContext::get_module_graph_mut(&mut context.module_graph_partial); if self.module.as_self_module().is_some() { let issuer = self diff --git a/crates/rspack_core/src/compiler/make/tasks/build.rs b/crates/rspack_core/src/compiler/make/repair/build.rs similarity index 98% rename from crates/rspack_core/src/compiler/make/tasks/build.rs rename to crates/rspack_core/src/compiler/make/repair/build.rs index 781bd6997f4..923a26ea96e 100644 --- a/crates/rspack_core/src/compiler/make/tasks/build.rs +++ b/crates/rspack_core/src/compiler/make/repair/build.rs @@ -138,7 +138,8 @@ impl Task for BuildResultTask { } } - let module_graph = &mut MakeTaskContext::get_module_graph(&mut context.module_graph_partial); + let module_graph = + &mut MakeTaskContext::get_module_graph_mut(&mut context.module_graph_partial); if context.compiler_options.builtins.tree_shaking.enable() { context .optimize_analyze_result_map diff --git a/crates/rspack_core/src/compiler/make/tasks/factorize.rs b/crates/rspack_core/src/compiler/make/repair/factorize.rs similarity index 98% rename from crates/rspack_core/src/compiler/make/tasks/factorize.rs rename to crates/rspack_core/src/compiler/make/repair/factorize.rs index 70d19d0ef3f..fcbe67a6739 100644 --- a/crates/rspack_core/src/compiler/make/tasks/factorize.rs +++ b/crates/rspack_core/src/compiler/make/repair/factorize.rs @@ -227,7 +227,8 @@ impl Task for FactorizeResultTask { context.file_dependencies.extend(file_dependencies); context.context_dependencies.extend(context_dependencies); context.missing_dependencies.extend(missing_dependencies); - let module_graph = &mut MakeTaskContext::get_module_graph(&mut context.module_graph_partial); + let module_graph = + &mut MakeTaskContext::get_module_graph_mut(&mut context.module_graph_partial); let Some(factory_result) = factory_result else { let dep = module_graph .dependency_by_id(&dependencies[0]) diff --git a/crates/rspack_core/src/compiler/make/repair/mod.rs b/crates/rspack_core/src/compiler/make/repair/mod.rs new file mode 100644 index 00000000000..e34aa16038f --- /dev/null +++ b/crates/rspack_core/src/compiler/make/repair/mod.rs @@ -0,0 +1,210 @@ +mod add; +mod build; +mod factorize; +mod process_dependencies; + +use std::{hash::BuildHasherDefault, path::PathBuf, sync::Arc}; + +use indexmap::IndexSet; +use rspack_error::{Diagnostic, Result}; +use rspack_identifier::{IdentifierMap, IdentifierSet}; +use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet, FxHasher}; + +use super::MakeArtifact; +use crate::{ + cache::Cache, + module_graph::{ModuleGraph, ModuleGraphPartial}, + tree_shaking::visitor::OptimizeAnalyzeResult, + utils::task_loop::{run_task_loop, Task}, + BuildDependency, CacheCount, CacheOptions, Compilation, CompilationLogger, CompilerOptions, + DependencyType, Logger, Module, ModuleFactory, ModuleIdentifier, ModuleProfile, + NormalModuleSource, ResolverFactory, SharedPluginDriver, +}; + +struct MakeTaskContext { + // compilation info + plugin_driver: SharedPluginDriver, + compiler_options: Arc, + resolver_factory: Arc, + loader_resolver_factory: Arc, + cache: Arc, + dependency_factories: HashMap>, + + // TODO move outof context + logger: CompilationLogger, + build_cache_counter: Option, + factorize_cache_counter: Option, + // add_timer: StartTimeAggregate, + // process_deps_timer: StartTimeAggregate, + // factorize_timer: StartTimeAggregate, + // build_timer: StartTimeAggregate, + /// Collecting all module that need to skip in tree-shaking ast modification phase + // bailout_module_identifiers: IdentifierMap, + // TODO change to artifact + module_graph_partial: ModuleGraphPartial, + make_failed_dependencies: HashSet, + make_failed_module: HashSet, + + entry_module_identifiers: IdentifierSet, + diagnostics: Vec, + optimize_analyze_result_map: IdentifierMap, + file_dependencies: IndexSet>, + context_dependencies: IndexSet>, + missing_dependencies: IndexSet>, + build_dependencies: IndexSet>, + has_module_graph_change: bool, +} + +impl MakeTaskContext { + fn new(compilation: &Compilation, artifact: MakeArtifact) -> Self { + let logger = compilation.get_logger("rspack.Compilation"); + let mut build_cache_counter = None; + let mut factorize_cache_counter = None; + if !(matches!(compilation.options.cache, CacheOptions::Disabled)) { + build_cache_counter = Some(logger.cache("module build cache")); + factorize_cache_counter = Some(logger.cache("module factorize cache")); + } + + Self { + plugin_driver: compilation.plugin_driver.clone(), + compiler_options: compilation.options.clone(), + resolver_factory: compilation.resolver_factory.clone(), + loader_resolver_factory: compilation.loader_resolver_factory.clone(), + cache: compilation.cache.clone(), + dependency_factories: compilation.dependency_factories.clone(), + + // TODO use timer in tasks + logger, + build_cache_counter, + factorize_cache_counter, + // add_timer: logger.time_aggregate("module add task"), + // process_deps_timer: logger.time_aggregate("module process dependencies task"), + // factorize_timer: logger.time_aggregate("module factorize task"), + // build_timer: logger.time_aggregate("module build task"), + module_graph_partial: artifact.module_graph_partial, + // ignore make_failed_xxx and diagnostics + make_failed_dependencies: Default::default(), + make_failed_module: Default::default(), + diagnostics: Default::default(), + + entry_module_identifiers: artifact.entry_module_identifiers, + optimize_analyze_result_map: artifact.optimize_analyze_result_map, + file_dependencies: artifact.file_dependencies, + context_dependencies: artifact.context_dependencies, + missing_dependencies: artifact.missing_dependencies, + build_dependencies: artifact.build_dependencies, + has_module_graph_change: artifact.has_module_graph_change, + } + } + + fn transform_to_make_artifact(self) -> MakeArtifact { + let Self { + module_graph_partial, + make_failed_dependencies, + make_failed_module, + diagnostics, + entry_module_identifiers, + optimize_analyze_result_map, + file_dependencies, + context_dependencies, + missing_dependencies, + build_dependencies, + has_module_graph_change, + build_cache_counter, + factorize_cache_counter, + logger, + .. + } = self; + if let Some(counter) = build_cache_counter { + logger.cache_end(counter); + } + if let Some(counter) = factorize_cache_counter { + logger.cache_end(counter); + } + MakeArtifact { + module_graph_partial, + make_failed_dependencies, + make_failed_module, + diagnostics, + entry_module_identifiers, + optimize_analyze_result_map, + file_dependencies, + context_dependencies, + missing_dependencies, + build_dependencies, + has_module_graph_change, + } + } + + // TODO use module graph with make artifact + fn get_module_graph_mut(partial: &mut ModuleGraphPartial) -> ModuleGraph { + ModuleGraph::new(vec![], Some(partial)) + } +} + +pub fn repair( + compilation: &Compilation, + mut artifact: MakeArtifact, + build_dependencies: HashSet, +) -> Result { + let module_graph = artifact.get_module_graph_mut(); + let init_tasks = build_dependencies + .into_iter() + .filter_map::>, _>(|(id, parent_module_identifier)| { + let dependency = module_graph + .dependency_by_id(&id) + .expect("dependency not found"); + // filter module_dependency and context_dependency + if dependency.as_module_dependency().is_none() && dependency.as_context_dependency().is_none() + { + return None; + } + + // filter parent module existed dependency + let parent_module = + parent_module_identifier.and_then(|id| module_graph.module_by_identifier(&id)); + if parent_module_identifier.is_some() && parent_module.is_none() { + return None; + } + + let current_profile = compilation + .options + .profile + .then(Box::::default); + let module_graph = compilation.get_module_graph(); + let original_module_source = parent_module_identifier + .and_then(|i| module_graph.module_by_identifier(&i)) + .and_then(|m| m.as_normal_module()) + .and_then(|m| { + if let NormalModuleSource::BuiltSucceed(s) = m.source() { + Some(s.clone()) + } else { + None + } + }); + Some(Box::new(factorize::FactorizeTask { + module_factory: compilation.get_dependency_factory(dependency), + original_module_identifier: parent_module_identifier, + original_module_source, + issuer: parent_module + .and_then(|m| m.as_normal_module()) + .and_then(|module| module.name_for_condition()), + original_module_context: parent_module.and_then(|m| m.get_context()), + dependency: dependency.clone(), + dependencies: vec![id], + is_entry: parent_module_identifier.is_none(), + resolve_options: parent_module.and_then(|module| module.get_resolve_options()), + resolver_factory: compilation.resolver_factory.clone(), + loader_resolver_factory: compilation.loader_resolver_factory.clone(), + options: compilation.options.clone(), + plugin_driver: compilation.plugin_driver.clone(), + cache: compilation.cache.clone(), + current_profile, + })) + }) + .collect::>(); + + let mut ctx = MakeTaskContext::new(compilation, artifact); + run_task_loop(&mut ctx, init_tasks)?; + Ok(ctx.transform_to_make_artifact()) +} diff --git a/crates/rspack_core/src/compiler/make/tasks/process_dependencies.rs b/crates/rspack_core/src/compiler/make/repair/process_dependencies.rs similarity index 97% rename from crates/rspack_core/src/compiler/make/tasks/process_dependencies.rs rename to crates/rspack_core/src/compiler/make/repair/process_dependencies.rs index 07de4bb038a..0d9fb3b1692 100644 --- a/crates/rspack_core/src/compiler/make/tasks/process_dependencies.rs +++ b/crates/rspack_core/src/compiler/make/repair/process_dependencies.rs @@ -26,7 +26,8 @@ impl Task for ProcessDependenciesTask { resolve_options, } = *self; let mut sorted_dependencies = HashMap::default(); - let module_graph = &mut MakeTaskContext::get_module_graph(&mut context.module_graph_partial); + let module_graph = + &mut MakeTaskContext::get_module_graph_mut(&mut context.module_graph_partial); dependencies.into_iter().for_each(|dependency_id| { let dependency = module_graph diff --git a/crates/rspack_core/src/compiler/make/tasks/clean.rs b/crates/rspack_core/src/compiler/make/tasks/clean.rs deleted file mode 100644 index dba2cb18556..00000000000 --- a/crates/rspack_core/src/compiler/make/tasks/clean.rs +++ /dev/null @@ -1,43 +0,0 @@ -use super::MakeTaskContext; -use crate::{ - utils::task_loop::{Task, TaskResult, TaskType}, - ModuleIdentifier, -}; - -pub struct CleanTask { - pub module_identifier: ModuleIdentifier, -} - -impl Task for CleanTask { - fn get_task_type(&self) -> TaskType { - TaskType::Sync - } - fn sync_run(self: Box, context: &mut MakeTaskContext) -> TaskResult { - let module_identifier = self.module_identifier; - let module_graph = &mut MakeTaskContext::get_module_graph(&mut context.module_graph_partial); - let Some(mgm) = module_graph.module_graph_module_by_identifier(&module_identifier) else { - tracing::trace!("Module is cleaned: {}", module_identifier); - return Ok(vec![]); - }; - - if !mgm.incoming_connections().is_empty() { - tracing::trace!("Module is used: {}", module_identifier); - return Ok(vec![]); - } - - let dependent_module_identifiers: Vec = module_graph - .get_module_all_depended_modules(&module_identifier) - .expect("should have module") - .into_iter() - .copied() - .collect(); - module_graph.revoke_module(&module_identifier); - - let mut res: Vec>> = - Vec::with_capacity(dependent_module_identifiers.len()); - for module_identifier in dependent_module_identifiers { - res.push(Box::new(CleanTask { module_identifier })) - } - Ok(res) - } -} diff --git a/crates/rspack_core/src/compiler/make/tasks/mod.rs b/crates/rspack_core/src/compiler/make/tasks/mod.rs deleted file mode 100644 index 666e5c9342e..00000000000 --- a/crates/rspack_core/src/compiler/make/tasks/mod.rs +++ /dev/null @@ -1,130 +0,0 @@ -mod add; -mod build; -pub mod clean; -pub mod factorize; -mod process_dependencies; - -use std::{hash::BuildHasherDefault, path::PathBuf, sync::Arc}; - -use indexmap::IndexSet; -use rspack_error::Diagnostic; -use rspack_identifier::{IdentifierMap, IdentifierSet}; -use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet, FxHasher}; - -use crate::{ - cache::Cache, module_graph::ModuleGraph, tree_shaking::visitor::OptimizeAnalyzeResult, - BuildDependency, CacheCount, CacheOptions, Compilation, CompilationLogger, CompilerOptions, - DependencyType, Logger, ModuleFactory, ModuleGraphPartial, ModuleIdentifier, ResolverFactory, - SharedPluginDriver, -}; - -pub struct MakeTaskContext { - plugin_driver: SharedPluginDriver, - compiler_options: Arc, - resolver_factory: Arc, - loader_resolver_factory: Arc, - cache: Arc, - dependency_factories: HashMap>, - - module_graph_partial: ModuleGraphPartial, - make_failed_dependencies: HashSet, - make_failed_module: HashSet, - entry_module_identifiers: IdentifierSet, - diagnostics: Vec, - - // TODO move outof context - logger: CompilationLogger, - build_cache_counter: Option, - factorize_cache_counter: Option, - // add_timer: StartTimeAggregate, - // process_deps_timer: StartTimeAggregate, - // factorize_timer: StartTimeAggregate, - // build_timer: StartTimeAggregate, - /// Collecting all module that need to skip in tree-shaking ast modification phase - // bailout_module_identifiers: IdentifierMap, - optimize_analyze_result_map: IdentifierMap, - - file_dependencies: IndexSet>, - context_dependencies: IndexSet>, - missing_dependencies: IndexSet>, - build_dependencies: IndexSet>, -} - -impl MakeTaskContext { - pub fn new(compilation: &Compilation, make_module_graph_partial: ModuleGraphPartial) -> Self { - let logger = compilation.get_logger("rspack.Compilation"); - let mut build_cache_counter = None; - let mut factorize_cache_counter = None; - if !(matches!(compilation.options.cache, CacheOptions::Disabled)) { - build_cache_counter = Some(logger.cache("module build cache")); - factorize_cache_counter = Some(logger.cache("module factorize cache")); - } - - Self { - plugin_driver: compilation.plugin_driver.clone(), - compiler_options: compilation.options.clone(), - resolver_factory: compilation.resolver_factory.clone(), - loader_resolver_factory: compilation.loader_resolver_factory.clone(), - cache: compilation.cache.clone(), - dependency_factories: compilation.dependency_factories.clone(), - - module_graph_partial: make_module_graph_partial, - make_failed_dependencies: Default::default(), - make_failed_module: Default::default(), - entry_module_identifiers: Default::default(), - diagnostics: Default::default(), - optimize_analyze_result_map: Default::default(), - - // TODO use timer in tasks - logger, - build_cache_counter, - factorize_cache_counter, - // add_timer: logger.time_aggregate("module add task"), - // process_deps_timer: logger.time_aggregate("module process dependencies task"), - // factorize_timer: logger.time_aggregate("module factorize task"), - // build_timer: logger.time_aggregate("module build task"), - file_dependencies: Default::default(), - context_dependencies: Default::default(), - missing_dependencies: Default::default(), - build_dependencies: Default::default(), - } - } - - pub fn emit_data_to_compilation(mut self, compilation: &mut Compilation) { - if let Some(counter) = self.build_cache_counter { - self.logger.cache_end(counter); - } - if let Some(counter) = self.factorize_cache_counter { - self.logger.cache_end(counter); - } - - compilation - .make_failed_dependencies - .extend(self.make_failed_dependencies.drain()); - compilation - .make_failed_module - .extend(self.make_failed_module.drain()); - compilation.file_dependencies.extend(self.file_dependencies); - compilation - .context_dependencies - .extend(self.context_dependencies); - compilation - .missing_dependencies - .extend(self.missing_dependencies); - compilation - .build_dependencies - .extend(self.build_dependencies); - - compilation.push_batch_diagnostic(self.diagnostics); - compilation - .entry_module_identifiers - .extend(self.entry_module_identifiers); - compilation.swap_make_module_graph(&mut self.module_graph_partial); - compilation.optimize_analyze_result_map = self.optimize_analyze_result_map; - } - - // TODO use module graph with make artifact - pub fn get_module_graph(module_graph_partial: &mut ModuleGraphPartial) -> ModuleGraph { - ModuleGraph::new(vec![], Some(module_graph_partial)) - } -} diff --git a/crates/rspack_core/src/module_graph/mod.rs b/crates/rspack_core/src/module_graph/mod.rs index fea1d766764..1c7d1b952bf 100644 --- a/crates/rspack_core/src/module_graph/mod.rs +++ b/crates/rspack_core/src/module_graph/mod.rs @@ -1,7 +1,6 @@ use std::borrow::Cow; use std::collections::hash_map::Entry; -use itertools::Itertools; use rspack_error::Result; use rspack_hash::RspackHashDigest; use rspack_identifier::IdentifierMap; @@ -923,64 +922,6 @@ impl<'a> ModuleGraph<'a> { .map(|m| &*m.__deprecated_all_dependencies) } - /// # Deprecated!!! - /// # Don't use this anymore!!! - /// A module is a DependenciesBlock, which means it has some Dependencies and some AsyncDependenciesBlocks - /// a static import is a Dependency, but a dynamic import is a AsyncDependenciesBlock - /// AsyncDependenciesBlock means it is a code-splitting point, and will create a ChunkGroup in code-splitting - /// and AsyncDependenciesBlock also is DependenciesBlock, so it can has some Dependencies and some AsyncDependenciesBlocks - /// so if you want get a module's dependencies and its blocks' dependencies (all dependencies) - /// just use module.get_dependencies() and module.get_blocks().map(|b| b.get_dependencyes()) - /// you don't need this one - pub(crate) fn get_module_all_depended_modules( - &self, - module_identifier: &ModuleIdentifier, - ) -> Option> { - self - .module_graph_module_by_identifier(module_identifier) - .map(|m| { - m.__deprecated_all_dependencies - .iter() - .filter_map(|id| self.module_identifier_by_dependency_id(id)) - .collect() - }) - } - - pub(crate) fn get_module_dependencies_modules_and_blocks( - &self, - module_identifier: &ModuleIdentifier, - ) -> (Vec, &[AsyncDependenciesBlockIdentifier]) { - let Some(m) = self.module_by_identifier(module_identifier) else { - unreachable!("cannot find the module correspanding to {module_identifier}"); - }; - let mut deps = m - .get_dependencies() - .iter() - .filter_map(|id| self.dependency_by_id(id)) - .filter(|dep| dep.as_module_dependency().is_some()) - .collect::>(); - - // sort by span, so user change import order can recalculate chunk graph - deps.sort_by(|a, b| { - if let (Some(span_a), Some(span_b)) = (a.span(), b.span()) { - span_a.cmp(&span_b) - } else if let (Some(a), Some(b)) = (a.source_order(), b.source_order()) { - a.cmp(&b) - } else { - a.id().cmp(b.id()) - } - }); - - let modules = deps - .into_iter() - .filter_map(|dep| self.module_identifier_by_dependency_id(dep.id())) - .dedup_by(|a, b| a.as_str() == b.as_str()) - .copied() - .collect(); - let blocks = m.get_blocks(); - (modules, blocks) - } - pub fn parent_module_by_dependency_id( &self, dependency_id: &DependencyId,