use std::hash::BuildHasherDefault;
use rspack_collections::{IdentifierHasher, IdentifierSet};
use rspack_error::Diagnostic;
use rustc_hash::FxHashSet;
use crate::{
ArtifactExt, BuildDependency, DependencyId, FactorizeInfo, ModuleGraph, ModuleIdentifier,
SideEffectsStateArtifact,
compilation::build_module_graph::ModuleToLazyMake,
incremental::IncrementalPasses,
incremental_info::IncrementalInfo,
utils::{FileCounter, ResourceId},
};
#[derive(Debug)]
pub struct BuildModuleGraphArtifact {
pub affected_modules: IncrementalInfo<ModuleIdentifier, BuildHasherDefault<IdentifierHasher>>,
pub affected_dependencies: IncrementalInfo<DependencyId>,
pub issuer_update_modules: IdentifierSet,
pub module_graph: ModuleGraph,
pub side_effects_state_artifact: SideEffectsStateArtifact,
pub module_to_lazy_make: ModuleToLazyMake,
pub make_failed_module: IdentifierSet,
pub make_failed_dependencies: FxHashSet<DependencyId>,
pub entry_dependencies: FxHashSet<DependencyId>,
pub file_dependencies: FileCounter,
pub context_dependencies: FileCounter,
pub missing_dependencies: FileCounter,
pub build_dependencies: FileCounter,
}
impl BuildModuleGraphArtifact {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
affected_modules: Default::default(),
affected_dependencies: Default::default(),
issuer_update_modules: Default::default(),
module_graph: Default::default(),
side_effects_state_artifact: Default::default(),
module_to_lazy_make: Default::default(),
make_failed_module: Default::default(),
make_failed_dependencies: Default::default(),
entry_dependencies: Default::default(),
file_dependencies: Default::default(),
context_dependencies: Default::default(),
missing_dependencies: Default::default(),
build_dependencies: Default::default(),
}
}
pub fn get_module_graph(&self) -> &ModuleGraph {
&self.module_graph
}
pub fn get_module_graph_mut(&mut self) -> &mut ModuleGraph {
&mut self.module_graph
}
pub fn steal_side_effects_state_artifact(&mut self) -> SideEffectsStateArtifact {
std::mem::take(&mut self.side_effects_state_artifact)
}
pub fn set_side_effects_state_artifact(
&mut self,
side_effects_state_artifact: SideEffectsStateArtifact,
) {
self.side_effects_state_artifact = side_effects_state_artifact;
}
pub fn revoke_module(&mut self, module_identifier: &ModuleIdentifier) -> Vec<BuildDependency> {
let mg = &mut self.module_graph;
let module = mg
.module_by_identifier(module_identifier)
.expect("should have module");
let build_info = module.build_info();
let resource_id = ResourceId::from(module_identifier);
self
.file_dependencies
.remove_files(&resource_id, &build_info.file_dependencies);
self
.context_dependencies
.remove_files(&resource_id, &build_info.context_dependencies);
self
.missing_dependencies
.remove_files(&resource_id, &build_info.missing_dependencies);
self
.build_dependencies
.remove_files(&resource_id, &build_info.build_dependencies);
self.make_failed_module.remove(module_identifier);
let mgm = mg
.module_graph_module_by_identifier(module_identifier)
.expect("should have mgm");
let dep_ids = mgm
.all_dependencies()
.iter()
.copied()
.chain(mgm.incoming_connections().clone())
.collect::<Vec<_>>();
for dep_id in dep_ids {
self.make_failed_dependencies.remove(&dep_id);
let dep = mg.dependency_by_id_mut(&dep_id);
if let Some(info) = FactorizeInfo::revoke(dep) {
let resource_id = ResourceId::from(dep_id);
self
.file_dependencies
.remove_files(&resource_id, info.file_dependencies());
self
.context_dependencies
.remove_files(&resource_id, info.context_dependencies());
self
.missing_dependencies
.remove_files(&resource_id, info.missing_dependencies());
}
self.affected_dependencies.mark_as_remove(&dep_id);
}
self.affected_modules.mark_as_remove(module_identifier);
self.issuer_update_modules.remove(module_identifier);
mg.revoke_module(module_identifier)
}
pub fn revoke_dependency(&mut self, dep_id: &DependencyId, force: bool) -> Vec<BuildDependency> {
self.make_failed_dependencies.remove(dep_id);
let mg = &mut self.module_graph;
let revoke_dep_ids =
if let Some(factorize_info) = FactorizeInfo::revoke(mg.dependency_by_id_mut(dep_id)) {
let resource_id = ResourceId::from(dep_id);
self
.file_dependencies
.remove_files(&resource_id, factorize_info.file_dependencies());
self
.context_dependencies
.remove_files(&resource_id, factorize_info.context_dependencies());
self
.missing_dependencies
.remove_files(&resource_id, factorize_info.missing_dependencies());
factorize_info.related_dep_ids().to_vec()
} else {
vec![*dep_id]
};
revoke_dep_ids
.iter()
.filter_map(|dep_id| {
self.affected_dependencies.mark_as_remove(dep_id);
mg.revoke_dependency(dep_id, force)
})
.collect()
}
pub fn diagnostics(&self) -> Vec<Diagnostic> {
let mg = self.get_module_graph();
let module_diagnostics = self
.make_failed_module
.iter()
.flat_map(|module_identifier| {
let m = mg
.module_by_identifier(module_identifier)
.expect("should have module");
m.diagnostics()
.iter()
.cloned()
.map(|mut d| {
d.module_identifier = Some(*module_identifier);
d
})
.collect::<Vec<_>>()
});
let dep_diagnostics = self.make_failed_dependencies.iter().flat_map(|dep_id| {
let dep = mg.dependency_by_id(dep_id);
let origin_module_identifier = mg.get_parent_module(dep_id);
FactorizeInfo::get_from(dep)
.expect("should have factorize info")
.diagnostics()
.iter()
.cloned()
.map(|mut d| {
d.module_identifier = origin_module_identifier.copied();
d
})
.collect::<Vec<_>>()
});
module_diagnostics.chain(dep_diagnostics).collect()
}
pub fn reset_temporary_data(&mut self) {
self.affected_modules.reset();
self.affected_dependencies.reset();
self.side_effects_state_artifact = Default::default();
self.file_dependencies.reset_incremental_info();
self.context_dependencies.reset_incremental_info();
self.missing_dependencies.reset_incremental_info();
self.build_dependencies.reset_incremental_info();
}
pub fn built_modules(&self) -> impl Iterator<Item = &ModuleIdentifier> {
self.affected_modules.active()
}
pub fn revoked_modules(&self) -> impl Iterator<Item = &ModuleIdentifier> {
self.affected_modules.dirty()
}
}
impl ArtifactExt for BuildModuleGraphArtifact {
const PASS: IncrementalPasses = IncrementalPasses::BUILD_MODULE_GRAPH;
fn recover(incremental: &crate::incremental::Incremental, new: &mut Self, old: &mut Self) {
if incremental.mutations_readable(Self::PASS) {
std::mem::swap(new, old);
new.get_module_graph_mut().reset();
new.side_effects_state_artifact = Default::default();
}
}
}