use oxc::ast::{
AstKind,
ast::{self, Argument, IdentifierReference},
};
use oxc_str::CompactStr;
use rolldown_common::{ImportRecordIdx, dynamic_import_usage::DynamicImportExportsUsage};
use rustc_hash::FxHashSet;
use super::AstScanner;
impl<'me, 'ast: 'me> AstScanner<'me, 'ast> {
pub fn update_dynamic_import_binding_usage_info(
&mut self,
ident: &IdentifierReference,
) -> Option<()> {
if !self
.dynamic_import_usage_info
.dynamic_import_binding_reference_id
.contains(&ident.reference_id())
{
return None;
}
let reference = self.result.symbol_ref_db.scoping().get_reference(ident.reference_id());
let symbol_id = reference.symbol_id().expect("should have symbol id");
let parent = self.visit_path.last()?;
let partial_name =
parent.as_member_expression_kind().and_then(|expr| expr.static_property_name());
let rec_idx = *self
.dynamic_import_usage_info
.dynamic_import_binding_to_import_record_idx
.get(&symbol_id)?;
let usage = match partial_name {
Some(name) => DynamicImportExportsUsage::Single(name.into()),
None => DynamicImportExportsUsage::Complete,
};
match self.dynamic_import_usage_info.dynamic_import_exports_usage.entry(rec_idx) {
std::collections::hash_map::Entry::Occupied(mut occ) => occ.get_mut().merge(usage),
std::collections::hash_map::Entry::Vacant(vac) => {
vac.insert(usage);
}
}
None
}
pub fn init_dynamic_import_binding_usage_info(&mut self, import_record_idx: ImportRecordIdx) {
let Some(last_visit_path) = self.visit_path.last() else {
return;
};
let init_set = match last_visit_path {
AstKind::StaticMemberExpression(expr) if expr.property.name == "then" => {
self.init_dynamic_import_usage_with_static_member_expr(import_record_idx)
}
AstKind::AwaitExpression(_) => {
self.extract_init_set_from_await_expr_ancestor(import_record_idx)
}
AstKind::ExpressionStatement(_) if self.is_root_scope() => Some(FxHashSet::default()),
_ => None,
};
let usage = match init_set {
Some(init_set) => DynamicImportExportsUsage::Partial(init_set),
None => DynamicImportExportsUsage::Complete,
};
self.dynamic_import_usage_info.dynamic_import_exports_usage.insert(import_record_idx, usage);
}
fn extract_init_set_from_await_expr_ancestor(
&mut self,
import_record_idx: ImportRecordIdx,
) -> Option<FxHashSet<CompactStr>> {
let ast_after_remove_paren_idx = self
.visit_path
.iter()
.skip(1)
.rposition(|kind| !matches!(kind, AstKind::ParenthesizedExpression(_)))?;
match self.visit_path[ast_after_remove_paren_idx] {
AstKind::VariableDeclarator(var_decl) => {
let is_exported = matches!(
self.visit_path.get(ast_after_remove_paren_idx.saturating_sub(2)),
Some(AstKind::ExportDefaultDeclaration(_) | AstKind::ExportNamedDeclaration(_))
);
self.update_dynamic_import_usage_info_from_binding_pattern(
&var_decl.id,
import_record_idx,
is_exported,
)
}
AstKind::ExpressionStatement(_) => {
match self.visit_path.get(ast_after_remove_paren_idx.saturating_sub(1))? {
AstKind::ReturnStatement(_) => None,
AstKind::FunctionBody(_) => {
match self.visit_path.get(ast_after_remove_paren_idx.saturating_sub(2))? {
AstKind::ArrowFunctionExpression(expr) if expr.expression => None,
_ => Some(FxHashSet::default()),
}
}
_ => Some(FxHashSet::default()),
}
}
kind if kind.is_member_expression_kind() => Some(FxHashSet::from_iter([kind
.as_member_expression_kind()
.unwrap()
.static_property_name()?
.into()])),
_ => None,
}
}
fn init_dynamic_import_usage_with_static_member_expr(
&mut self,
import_record_idx: ImportRecordIdx,
) -> Option<FxHashSet<CompactStr>> {
let parent_parent_idx = self.visit_path.len().saturating_sub(2);
let parent_parent = self.visit_path.get(parent_parent_idx)?.as_call_expression()?;
let first_arg = parent_parent.arguments.first()?;
let dynamic_import_binding = match first_arg {
Argument::FunctionExpression(func) => func.params.items.first(),
Argument::ArrowFunctionExpression(func) => func.params.items.first(),
_ => return None,
};
let Some(dynamic_import_binding) = dynamic_import_binding else {
return Some(FxHashSet::default());
};
self.update_dynamic_import_usage_info_from_binding_pattern(
&dynamic_import_binding.pattern,
import_record_idx,
false,
)
}
fn update_dynamic_import_usage_info_from_binding_pattern(
&mut self,
binding_pattern: &ast::BindingPattern<'_>,
import_record_idx: ImportRecordIdx,
is_exported: bool,
) -> Option<FxHashSet<CompactStr>> {
let symbol_id = match binding_pattern {
ast::BindingPattern::BindingIdentifier(id) => {
if is_exported {
return None;
}
id.symbol_id()
}
ast::BindingPattern::ObjectPattern(obj) => {
let mut set = FxHashSet::default();
for binding in &obj.properties {
let binding_name = match &binding.key {
ast::PropertyKey::StaticIdentifier(id) => id.name.as_str(),
_ => return None,
};
let binding_symbol_id = match &binding.value {
ast::BindingPattern::BindingIdentifier(id) => id.symbol_id(),
_ => {
set.insert(binding_name.into());
continue;
}
};
let is_used = !self
.result
.symbol_ref_db
.scoping()
.get_resolved_reference_ids(binding_symbol_id)
.is_empty();
if is_exported || is_used {
set.insert(binding_name.into());
}
}
if let Some(rest) = &obj.rest {
match &rest.argument {
ast::BindingPattern::BindingIdentifier(id) => {
let symbol_id = id.symbol_id();
self
.dynamic_import_usage_info
.dynamic_import_binding_to_import_record_idx
.insert(symbol_id, import_record_idx);
self
.dynamic_import_usage_info
.dynamic_import_binding_reference_id
.extend(self.result.symbol_ref_db.scoping().get_resolved_reference_ids(symbol_id));
}
_ => unreachable!(),
}
}
return Some(set);
}
ast::BindingPattern::ArrayPattern(_) | ast::BindingPattern::AssignmentPattern(_) => {
return None;
}
};
self
.dynamic_import_usage_info
.dynamic_import_binding_to_import_record_idx
.insert(symbol_id, import_record_idx);
self
.dynamic_import_usage_info
.dynamic_import_binding_reference_id
.extend(self.result.symbol_ref_db.scoping().get_resolved_reference_ids(symbol_id));
Some(FxHashSet::default())
}
}