use oxc_index::IndexVec;
use oxc_str::CompactStr;
use rolldown_common::{
Chunk, ChunkIdx, InstantiatedChunk, ModuleRenderOutput, NormalizedBundlerOptions, OutputExports,
PathsOutputOption, SymbolRef,
};
use rolldown_error::{BuildDiagnostic, BuildResult};
use rolldown_plugin::SharedPluginDriver;
use rolldown_utils::{ecmascript::property_access_str, indexmap::FxIndexMap};
use rustc_hash::FxHashMap;
use crate::{chunk_graph::ChunkGraph, stages::link_stage::LinkStageOutput};
pub struct GenerateContext<'a> {
pub chunk_idx: ChunkIdx,
pub chunk: &'a Chunk,
pub options: &'a NormalizedBundlerOptions,
pub link_output: &'a LinkStageOutput,
pub chunk_graph: &'a ChunkGraph,
pub plugin_driver: &'a SharedPluginDriver,
pub module_id_to_codegen_ret: Vec<Option<ModuleRenderOutput>>,
pub render_export_items_index_vec: &'a IndexVec<ChunkIdx, FxIndexMap<SymbolRef, Vec<CompactStr>>>,
pub resolved_paths: Option<&'a PathsOutputOption>,
}
impl GenerateContext<'_> {
pub fn finalized_string_pattern_for_symbol_ref(
&self,
symbol_ref: SymbolRef,
cur_chunk_idx: ChunkIdx,
canonical_names: &FxHashMap<SymbolRef, CompactStr>,
) -> String {
let symbol_db = &self.link_output.symbol_db;
if !symbol_ref.is_declared_in_root_scope(symbol_db) {
return self.canonical_name_for(canonical_names, symbol_ref).to_string();
}
let canonical_ref = symbol_db.canonical_ref_for(symbol_ref);
let canonical_symbol = symbol_db.get(canonical_ref);
let namespace_alias = &canonical_symbol.namespace_alias;
if let Some(ns_alias) = namespace_alias {
let canonical_ns_name =
symbol_db.canonical_name_for_or_original(ns_alias.namespace_ref, canonical_names);
let property_name = &ns_alias.property_name;
return property_access_str(canonical_ns_name, property_name);
}
if self.link_output.module_table[canonical_ref.owner].is_external() {
let namespace = symbol_db.canonical_name_for_or_original(canonical_ref, canonical_names);
return namespace.to_string();
}
match self.options.format {
rolldown_common::OutputFormat::Cjs => {
let chunk_idx_of_canonical_symbol = canonical_symbol.chunk_idx.unwrap_or_else(|| {
let symbol_name = canonical_ref.name(symbol_db);
panic!("{canonical_ref:?} {symbol_name:?} is not in any chunk, which isn't unexpected");
});
let is_symbol_in_other_chunk = cur_chunk_idx != chunk_idx_of_canonical_symbol;
if is_symbol_in_other_chunk {
let require_binding = &self.chunk_graph.chunk_table[cur_chunk_idx]
.require_binding_names_for_other_chunks[&chunk_idx_of_canonical_symbol];
let exported_name = &self.chunk_graph.chunk_table[chunk_idx_of_canonical_symbol]
.exports_to_other_chunks[&canonical_ref][0];
if exported_name == "default" {
match self.options.exports {
OutputExports::Auto => require_binding.clone(),
OutputExports::Default | OutputExports::Named => {
rolldown_utils::ecmascript::property_access_str(require_binding, exported_name)
}
OutputExports::None => unreachable!(),
}
} else {
rolldown_utils::ecmascript::property_access_str(require_binding, exported_name)
}
} else {
self.canonical_name_for(canonical_names, canonical_ref).to_string()
}
}
_ => self.canonical_name_for(canonical_names, canonical_ref).to_string(),
}
}
fn canonical_name_for<'name>(
&'name self,
canonical_names: &'name FxHashMap<SymbolRef, CompactStr>,
symbol: SymbolRef,
) -> &'name str {
let symbol_db = &self.link_output.symbol_db;
symbol_db.canonical_name_for_or_original(symbol, canonical_names)
}
}
#[derive(Default)]
pub struct GenerateOutput {
pub chunks: Vec<InstantiatedChunk>,
pub warnings: Vec<BuildDiagnostic>,
}
pub trait Generator {
async fn instantiate_chunk(
ctx: &mut GenerateContext,
) -> anyhow::Result<BuildResult<GenerateOutput>>;
}