use std::borrow::Cow;
use arcstr::ArcStr;
use rolldown_common::ModuleType;
use rolldown_plugin::{
HookLoadArgs, HookLoadOutput, HookLoadReturn, HookResolveIdArgs, HookResolveIdOutput,
HookResolveIdReturn, HookUsage, Plugin, PluginContext, PluginHookMeta, PluginOrder,
SharedLoadPluginContext,
};
use rolldown_utils::{
dashmap::FxDashMap,
dataurl::{is_data_url, parse_data_url},
xxhash::xxhash_base64_url,
};
#[derive(Debug)]
pub struct ResolvedDataUrl {
pub data: ArcStr,
pub module_type: ModuleType,
}
#[derive(Debug, Default)]
pub struct DataUrlPlugin {
resolved_data_url: FxDashMap<ArcStr, ResolvedDataUrl>,
}
impl Plugin for DataUrlPlugin {
fn name(&self) -> Cow<'static, str> {
Cow::Borrowed("builtin:data-url")
}
fn register_hook_usage(&self) -> HookUsage {
HookUsage::ResolveId | HookUsage::Load
}
fn resolve_id_meta(&self) -> Option<PluginHookMeta> {
Some(PluginHookMeta { order: Some(PluginOrder::PinPost) })
}
async fn resolve_id(
&self,
_ctx: &PluginContext,
args: &HookResolveIdArgs<'_>,
) -> HookResolveIdReturn {
if is_data_url(args.specifier) {
let Some(parsed) = parse_data_url(args.specifier) else {
return Ok(None);
};
let module_type = match parsed.mime {
"text/css" => ModuleType::Css,
"text/javascript" => ModuleType::Js,
"application/json" => ModuleType::Json,
_ => {
return Ok(None);
}
};
let data = if parsed.is_base64 {
let data = base64_simd::STANDARD.decode_to_vec(parsed.data)?;
simdutf8::basic::from_utf8(&data)?;
unsafe { String::from_utf8_unchecked(data) }.into()
} else {
urlencoding::decode(parsed.data)?.as_ref().into()
};
let id: ArcStr =
format!("\0rolldown/data-url:{}", xxhash_base64_url(args.specifier.as_bytes())).into();
self.resolved_data_url.insert(id.clone(), ResolvedDataUrl { data, module_type });
return Ok(Some(HookResolveIdOutput { id, ..Default::default() }));
}
Ok(None)
}
fn load_meta(&self) -> Option<PluginHookMeta> {
Some(PluginHookMeta { order: Some(PluginOrder::Pre) })
}
async fn load(&self, _ctx: SharedLoadPluginContext, args: &HookLoadArgs<'_>) -> HookLoadReturn {
let Some(resolved) = self.resolved_data_url.get(args.id) else {
return Ok(None);
};
Ok(Some(HookLoadOutput {
code: resolved.data.clone(),
module_type: Some(resolved.module_type.clone()),
..Default::default()
}))
}
}