use rspack_cacheable::{cacheable, cacheable_dyn};
use rspack_collections::{IdentifierMap, IdentifierSet};
use rspack_core::{
AsContextDependency, AsModuleDependency, Compilation, ConnectionState, Dependency,
DependencyCodeGeneration, DependencyId, DependencyRange, DependencyTemplate,
DependencyTemplateType, ModuleGraph, ModuleGraphCacheArtifact, ModuleIdentifier,
PrefetchExportsInfoMode, RuntimeCondition, RuntimeSpec, TemplateContext, TemplateReplaceSource,
UsageState, UsedByExports, filter_runtime,
};
use rspack_util::ext::DynHash;
#[cacheable]
#[derive(Debug, Clone)]
pub struct PureExpressionDependency {
pub range: DependencyRange,
used_by_exports: Option<UsedByExports>,
id: DependencyId,
pub module_identifier: ModuleIdentifier,
}
impl PureExpressionDependency {
pub fn new(range: DependencyRange, module_identifier: ModuleIdentifier) -> Self {
Self {
range,
used_by_exports: None,
id: DependencyId::default(),
module_identifier,
}
}
fn get_runtime_condition(
&self,
compilation: &Compilation,
runtime: Option<&RuntimeSpec>,
) -> RuntimeCondition {
match self.used_by_exports {
Some(UsedByExports::Bool(true)) => {
unreachable!()
}
Some(UsedByExports::Bool(false)) => RuntimeCondition::Boolean(false),
Some(UsedByExports::Set(ref set)) => {
let module_graph = compilation.get_module_graph();
let exports_info = module_graph
.get_prefetched_exports_info(&self.module_identifier, PrefetchExportsInfoMode::Default);
filter_runtime(runtime, |cur_runtime| {
set.iter().any(|id| {
exports_info.get_used(std::slice::from_ref(id), cur_runtime) != UsageState::Unused
})
})
}
None => {
unreachable!()
}
}
}
pub fn set_used_by_exports(&mut self, used_by_exports: Option<UsedByExports>) {
self.used_by_exports = used_by_exports;
}
}
#[cacheable_dyn]
impl Dependency for PureExpressionDependency {
fn id(&self) -> &rspack_core::DependencyId {
&self.id
}
fn range(&self) -> Option<DependencyRange> {
Some(self.range)
}
fn get_module_evaluation_side_effects_state(
&self,
_module_graph: &ModuleGraph,
_module_graph_cache: &ModuleGraphCacheArtifact,
_module_chain: &mut IdentifierSet,
_connection_state_cache: &mut IdentifierMap<ConnectionState>,
) -> ConnectionState {
ConnectionState::Active(false)
}
fn could_affect_referencing_module(&self) -> rspack_core::AffectType {
rspack_core::AffectType::False
}
}
impl AsModuleDependency for PureExpressionDependency {}
#[cacheable_dyn]
impl DependencyCodeGeneration for PureExpressionDependency {
fn dependency_template(&self) -> Option<DependencyTemplateType> {
Some(PureExpressionDependencyTemplate::template_type())
}
fn update_hash(
&self,
hasher: &mut dyn std::hash::Hasher,
compilation: &Compilation,
runtime: Option<&RuntimeSpec>,
) {
let runtime_condition = self.get_runtime_condition(compilation, runtime);
runtime_condition.dyn_hash(hasher);
}
}
impl AsContextDependency for PureExpressionDependency {}
#[cacheable]
#[derive(Debug, Clone, Default)]
pub struct PureExpressionDependencyTemplate;
impl PureExpressionDependencyTemplate {
pub fn template_type() -> DependencyTemplateType {
DependencyTemplateType::Custom("PureExpressionDependency")
}
}
impl DependencyTemplate for PureExpressionDependencyTemplate {
fn render(
&self,
dep: &dyn DependencyCodeGeneration,
source: &mut TemplateReplaceSource,
code_generatable_context: &mut TemplateContext,
) {
let dep = dep
.as_any()
.downcast_ref::<PureExpressionDependency>()
.expect("PureExpressionDependencyTemplate should be used for PureExpressionDependency");
let runtime_condition = dep.get_runtime_condition(
code_generatable_context.compilation,
code_generatable_context.runtime,
);
let condition = match &runtime_condition {
rspack_core::RuntimeCondition::Boolean(true) => return,
rspack_core::RuntimeCondition::Boolean(false) => None,
rspack_core::RuntimeCondition::Spec(_spec) => Some(
code_generatable_context
.compilation
.runtime_template
.runtime_condition_expression(
&code_generatable_context.compilation.chunk_graph,
Some(&runtime_condition),
code_generatable_context.runtime,
code_generatable_context.runtime_requirements,
),
),
};
if let Some(condition) = condition {
source.insert(
dep.range.start,
&format!("(/* runtime-dependent pure expression or super */ {condition} ? ("),
None,
);
source.insert(dep.range.end, ") : null)", None);
} else {
source.insert(
dep.range.start,
"(/* unused pure expression or super */ null && (",
None,
);
source.insert(dep.range.end, "))", None);
}
}
}