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, AssetContent},
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 Asset for InlinedBytesJsModule {
61    #[turbo_tasks::function]
62    fn content(&self) -> Vc<AssetContent> {
63        self.source.content()
64    }
65}
66
67#[turbo_tasks::value_impl]
68impl ChunkableModule for InlinedBytesJsModule {
69    #[turbo_tasks::function]
70    fn as_chunk_item(
71        self: ResolvedVc<Self>,
72        _module_graph: Vc<ModuleGraph>,
73        chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
74    ) -> Vc<Box<dyn turbopack_core::chunk::ChunkItem>> {
75        Vc::upcast(InlinedBytesJsChunkItem::cell(InlinedBytesJsChunkItem {
76            module: self,
77            chunking_context,
78        }))
79    }
80}
81
82#[turbo_tasks::value_impl]
83impl EcmascriptChunkPlaceable for InlinedBytesJsModule {
84    #[turbo_tasks::function]
85    fn get_exports(&self) -> Vc<EcmascriptExports> {
86        EcmascriptExports::Value.cell()
87    }
88}
89
90#[turbo_tasks::value]
91struct InlinedBytesJsChunkItem {
92    module: ResolvedVc<InlinedBytesJsModule>,
93    chunking_context: ResolvedVc<Box<dyn ChunkingContext>>,
94}
95
96#[turbo_tasks::value_impl]
97impl OutputAssetsReference for InlinedBytesJsChunkItem {}
98
99#[turbo_tasks::value_impl]
100impl ChunkItem for InlinedBytesJsChunkItem {
101    #[turbo_tasks::function]
102    fn asset_ident(&self) -> Vc<AssetIdent> {
103        self.module.ident()
104    }
105
106    #[turbo_tasks::function]
107    fn chunking_context(&self) -> Vc<Box<dyn ChunkingContext>> {
108        *self.chunking_context
109    }
110
111    #[turbo_tasks::function]
112    async fn ty(&self) -> Result<Vc<Box<dyn ChunkType>>> {
113        Ok(Vc::upcast(
114            Vc::<EcmascriptChunkType>::default().resolve().await?,
115        ))
116    }
117
118    #[turbo_tasks::function]
119    fn module(&self) -> Vc<Box<dyn Module>> {
120        Vc::upcast(*self.module)
121    }
122}
123
124#[turbo_tasks::value_impl]
125impl EcmascriptChunkItem for InlinedBytesJsChunkItem {
126    #[turbo_tasks::function]
127    async fn content(&self) -> Result<Vc<EcmascriptChunkItemContent>> {
128        let content = self.module.content().file_content().await?;
129        match &*content {
130            FileContent::Content(data) => {
131                let mut inner_code = RopeBuilder::default();
132                inner_code += "
133var decode = Uint8Array.fromBase64 || function Uint8Array_fromBase64(base64) {
134  var binaryString = atob(base64);
135  var buffer = new Uint8Array(binaryString.length);
136  for (var i = 0; i < binaryString.length; i++) {
137    buffer[i] = binaryString.charCodeAt(i)
138  }
139  return buffer
140};\n";
141
142                let encoded = data_encoding::BASE64_NOPAD
143                    .encode(&data.read().bytes().collect::<std::io::Result<Vec<u8>>>()?);
144                write!(
145                    inner_code,
146                    "{TURBOPACK_EXPORT_VALUE}(decode({}));",
147                    StringifyJs(&encoded)
148                )?;
149
150                Ok(EcmascriptChunkItemContent {
151                    inner_code: inner_code.build(),
152                    ..Default::default()
153                }
154                .cell())
155            }
156            FileContent::NotFound => {
157                bail!("File not found: {}", self.module.ident().to_string().await?);
158            }
159        }
160    }
161}