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(AssetIdent::from_path(self.lookup_path.join(&filename)?)
71 .with_content_type(content_type)
72 .into_vc())
73 }
74}
75
76#[turbo_tasks::value_impl]
77impl Asset for DataUriSource {
78 #[turbo_tasks::function]
79 async fn content(&self) -> Result<Vc<AssetContent>> {
80 let data = self.data.await?;
81 let rope = if self.encoding == "base64" {
82 let decoded = data_encoding::BASE64.decode(data.as_bytes())?;
83 Rope::from(decoded)
85 } else if self.encoding.is_empty() {
86 let decoded = urlencoding::decode(data.as_str())?.into_owned();
87 Rope::from(decoded)
88 } else {
89 bail!("Unsupported data URL encoding: {}", self.encoding);
90 };
91 Ok(AssetContent::file(
92 FileContent::from(File::from(rope)).cell(),
93 ))
94 }
95}