use crate::stages::link_stage::{ModuleInclusionVec, ModuleNamespaceReasonVec, StmtInclusionVec};
use oxc_index::IndexVec;
use oxc_str::CompactStr;
use rolldown_common::{
ConcatenateWrappedModuleKind, EntryPointKind, ImportRecordIdx, MemberExprRefResolutionMap,
ModuleIdx, ModuleNamespaceIncludedReason, ResolvedExport, RuntimeHelper, StmtInfoIdx, SymbolRef,
WrapKind, dynamic_import_usage::DynamicImportExportsUsage,
};
use rolldown_utils::IndexBitSet;
use rolldown_utils::indexmap::{FxIndexMap, FxIndexSet};
use rustc_hash::{FxHashMap, FxHashSet};
#[derive(Debug, Default)]
#[expect(clippy::struct_excessive_bools)]
pub struct LinkingMetadata {
pub wrapper_ref: Option<SymbolRef>,
pub wrapper_stmt_info: Option<StmtInfoIdx>,
original_wrap_kind: WrapKind,
wrap_kind: WrapKind,
pub resolved_exports: FxHashMap<CompactStr, ResolvedExport>,
pub sorted_and_non_ambiguous_resolved_exports: FxIndexMap<CompactStr, bool>,
pub has_dynamic_exports: bool,
pub shimmed_missing_exports: FxHashMap<CompactStr, SymbolRef>,
pub required_by_other_module: bool,
pub referenced_symbols_by_entry_point_chunk: Vec<(SymbolRef, bool)>,
pub dependencies: FxIndexSet<ModuleIdx>,
pub resolved_member_expr_refs: MemberExprRefResolutionMap,
pub star_exports_from_external_modules: Vec<ImportRecordIdx>,
pub is_tla_or_contains_tla_dependency: bool,
pub concatenated_wrapped_module_kind: ConcatenateWrappedModuleKind,
pub named_import_to_cjs_module: FxHashMap<SymbolRef, ModuleIdx>,
pub import_record_ns_to_cjs_module: FxHashMap<SymbolRef, ModuleIdx>,
pub included_commonjs_export_symbol: FxHashSet<SymbolRef>,
pub depended_runtime_helper: RuntimeHelper,
pub has_side_effectful_runtime_dep: bool,
pub module_namespace_included_reason: ModuleNamespaceIncludedReason,
pub stmt_info_included: IndexBitSet<StmtInfoIdx>,
pub is_included: bool,
}
impl LinkingMetadata {
pub fn canonical_exports(
&self,
needs_commonjs_export: bool,
) -> impl Iterator<Item = (&CompactStr, &ResolvedExport)> {
self.sorted_and_non_ambiguous_resolved_exports.iter().filter_map(
move |(name, came_from_cjs)| {
(needs_commonjs_export || !came_from_cjs).then_some((name, &self.resolved_exports[name]))
},
)
}
pub fn is_canonical_exports_empty(&self) -> bool {
self.sorted_and_non_ambiguous_resolved_exports.is_empty()
}
#[inline]
pub fn wrap_kind(&self) -> WrapKind {
self.wrap_kind
}
#[inline]
pub fn original_wrap_kind(&self) -> WrapKind {
self.original_wrap_kind
}
#[inline]
pub fn sync_wrap_kind(&mut self, wrap_kind: WrapKind) {
self.original_wrap_kind = wrap_kind;
self.wrap_kind = wrap_kind;
}
#[inline]
pub fn update_wrap_kind(&mut self, wrap_kind: WrapKind) {
self.wrap_kind = wrap_kind;
}
pub fn referenced_canonical_exports_symbols<'b, 'a: 'b>(
&'b self,
module_idx: ModuleIdx,
entry_point_kind: EntryPointKind,
dynamic_import_exports_usage_map: &'a FxHashMap<ModuleIdx, DynamicImportExportsUsage>,
needs_commonjs_export: bool,
) -> impl Iterator<Item = (&'b CompactStr, &'b ResolvedExport)> + 'b {
let partial_used_exports = match entry_point_kind {
rolldown_common::EntryPointKind::UserDefined
| rolldown_common::EntryPointKind::EmittedUserDefined => None,
rolldown_common::EntryPointKind::DynamicImport => {
dynamic_import_exports_usage_map.get(&module_idx).and_then(|usage| match usage {
DynamicImportExportsUsage::Complete => None,
DynamicImportExportsUsage::Partial(set) => Some(set),
DynamicImportExportsUsage::Single(_) => unreachable!(),
})
}
};
self.canonical_exports(needs_commonjs_export).filter(
move |(name, _)| match partial_used_exports {
Some(set) => set.contains(name.as_str()),
None => true,
},
)
}
}
pub type LinkingMetadataVec = IndexVec<ModuleIdx, LinkingMetadata>;
pub fn linking_metadata_vec_to_included_info(
metas: &mut LinkingMetadataVec,
) -> (StmtInclusionVec, ModuleInclusionVec, ModuleNamespaceReasonVec) {
let stmt_info_included_vec: StmtInclusionVec =
metas.iter_mut().map(|meta| std::mem::take(&mut meta.stmt_info_included)).collect();
let mut module_included_vec: ModuleInclusionVec = IndexBitSet::new(metas.len());
for (idx, meta) in metas.iter_enumerated() {
if meta.is_included {
module_included_vec.set_bit(idx);
}
}
let module_namespace_reason_vec: ModuleNamespaceReasonVec =
metas.iter().map(|meta| meta.module_namespace_included_reason).collect();
(stmt_info_included_vec, module_included_vec, module_namespace_reason_vec)
}
pub fn included_info_to_linking_metadata_vec(
metas: &mut LinkingMetadataVec,
mut stmt_info_included_vec: StmtInclusionVec,
module_included_vec: &ModuleInclusionVec,
module_namespace_reason_vec: &ModuleNamespaceReasonVec,
) {
for (idx, meta) in metas.iter_mut_enumerated() {
meta.stmt_info_included = std::mem::take(&mut stmt_info_included_vec[idx]);
meta.is_included = module_included_vec.has_bit(idx);
meta.module_namespace_included_reason = module_namespace_reason_vec[idx];
}
}