turbopack_core/
data_uri_source.rs

1use 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/// The raw [Source]. It represents raw content from a path without any
14/// references to other [Source]s.
15#[turbo_tasks::value]
16pub struct DataUriSource {
17    media_type: RcStr,
18    encoding: RcStr,
19    data: ResolvedVc<RcStr>,
20    lookup_path: ResolvedVc<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: ResolvedVc<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 ident(&self) -> Result<Vc<AssetIdent>> {
45        let content_type = self.media_type.split(";").next().unwrap().into();
46        let filename = format!(
47            "data:{}",
48            &encode_hex(hash_xxh3_hash64((
49                &*self.data.await?,
50                &self.media_type,
51                &self.encoding
52            )))[0..6]
53        );
54        Ok(
55            AssetIdent::from_path(self.lookup_path.join(filename.into()))
56                .with_content_type(content_type),
57        )
58    }
59}
60
61#[turbo_tasks::value_impl]
62impl Asset for DataUriSource {
63    #[turbo_tasks::function]
64    async fn content(&self) -> Result<Vc<AssetContent>> {
65        let data = self.data.await?;
66        let rope = if self.encoding == "base64" {
67            let decoded = data_encoding::BASE64.decode(data.as_bytes())?;
68            // TODO this should read self.media_type and potentially use a different encoding
69            Rope::from(decoded)
70        } else if self.encoding.is_empty() {
71            let decoded = urlencoding::decode(data.as_str())?.into_owned();
72            Rope::from(decoded)
73        } else {
74            bail!("Unsupported data URL encoding: {}", self.encoding);
75        };
76        Ok(AssetContent::file(
77            FileContent::from(File::from(rope)).cell(),
78        ))
79    }
80}