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