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