use std::borrow::Cow;
use arcstr::ArcStr;
use rolldown_common::ModuleType;
use rolldown_plugin::{
HookLoadArgs, HookLoadOutput, HookLoadReturn, HookResolveIdArgs, HookResolveIdOutput,
HookResolveIdReturn, HookUsage, Plugin, PluginContext,
};
use rolldown_utils::{
dashmap::FxDashMap,
dataurl::{is_data_url, parse_data_url},
};
#[derive(Debug)]
pub struct ResolvedDataUri {
pub data: ArcStr,
pub module_type: ModuleType,
}
#[derive(Debug, Default)]
pub struct DataUriPlugin {
resolved_data_uri: FxDashMap<String, ResolvedDataUri>,
}
impl Plugin for DataUriPlugin {
fn name(&self) -> Cow<'static, str> {
Cow::Borrowed("builtin:data-uri")
}
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()
};
self
.resolved_data_uri
.insert(args.specifier.to_string(), ResolvedDataUri { data, module_type });
return Ok(Some(HookResolveIdOutput { id: args.specifier.into(), ..Default::default() }));
}
Ok(None)
}
async fn load(&self, _ctx: &PluginContext, args: &HookLoadArgs<'_>) -> HookLoadReturn {
if is_data_url(args.id) {
let Some(resolved) = self.resolved_data_uri.get(args.id) else {
return Ok(None);
};
Ok(Some(HookLoadOutput {
code: resolved.data.clone(),
module_type: Some(resolved.module_type.clone()),
..Default::default()
}))
} else {
Ok(None)
}
}
fn register_hook_usage(&self) -> HookUsage {
HookUsage::ResolveId | HookUsage::Load
}
}