use itertools::Itertools;
use rolldown_common::{ExternalModule, NormalModule};
use rolldown_sourcemap::SourceJoiner;
use crate::{
ecmascript::ecma_generator::{RenderedModuleSource, RenderedModuleSources},
types::generator::GenerateContext,
utils::external_import_interop::external_import_needs_interop,
};
pub mod namespace;
#[derive(Clone, Copy)]
pub enum ExternalImportKind<'a> {
Used(&'a ExternalModule),
SideEffectOnly(&'a ExternalModule),
}
impl<'a> ExternalImportKind<'a> {
pub fn module(&self) -> &'a ExternalModule {
match self {
ExternalImportKind::Used(m) | ExternalImportKind::SideEffectOnly(m) => m,
}
}
pub fn is_used(&self) -> bool {
matches!(self, ExternalImportKind::Used(_))
}
}
pub fn render_factory_parameters(
ctx: &GenerateContext<'_>,
externals: &[&ExternalModule],
has_exports: bool,
) -> String {
let mut parameters: Vec<&str> = if has_exports { vec!["exports"] } else { vec![] };
externals.iter().for_each(|external| {
let symbol_name = ctx
.link_output
.symbol_db
.canonical_name_for_or_original(external.namespace_ref, &ctx.chunk.canonical_names);
parameters.push(symbol_name);
});
parameters.join(", ")
}
pub fn render_chunk_external_imports<'a>(
ctx: &'a GenerateContext<'_>,
) -> (String, Vec<ExternalImportKind<'a>>) {
let mut import_code = String::new();
let externals = ctx
.chunk
.direct_imports_from_external_modules
.iter()
.filter_map(|(importee_id, named_imports)| {
let importee = ctx.link_output.module_table[*importee_id]
.as_external()
.expect("Should be external module here");
let external_module_symbol_name = ctx
.link_output
.symbol_db
.canonical_name_for_or_original(importee.namespace_ref, &ctx.chunk.canonical_names);
if ctx.link_output.used_symbol_refs.contains(&importee.namespace_ref) {
let needs_interop = external_import_needs_interop(named_imports);
if needs_interop {
let to_esm_fn_name = ctx.link_output.symbol_db.canonical_name_for_or_original(
ctx.link_output.runtime.resolve_symbol("__toESM"),
&ctx.chunk.canonical_names,
);
let canonical_ref = ctx.link_output.symbol_db.canonical_ref_for(importee.namespace_ref);
if let Some(node_mode_name) = ctx.chunk.node_mode_external_ns_names.get(&canonical_ref) {
import_code.push_str("let ");
import_code.push_str(node_mode_name);
import_code.push_str(" = ");
import_code.push_str(to_esm_fn_name);
import_code.push('(');
import_code.push_str(external_module_symbol_name);
import_code.push_str(", 1);\n");
import_code.push_str(external_module_symbol_name);
import_code.push_str(" = ");
import_code.push_str(to_esm_fn_name);
import_code.push('(');
import_code.push_str(external_module_symbol_name);
import_code.push_str(");\n");
} else {
let is_node_esm = named_imports.iter().any(|(importer_idx, _)| {
ctx.link_output.module_table[*importer_idx]
.as_normal()
.is_some_and(NormalModule::should_consider_node_esm_spec_for_static_import)
});
import_code.push_str(external_module_symbol_name);
import_code.push_str(" = ");
import_code.push_str(to_esm_fn_name);
import_code.push('(');
import_code.push_str(external_module_symbol_name);
if is_node_esm {
import_code.push_str(", 1");
}
import_code.push_str(");\n");
}
}
Some(ExternalImportKind::Used(importee))
} else if importee.side_effects.has_side_effects() {
Some(ExternalImportKind::SideEffectOnly(importee))
} else {
None
}
})
.collect_vec();
(import_code, externals)
}
pub fn render_modules_with_peek_runtime_module_at_first<'a>(
ctx: &GenerateContext<'_>,
source_joiner: &mut SourceJoiner<'a>,
module_sources: &'a RenderedModuleSources,
import_code: String,
) {
let mut module_sources_peekable = module_sources.iter().peekable();
match module_sources_peekable.peek() {
Some(RenderedModuleSource { module_idx, .. })
if *module_idx == ctx.link_output.runtime.id() =>
{
if let RenderedModuleSource { sources: Some(emitted_sources), .. } =
module_sources_peekable.next().expect("Must have module")
{
for source in emitted_sources.iter() {
source_joiner.append_source(source);
}
}
}
_ => {}
}
source_joiner.append_source(import_code);
module_sources_peekable.for_each(
|RenderedModuleSource { sources: module_render_output, .. }| {
if let Some(emitted_sources) = module_render_output {
for source in emitted_sources.as_ref() {
source_joiner.append_source(source);
}
}
},
);
}
pub fn render_chunk_directives<'a, T: Iterator<Item = &'a &'a str>>(directives: T) -> String {
let mut ret = String::new();
for d in directives {
ret.push_str(d);
if (d).ends_with(';') {
ret.push('\n');
} else {
ret.push_str(";\n");
}
}
ret
}
pub fn is_use_strict_directive(s: &str) -> bool {
s.trim_start_matches(['\'', '"']).trim_end_matches(['\'', '"', ';']) == "use strict"
}