turbopack_core/
data_uri_source.rs1use anyhow::{Result, bail};
2use turbo_rcstr::RcStr;
3use turbo_tasks::{ResolvedVc, Vc};
4use turbo_tasks_fs::{File, FileContent, FileSystemPath, rope::Rope};
5use turbo_tasks_hash::{encode_hex, hash_xxh3_hash64};
6
7use crate::{
8 asset::{Asset, AssetContent},
9 ident::AssetIdent,
10 source::Source,
11};
12
13#[turbo_tasks::value]
16pub struct DataUriSource {
17 media_type: RcStr,
18 encoding: RcStr,
19 data: ResolvedVc<RcStr>,
20 lookup_path: FileSystemPath,
21}
22
23#[turbo_tasks::value_impl]
24impl DataUriSource {
25 #[turbo_tasks::function]
26 pub fn new(
27 media_type: RcStr,
28 encoding: RcStr,
29 data: ResolvedVc<RcStr>,
30 lookup_path: FileSystemPath,
31 ) -> Vc<Self> {
32 Self::cell(DataUriSource {
33 media_type,
34 encoding,
35 data,
36 lookup_path,
37 })
38 }
39}
40
41#[turbo_tasks::value_impl]
42impl Source for DataUriSource {
43 #[turbo_tasks::function]
44 async fn description(&self) -> Result<Vc<RcStr>> {
45 let media_type = &self.media_type;
46 let encoding = &self.encoding;
47 let data = self.data.await?;
48 let sep = if encoding.is_empty() { "" } else { ";" };
51 let full_with_data = format!("data:{media_type}{sep}{encoding},{data}");
52 let prefix: String = full_with_data.chars().take(50).collect();
53 let ellipsis = if full_with_data.len() > 50 { "..." } else { "" };
54 Ok(Vc::cell(
55 format!("data URI content ({prefix}{ellipsis})").into(),
56 ))
57 }
58
59 #[turbo_tasks::function]
60 async fn ident(&self) -> Result<Vc<AssetIdent>> {
61 let content_type = self.media_type.split(";").next().unwrap().into();
62 let filename = format!(
63 "data:{}",
64 &encode_hex(hash_xxh3_hash64((
65 &*self.data.await?,
66 &self.media_type,
67 &self.encoding
68 )))[0..6]
69 );
70 Ok(
71 AssetIdent::from_path(self.lookup_path.join(&filename)?)
72 .with_content_type(content_type),
73 )
74 }
75}
76
77#[turbo_tasks::value_impl]
78impl Asset for DataUriSource {
79 #[turbo_tasks::function]
80 async fn content(&self) -> Result<Vc<AssetContent>> {
81 let data = self.data.await?;
82 let rope = if self.encoding == "base64" {
83 let decoded = data_encoding::BASE64.decode(data.as_bytes())?;
84 Rope::from(decoded)
86 } else if self.encoding.is_empty() {
87 let decoded = urlencoding::decode(data.as_str())?.into_owned();
88 Rope::from(decoded)
89 } else {
90 bail!("Unsupported data URL encoding: {}", self.encoding);
91 };
92 Ok(AssetContent::file(
93 FileContent::from(File::from(rope)).cell(),
94 ))
95 }
96}