turbopack_ecmascript_runtime/
browser_runtime.rs1use std::io::Write;
2
3use anyhow::Result;
4use indoc::writedoc;
5use turbo_rcstr::{RcStr, rcstr};
6use turbo_tasks::{ResolvedVc, Vc};
7use turbopack_core::{
8 chunk::{AssetSuffix, ChunkLoadRetry, CrossOrigin},
9 code_builder::{Code, CodeBuilder},
10 context::AssetContext,
11 environment::ChunkLoading,
12};
13use turbopack_ecmascript::utils::StringifyJs;
14
15use crate::{RuntimeType, embed_js::embed_static_code};
16
17#[turbo_tasks::function]
19pub async fn get_browser_runtime_code(
20 asset_context: ResolvedVc<Box<dyn AssetContext>>,
21 chunk_base_path: Vc<Option<RcStr>>,
22 asset_suffix: Vc<AssetSuffix>,
23 runtime_type: RuntimeType,
24 output_root_to_root_path: RcStr,
25 generate_source_map: bool,
26 chunk_loading_global: Vc<RcStr>,
27 cross_origin: Vc<CrossOrigin>,
28 chunk_load_retry: Vc<ChunkLoadRetry>,
29 has_async_modules: bool,
30 chunk_loading: Vc<ChunkLoading>,
31 support_component_chunks: bool,
32) -> Result<Vc<Code>> {
33 let asset_context = *asset_context;
34 let environment = asset_context.compile_time_info().environment();
35
36 let shared_runtime_utils_code = embed_static_code(
37 asset_context,
38 rcstr!("shared/runtime/runtime-utils.ts"),
39 generate_source_map,
40 );
41
42 let mut runtime_base_code = vec!["browser/runtime/base/runtime-base.ts"];
43 match runtime_type {
44 RuntimeType::Production => runtime_base_code.push("browser/runtime/base/build-base.ts"),
45 RuntimeType::Development => {
46 runtime_base_code.push("shared/runtime/hmr-runtime.ts");
47 runtime_base_code.push("browser/runtime/base/dev-base.ts");
48 }
49 #[cfg(feature = "test")]
50 RuntimeType::Dummy => {
51 panic!("This configuration is not supported in the browser runtime")
52 }
53 }
54
55 let chunk_loading = &*chunk_loading.await?;
56
57 let mut runtime_backend_code = vec![];
58 match (chunk_loading, runtime_type) {
59 (ChunkLoading::Edge | ChunkLoading::SingleChunk, RuntimeType::Development) => {
62 runtime_backend_code
63 .push("browser/runtime/self-contained/runtime-backend-self-contained.ts");
64 runtime_backend_code
65 .push("browser/runtime/self-contained/dev-backend-self-contained.ts");
66 }
67 (ChunkLoading::Edge | ChunkLoading::SingleChunk, RuntimeType::Production) => {
68 runtime_backend_code
69 .push("browser/runtime/self-contained/runtime-backend-self-contained.ts");
70 }
71 (ChunkLoading::NodeJs, _) => {
73 panic!("Node.js runtime is not supported in the browser runtime!")
74 }
75 (ChunkLoading::Dom, RuntimeType::Development) => {
76 runtime_backend_code.push("browser/runtime/dom/runtime-backend-dom.ts");
77 runtime_backend_code.push("browser/runtime/dom/dev-backend-dom.ts");
78 }
79 (ChunkLoading::Dom, RuntimeType::Production) => {
80 runtime_backend_code.push("browser/runtime/dom/runtime-backend-dom.ts");
81 }
82
83 #[cfg(feature = "test")]
84 (_, RuntimeType::Dummy) => {
85 panic!("This configuration is not supported in the browser runtime")
86 }
87 };
88
89 let mut code: CodeBuilder = CodeBuilder::default();
90 let relative_root_path = output_root_to_root_path;
91 let chunk_base_path = chunk_base_path.await?;
92 let chunk_base_path = chunk_base_path.as_ref().map_or_else(|| "", |f| f.as_str());
93 let asset_suffix = asset_suffix.await?;
94 let chunk_loading_global = chunk_loading_global.await?;
95 let cross_origin = *cross_origin.await?;
96 let chunk_lists_global = format!("{}_CHUNK_LISTS", chunk_loading_global);
97
98 if *environment
99 .runtime_versions()
100 .supports_arrow_functions()
101 .await?
102 {
103 code += "(() => {\n";
104 } else {
105 code += "(function(){\n";
106 }
107
108 writedoc!(
109 code,
110 r#"
111 if (!Array.isArray(globalThis[{}])) {{
112 return;
113 }}
114
115 var CHUNK_BASE_PATH = {};
116 var RELATIVE_ROOT_PATH = {};
117 var RUNTIME_PUBLIC_PATH = {};
118 const SUPPORT_COMPONENT_CHUNKS = {};
119 "#,
120 StringifyJs(&chunk_loading_global),
121 StringifyJs(chunk_base_path),
122 StringifyJs(relative_root_path.as_str()),
123 StringifyJs(chunk_base_path),
124 support_component_chunks,
125 )?;
126
127 match &*asset_suffix {
128 AssetSuffix::None => {
129 writedoc!(
130 code,
131 r#"
132 var ASSET_SUFFIX = "";
133 "#
134 )?;
135 }
136 AssetSuffix::Constant(suffix) => {
137 writedoc!(
138 code,
139 r#"
140 var ASSET_SUFFIX = {};
141 "#,
142 StringifyJs(suffix.as_str())
143 )?;
144 }
145 AssetSuffix::Inferred => {
146 if matches!(
147 chunk_loading,
148 ChunkLoading::Edge | ChunkLoading::SingleChunk
149 ) {
150 panic!("AssetSuffix::Inferred is not supported in Edge or single-chunk runtimes");
151 }
152 writedoc!(
153 code,
154 r#"
155 var ASSET_SUFFIX = getAssetSuffixFromScriptSrc();
156 "#
157 )?;
158 }
159 AssetSuffix::FromGlobal(global_name) => {
160 writedoc!(
161 code,
162 r#"
163 var ASSET_SUFFIX = globalThis[{}] || "";
164 "#,
165 StringifyJs(global_name)
166 )?;
167 }
168 }
169
170 let cross_origin = cross_origin.as_str();
171 writedoc!(
172 code,
173 r#"
174 var CROSS_ORIGIN = {};
175 "#,
176 StringifyJs(&cross_origin)
177 )?;
178
179 let chunk_load_retry = *chunk_load_retry.await?;
182 writedoc!(
183 code,
184 r#"
185 var CHUNK_LOAD_RETRY_MAX_ATTEMPTS = {};
186 var CHUNK_LOAD_RETRY_BASE_DELAY_MS = {};
187 var CHUNK_LOAD_RETRY_MAX_JITTER_MS = {};
188 "#,
189 chunk_load_retry.max_retry_attempts,
190 chunk_load_retry.base_delay_ms,
191 chunk_load_retry.max_jitter_ms,
192 )?;
193
194 code.push_code(&*shared_runtime_utils_code.await?);
195 if has_async_modules {
197 code.push_code(
198 &*embed_static_code(
199 asset_context,
200 rcstr!("shared/runtime/async-module.ts"),
201 generate_source_map,
202 )
203 .await?,
204 );
205 }
206 for runtime_code in runtime_base_code {
207 code.push_code(
208 &*embed_static_code(asset_context, runtime_code.into(), generate_source_map).await?,
209 );
210 }
211
212 if *environment.supports_commonjs_externals().await? {
213 code.push_code(
214 &*embed_static_code(
215 asset_context,
216 rcstr!("shared-node/base-externals-utils.ts"),
217 generate_source_map,
218 )
219 .await?,
220 );
221 }
222 if *environment.node_externals().await? {
223 code.push_code(
224 &*embed_static_code(
225 asset_context,
226 rcstr!("shared-node/node-externals-utils.ts"),
227 generate_source_map,
228 )
229 .await?,
230 );
231 }
232 for backend_code in runtime_backend_code {
233 code.push_code(
234 &*embed_static_code(asset_context, backend_code.into(), generate_source_map).await?,
235 );
236 }
237
238 writedoc!(
242 code,
243 r#"
244 var chunksToRegister = globalThis[{chunk_loading_global}];
245 globalThis[{chunk_loading_global}] = {{ push: registerChunk }};
246 chunksToRegister.forEach(registerChunk);
247 "#,
248 chunk_loading_global = StringifyJs(&chunk_loading_global),
249 )?;
250 if matches!(runtime_type, RuntimeType::Development) {
251 writedoc!(
252 code,
253 r#"
254 var chunkListsToRegister = globalThis[{chunk_lists_global}] || [];
255 globalThis[{chunk_lists_global}] = {{ push: registerChunkList }};
256 chunkListsToRegister.forEach(registerChunkList);
257 "#,
258 chunk_lists_global = StringifyJs(&chunk_lists_global),
259 )?;
260 }
261 writedoc!(
262 code,
263 r#"
264 }})();
265 "#
266 )?;
267
268 Ok(Code::cell(code.build()))
269}
270
271pub fn get_worker_runtime_code(
273 asset_context: Vc<Box<dyn AssetContext>>,
274 generate_source_map: bool,
275) -> Result<Vc<Code>> {
276 Ok(embed_static_code(
277 asset_context,
278 rcstr!("browser/runtime/base/worker-entrypoint.ts"),
279 generate_source_map,
280 ))
281}