Skip to main content

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