use std::{path::Path, sync::Arc};
use arcstr::ArcStr;
use rolldown_common::{
ExportsKind, ExternalModuleTaskResult, ModuleIdx, ModuleInfo, ModuleLoaderMsg, ResolvedExternal,
ResolvedId,
};
use rolldown_error::BuildResult;
use rolldown_utils::{ecmascript::legitimize_identifier_name, indexmap::FxIndexSet};
use sugar_path::SugarPath;
use rolldown_fs::FileSystem;
use crate::ecmascript::ecma_module_view_factory::normalize_side_effects;
use super::task_context::TaskContext;
#[expect(clippy::rc_buffer)]
pub struct ExternalModuleTask<Fs: FileSystem> {
ctx: Arc<TaskContext<Fs>>,
module_idx: ModuleIdx,
resolved_id: ResolvedId,
user_defined_entries: Arc<Vec<(Option<ArcStr>, ResolvedId)>>,
}
#[expect(clippy::rc_buffer)]
impl<Fs: FileSystem> ExternalModuleTask<Fs> {
pub fn new(
ctx: Arc<TaskContext<Fs>>,
idx: ModuleIdx,
resolved_id: ResolvedId,
user_defined_entries: Arc<Vec<(Option<ArcStr>, ResolvedId)>>,
) -> Self {
Self { ctx, module_idx: idx, resolved_id, user_defined_entries }
}
#[tracing::instrument(name="ExternalModuleTask::run", level = "trace", skip_all, fields(module_id = ?self.resolved_id.id))]
pub async fn run(self) {
if let Err(errs) = self.run_inner().await {
self
.ctx
.tx
.send(ModuleLoaderMsg::BuildErrors(errs.into_vec().into_boxed_slice()))
.expect("ModuleLoader: failed to send external module build errors - main thread terminated while processing errors");
}
}
async fn run_inner(&self) -> BuildResult<()> {
let resolved_id = &self.resolved_id;
let external_module_side_effects =
normalize_side_effects(&self.ctx.options, resolved_id, None, resolved_id.side_effects)
.await?;
let id = resolved_id.id.clone();
self.ctx.plugin_driver.set_module_info(
&id.clone(),
Arc::new(ModuleInfo {
code: None,
id,
is_entry: false,
importers: FxIndexSet::default(),
dynamic_importers: FxIndexSet::default(),
imported_ids: FxIndexSet::default(),
dynamically_imported_ids: FxIndexSet::default(),
exports: vec![],
input_format: ExportsKind::None,
}),
);
let need_renormalize_render_path = !matches!(resolved_id.external, ResolvedExternal::Absolute)
&& Path::new(resolved_id.id.as_str()).is_absolute();
let file_name: ArcStr = if need_renormalize_render_path {
let entries_common_dir = commondir::CommonDir::try_new(
self.user_defined_entries.iter().map(|(_, resolved_id)| resolved_id.id.as_str()),
)
.expect("should have common dir for entries");
let relative_path =
Path::new(resolved_id.id.as_str()).relative(entries_common_dir.common_root());
relative_path.to_slash_lossy().into()
} else {
resolved_id.id.as_arc_str().clone()
};
let identifier_name: ArcStr = if need_renormalize_render_path {
Path::new(resolved_id.id.as_str())
.relative(&self.ctx.options.cwd)
.normalize()
.to_slash_lossy()
.into()
} else {
resolved_id.id.as_arc_str().clone()
};
let legitimized_identifier_name = legitimize_identifier_name(&identifier_name);
let msg = ModuleLoaderMsg::ExternalModuleDone(Box::new(ExternalModuleTaskResult {
idx: self.module_idx,
id: resolved_id.id.clone(),
name: file_name,
identifier_name: legitimized_identifier_name.into(),
side_effects: external_module_side_effects,
need_renormalize_render_path,
}));
self.ctx.tx.send(msg).expect(
"ModuleLoader channel closed while sending external module completion - main thread terminated unexpectedly"
);
Ok(())
}
}