turbopack_ecmascript/
inlined_bytes_module.rs

1use std::io::{Read, Write};
2
3use anyhow::{Result, bail};
4use turbo_rcstr::rcstr;
5use turbo_tasks::{ResolvedVc, ValueToString, Vc};
6use turbo_tasks_fs::{FileContent, glob::Glob, rope::RopeBuilder};
7use turbopack_core::{
8    asset::{Asset, AssetContent},
9    chunk::{ChunkItem, ChunkType, ChunkableModule, ChunkingContext},
10    ident::AssetIdent,
11    module::Module,
12    module_graph::ModuleGraph,
13    source::Source,
14};
15
16use crate::{
17    chunk::{
18        EcmascriptChunkItem, EcmascriptChunkItemContent, EcmascriptChunkPlaceable,
19        EcmascriptChunkType, EcmascriptExports,
20    },
21    runtime_functions::TURBOPACK_EXPORT_VALUE,
22    utils::StringifyJs,
23};
24
25#[turbo_tasks::value]
26pub struct InlinedBytesJsModule {
27    source: ResolvedVc<Box<dyn Source>>,
28}
29
30#[turbo_tasks::value_impl]
31impl InlinedBytesJsModule {
32    #[turbo_tasks::function]
33    pub fn new(source: ResolvedVc<Box<dyn Source>>) -> Vc<Self> {
34        Self::cell(InlinedBytesJsModule { source })
35    }
36}
37
38#[turbo_tasks::value_impl]
39impl Module for InlinedBytesJsModule {
40    #[turbo_tasks::function]
41    fn ident(&self) -> Vc<AssetIdent> {
42        self.source
43            .ident()
44            .with_modifier(rcstr!("static bytes in ecmascript"))
45    }
46}
47
48#[turbo_tasks::value_impl]
49impl Asset for InlinedBytesJsModule {
50    #[turbo_tasks::function]
51    fn content(&self) -> Vc<AssetContent> {
52        self.source.content()
53    }
54}
55
56#[turbo_tasks::value_impl]
57impl ChunkableModule for InlinedBytesJsModule {
58    #[turbo_tasks::function]
59    fn as_chunk_item(
60        self: ResolvedVc<Self>,
61        _module_graph: Vc<ModuleGraph>,
62        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
63    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
64        Vc::upcast(InlinedBytesJsChunkItem::cell(InlinedBytesJsChunkItem {
65            module: self,
66            chunking_context,
67        }))
68    }
69}
70
71#[turbo_tasks::value_impl]
72impl EcmascriptChunkPlaceable for InlinedBytesJsModule {
73    #[turbo_tasks::function]
74    fn get_exports(&self) -> Vc<EcmascriptExports> {
75        EcmascriptExports::Value.cell()
76    }
77
78    #[turbo_tasks::function]
79    fn is_marked_as_side_effect_free(&self, _side_effect_free_packages: Vc<Glob>) -> Vc<bool> {
80        Vc::cell(true)
81    }
82}
83
84#[turbo_tasks::value]
85struct InlinedBytesJsChunkItem {
86    module: ResolvedVc<InlinedBytesJsModule>,
87    chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
88}
89
90#[turbo_tasks::value_impl]
91impl ChunkItem for InlinedBytesJsChunkItem {
92    #[turbo_tasks::function]
93    fn asset_ident(&self) -> Vc<AssetIdent> {
94        self.module.ident()
95    }
96
97    #[turbo_tasks::function]
98    fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
99        *self.chunking_context
100    }
101
102    #[turbo_tasks::function]
103    async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
104        Ok(Vc::upcast(
105            Vc::<EcmascriptChunkType>::default().resolve().await?,
106        ))
107    }
108
109    #[turbo_tasks::function]
110    fn module(&self) -> Vc<Box<dyn Module>> {
111        Vc::upcast(*self.module)
112    }
113}
114
115#[turbo_tasks::value_impl]
116impl EcmascriptChunkItem for InlinedBytesJsChunkItem {
117    #[turbo_tasks::function]
118    async fn content(&self) -> Result<Vc<EcmascriptChunkItemContent>> {
119        let content = self.module.content().file_content().await?;
120        match &*content {
121            FileContent::Content(data) => {
122                let mut inner_code = RopeBuilder::default();
123                inner_code += "
124var decode = Uint8Array.fromBase64 || function Uint8Array_fromBase64(base64) {
125  var binaryString = atob(base64);
126  var buffer = new Uint8Array(binaryString.length);
127  for (var i = 0; i < binaryString.length; i++) {
128    buffer[i] = binaryString.charCodeAt(i)
129  }
130  return buffer
131};\n";
132
133                let encoded = data_encoding::BASE64_NOPAD
134                    .encode(&data.read().bytes().collect::<std::io::Result<Vec<u8>>>()?);
135                write!(
136                    inner_code,
137                    "{TURBOPACK_EXPORT_VALUE}(decode({}));",
138                    StringifyJs(&encoded)
139                )?;
140
141                Ok(EcmascriptChunkItemContent {
142                    inner_code: inner_code.build(),
143                    ..Default::default()
144                }
145                .into())
146            }
147            FileContent::NotFound => {
148                bail!("File not found: {}", self.module.ident().to_string().await?);
149            }
150        }
151    }
152}