use std::{borrow::Cow, path::Path};
use rolldown_common::{
ModuleType, NormalizedBundlerOptions, ResolvedId, SourcemapChainElement, StrOrBytes,
side_effects::HookSideEffects,
};
use rolldown_fs::FileSystem;
use rolldown_plugin::{HookLoadArgs, PluginDriver};
use rustc_hash::FxHashMap;
use sugar_path::SugarPath;
#[expect(clippy::too_many_arguments)]
pub async fn load_source<Fs: FileSystem + 'static>(
plugin_driver: &PluginDriver,
resolved_id: &ResolvedId,
fs: Fs,
sourcemap_chain: &mut Vec<SourcemapChainElement>,
side_effects: &mut Option<HookSideEffects>,
options: &NormalizedBundlerOptions,
asserted_module_type: Option<&ModuleType>,
is_read_from_disk: &mut bool,
) -> anyhow::Result<(StrOrBytes, ModuleType)> {
let (maybe_source, maybe_module_type) =
match plugin_driver.load(&HookLoadArgs { id: &resolved_id.id }).await? {
Some(load_hook_output) => {
sourcemap_chain.extend(load_hook_output.map.map(SourcemapChainElement::Load));
if let Some(v) = load_hook_output.side_effects {
*side_effects = Some(v);
}
(Some(load_hook_output.code.to_string()), load_hook_output.module_type)
}
_ => {
if resolved_id.ignored {
(Some(String::new()), Some(ModuleType::Empty))
} else {
(None, None)
}
}
};
if let Some(asserted) = asserted_module_type {
let is_type_conflicted = match &maybe_module_type {
None => false,
Some(user_specified_type) if user_specified_type == asserted => false,
_ => true,
};
if is_type_conflicted {
}
}
if maybe_source.is_some() {
*is_read_from_disk = false;
}
match (maybe_source, maybe_module_type) {
(Some(source), Some(module_type)) => Ok((source.into(), module_type)),
(source, None) => {
let guessed = get_module_loader_from_file_extension(&resolved_id.id, &options.module_types);
match (source, guessed) {
(None, None) => {
Ok((
StrOrBytes::Str({
#[cfg(not(target_family = "wasm"))]
{
let id = resolved_id.id.clone();
tokio::runtime::Handle::current()
.spawn_blocking(move || fs.read_to_string(id.as_path()))
.await??
}
#[cfg(target_family = "wasm")]
{
fs.read_to_string(resolved_id.id.as_path())?
}
}),
ModuleType::Js,
))
}
(source, Some(guessed)) => match &guessed {
ModuleType::Base64 | ModuleType::Binary | ModuleType::Dataurl | ModuleType::Asset => {
Ok((
StrOrBytes::Bytes({
match source {
Some(s) => s.into_bytes(),
None => {
if cfg!(target_family = "wasm") {
fs.read(resolved_id.id.as_path())?
} else {
let id = resolved_id.id.clone();
tokio::runtime::Handle::current()
.spawn_blocking(move || fs.read(id.as_path()))
.await??
}
}
}
}),
guessed,
))
}
ModuleType::Js
| ModuleType::Jsx
| ModuleType::Ts
| ModuleType::Tsx
| ModuleType::Json
| ModuleType::Text
| ModuleType::Empty
| ModuleType::Css
| ModuleType::Custom(_) => Ok((
StrOrBytes::Str({
if let Some(s) = source {
s
} else {
#[cfg(not(target_family = "wasm"))]
{
let id = resolved_id.id.clone();
tokio::runtime::Handle::current()
.spawn_blocking(move || fs.read_to_string(id.as_path()))
.await??
}
#[cfg(target_family = "wasm")]
{
fs.read_to_string(resolved_id.id.as_path())?
}
}
}),
guessed,
)),
},
(Some(source), None) => Ok((StrOrBytes::Str(source), ModuleType::Js)),
}
}
(None, Some(ty)) => {
if asserted_module_type.is_some() {
Ok((read_file_by_module_type(resolved_id.id.as_path(), &ty, fs).await?, ty))
} else {
unreachable!("Invalid state")
}
}
}
}
fn get_module_loader_from_file_extension<S: AsRef<str>>(
id: S,
module_types: &FxHashMap<Cow<'static, str>, ModuleType>,
) -> Option<ModuleType> {
let id = id.as_ref();
for i in memchr::memchr_iter(b'.', id.as_bytes()) {
if let Some(ty) = module_types.get(&id[i + 1..]) {
return Some(ty.clone());
}
}
None
}
async fn read_file_by_module_type<Fs: FileSystem + 'static>(
path: impl AsRef<Path>,
ty: &ModuleType,
fs: Fs,
) -> anyhow::Result<StrOrBytes> {
let path = path.as_ref().to_path_buf();
match ty {
ModuleType::Js
| ModuleType::Jsx
| ModuleType::Ts
| ModuleType::Tsx
| ModuleType::Json
| ModuleType::Css
| ModuleType::Empty
| ModuleType::Custom(_)
| ModuleType::Text => Ok(StrOrBytes::Str({
if cfg!(target_family = "wasm") {
fs.read_to_string(&path)?
} else {
tokio::runtime::Handle::current().spawn_blocking(move || fs.read_to_string(&path)).await??
}
})),
ModuleType::Base64 | ModuleType::Binary | ModuleType::Dataurl | ModuleType::Asset => {
Ok(StrOrBytes::Bytes({
if cfg!(target_family = "wasm") {
fs.read(&path)?
} else {
tokio::runtime::Handle::current().spawn_blocking(move || fs.read(&path)).await??
}
}))
}
}
}