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