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,
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}