turbopack_ecmascript_runtime/
browser_runtime.rs

1use std::io::Write;
2
3use anyhow::Result;
4use indoc::writedoc;
5use turbo_rcstr::{RcStr, rcstr};
6use turbo_tasks::{ResolvedVc, Vc};
7use turbopack_core::{
8    code_builder::{Code, CodeBuilder},
9    context::AssetContext,
10    environment::{ChunkLoading, Environment},
11};
12use turbopack_ecmascript::utils::StringifyJs;
13
14use crate::{RuntimeType, asset_context::get_runtime_asset_context, embed_js::embed_static_code};
15
16/// Returns the code for the ECMAScript runtime.
17#[turbo_tasks::function]
18pub async fn get_browser_runtime_code(
19    environment: ResolvedVc<Environment>,
20    chunk_base_path: Vc<Option<RcStr>>,
21    chunk_suffix_path: Vc<Option<RcStr>>,
22    runtime_type: RuntimeType,
23    output_root_to_root_path: RcStr,
24    generate_source_map: bool,
25) -> Result<Vc<Code>> {
26    let asset_context = get_runtime_asset_context(*environment).resolve().await?;
27
28    let shared_runtime_utils_code = embed_static_code(
29        asset_context,
30        rcstr!("shared/runtime-utils.ts"),
31        generate_source_map,
32    );
33
34    let mut runtime_base_code = vec!["browser/runtime/base/runtime-base.ts"];
35    match runtime_type {
36        RuntimeType::Production => runtime_base_code.push("browser/runtime/base/build-base.ts"),
37        RuntimeType::Development => {
38            runtime_base_code.push("browser/runtime/base/dev-base.ts");
39        }
40        #[cfg(feature = "test")]
41        RuntimeType::Dummy => {
42            panic!("This configuration is not supported in the browser runtime")
43        }
44    }
45
46    let chunk_loading = &*asset_context
47        .compile_time_info()
48        .environment()
49        .chunk_loading()
50        .await?;
51
52    let mut runtime_backend_code = vec![];
53    match (chunk_loading, runtime_type) {
54        (ChunkLoading::Edge, RuntimeType::Development) => {
55            runtime_backend_code.push("browser/runtime/edge/runtime-backend-edge.ts");
56            runtime_backend_code.push("browser/runtime/edge/dev-backend-edge.ts");
57        }
58        (ChunkLoading::Edge, RuntimeType::Production) => {
59            runtime_backend_code.push("browser/runtime/edge/runtime-backend-edge.ts");
60        }
61        // This case should never be hit.
62        (ChunkLoading::NodeJs, _) => {
63            panic!("Node.js runtime is not supported in the browser runtime!")
64        }
65        (ChunkLoading::Dom, RuntimeType::Development) => {
66            runtime_backend_code.push("browser/runtime/dom/runtime-backend-dom.ts");
67            runtime_backend_code.push("browser/runtime/dom/dev-backend-dom.ts");
68        }
69        (ChunkLoading::Dom, RuntimeType::Production) => {
70            runtime_backend_code.push("browser/runtime/dom/runtime-backend-dom.ts");
71        }
72
73        #[cfg(feature = "test")]
74        (_, RuntimeType::Dummy) => {
75            panic!("This configuration is not supported in the browser runtime")
76        }
77    };
78
79    let mut code: CodeBuilder = CodeBuilder::default();
80    let relative_root_path = output_root_to_root_path;
81    let chunk_base_path = chunk_base_path.await?;
82    let chunk_base_path = chunk_base_path.as_ref().map_or_else(|| "", |f| f.as_str());
83    let chunk_suffix_path = chunk_suffix_path.await?;
84    let chunk_suffix_path = chunk_suffix_path
85        .as_ref()
86        .map_or_else(|| "", |f| f.as_str());
87
88    writedoc!(
89        code,
90        r#"
91            (() => {{
92            if (!Array.isArray(globalThis.TURBOPACK)) {{
93                return;
94            }}
95
96            const CHUNK_BASE_PATH = {};
97            const CHUNK_SUFFIX_PATH = {};
98            const RELATIVE_ROOT_PATH = {};
99            const RUNTIME_PUBLIC_PATH = {};
100        "#,
101        StringifyJs(chunk_base_path),
102        StringifyJs(chunk_suffix_path),
103        StringifyJs(relative_root_path.as_str()),
104        StringifyJs(chunk_base_path),
105    )?;
106
107    code.push_code(&*shared_runtime_utils_code.await?);
108    for runtime_code in runtime_base_code {
109        code.push_code(
110            &*embed_static_code(asset_context, runtime_code.into(), generate_source_map).await?,
111        );
112    }
113
114    if *environment.supports_commonjs_externals().await? {
115        code.push_code(
116            &*embed_static_code(
117                asset_context,
118                rcstr!("shared-node/base-externals-utils.ts"),
119                generate_source_map,
120            )
121            .await?,
122        );
123    }
124    if *environment.node_externals().await? {
125        code.push_code(
126            &*embed_static_code(
127                asset_context,
128                rcstr!("shared-node/node-externals-utils.ts"),
129                generate_source_map,
130            )
131            .await?,
132        );
133    }
134    if *environment.supports_wasm().await? {
135        code.push_code(
136            &*embed_static_code(
137                asset_context,
138                rcstr!("shared-node/node-wasm-utils.ts"),
139                generate_source_map,
140            )
141            .await?,
142        );
143    }
144
145    for backend_code in runtime_backend_code {
146        code.push_code(
147            &*embed_static_code(asset_context, backend_code.into(), generate_source_map).await?,
148        );
149    }
150
151    // Registering chunks and chunk lists depends on the BACKEND variable, which is set by the
152    // specific runtime code, hence it must be appended after it.
153    writedoc!(
154        code,
155        r#"
156            const chunksToRegister = globalThis.TURBOPACK;
157            globalThis.TURBOPACK = {{ push: registerChunk }};
158            chunksToRegister.forEach(registerChunk);
159        "#
160    )?;
161    if matches!(runtime_type, RuntimeType::Development) {
162        writedoc!(
163            code,
164            r#"
165            const chunkListsToRegister = globalThis.TURBOPACK_CHUNK_LISTS || [];
166            globalThis.TURBOPACK_CHUNK_LISTS = {{ push: registerChunkList }};
167            chunkListsToRegister.forEach(registerChunkList);
168        "#
169        )?;
170    }
171    writedoc!(
172        code,
173        r#"
174            }})();
175        "#
176    )?;
177
178    Ok(Code::cell(code.build()))
179}