turbopack_ecmascript/
inlined_bytes_module.rs1use 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}