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::{magic_identifier, 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            debug_assert!(
39                // The dev runtime makes this assumption. If that's no longer true, we need to
40                // update the runtime code.
41                magic_identifier::mangle("module evaluation").as_str()
42                    == "__TURBOPACK__module__evaluation__"
43            );
44            runtime_base_code.push("browser/runtime/base/dev-base.ts");
45        }
46        #[cfg(feature = "test")]
47        RuntimeType::Dummy => {
48            panic!("This configuration is not supported in the browser runtime")
49        }
50    }
51
52    let chunk_loading = &*asset_context
53        .compile_time_info()
54        .environment()
55        .chunk_loading()
56        .await?;
57
58    let mut runtime_backend_code = vec![];
59    match (chunk_loading, runtime_type) {
60        (ChunkLoading::Edge, RuntimeType::Development) => {
61            runtime_backend_code.push("browser/runtime/edge/runtime-backend-edge.ts");
62            runtime_backend_code.push("browser/runtime/edge/dev-backend-edge.ts");
63        }
64        (ChunkLoading::Edge, RuntimeType::Production) => {
65            runtime_backend_code.push("browser/runtime/edge/runtime-backend-edge.ts");
66        }
67        // This case should never be hit.
68        (ChunkLoading::NodeJs, _) => {
69            panic!("Node.js runtime is not supported in the browser runtime!")
70        }
71        (ChunkLoading::Dom, RuntimeType::Development) => {
72            runtime_backend_code.push("browser/runtime/dom/runtime-backend-dom.ts");
73            runtime_backend_code.push("browser/runtime/dom/dev-backend-dom.ts");
74        }
75        (ChunkLoading::Dom, RuntimeType::Production) => {
76            runtime_backend_code.push("browser/runtime/dom/runtime-backend-dom.ts");
77        }
78
79        #[cfg(feature = "test")]
80        (_, RuntimeType::Dummy) => {
81            panic!("This configuration is not supported in the browser runtime")
82        }
83    };
84
85    let mut code: CodeBuilder = CodeBuilder::default();
86    let relative_root_path = output_root_to_root_path;
87    let chunk_base_path = chunk_base_path.await?;
88    let chunk_base_path = chunk_base_path.as_ref().map_or_else(|| "", |f| f.as_str());
89    let chunk_suffix_path = chunk_suffix_path.await?;
90    let chunk_suffix_path = chunk_suffix_path
91        .as_ref()
92        .map_or_else(|| "", |f| f.as_str());
93
94    writedoc!(
95        code,
96        r#"
97            (() => {{
98            if (!Array.isArray(globalThis.TURBOPACK)) {{
99                return;
100            }}
101
102            const CHUNK_BASE_PATH = {};
103            const CHUNK_SUFFIX_PATH = {};
104            const RELATIVE_ROOT_PATH = {};
105            const RUNTIME_PUBLIC_PATH = {};
106        "#,
107        StringifyJs(chunk_base_path),
108        StringifyJs(chunk_suffix_path),
109        StringifyJs(relative_root_path.as_str()),
110        StringifyJs(chunk_base_path),
111    )?;
112
113    code.push_code(&*shared_runtime_utils_code.await?);
114    for runtime_code in runtime_base_code {
115        code.push_code(
116            &*embed_static_code(asset_context, runtime_code.into(), generate_source_map).await?,
117        );
118    }
119
120    if *environment.supports_commonjs_externals().await? {
121        code.push_code(
122            &*embed_static_code(
123                asset_context,
124                rcstr!("shared-node/base-externals-utils.ts"),
125                generate_source_map,
126            )
127            .await?,
128        );
129    }
130    if *environment.node_externals().await? {
131        code.push_code(
132            &*embed_static_code(
133                asset_context,
134                rcstr!("shared-node/node-externals-utils.ts"),
135                generate_source_map,
136            )
137            .await?,
138        );
139    }
140    if *environment.supports_wasm().await? {
141        code.push_code(
142            &*embed_static_code(
143                asset_context,
144                rcstr!("shared-node/node-wasm-utils.ts"),
145                generate_source_map,
146            )
147            .await?,
148        );
149    }
150
151    for backend_code in runtime_backend_code {
152        code.push_code(
153            &*embed_static_code(asset_context, backend_code.into(), generate_source_map).await?,
154        );
155    }
156
157    // Registering chunks and chunk lists depends on the BACKEND variable, which is set by the
158    // specific runtime code, hence it must be appended after it.
159    writedoc!(
160        code,
161        r#"
162            const chunksToRegister = globalThis.TURBOPACK;
163            globalThis.TURBOPACK = {{ push: registerChunk }};
164            chunksToRegister.forEach(registerChunk);
165        "#
166    )?;
167    if matches!(runtime_type, RuntimeType::Development) {
168        writedoc!(
169            code,
170            r#"
171            const chunkListsToRegister = globalThis.TURBOPACK_CHUNK_LISTS || [];
172            globalThis.TURBOPACK_CHUNK_LISTS = {{ push: registerChunkList }};
173            chunkListsToRegister.forEach(registerChunkList);
174        "#
175        )?;
176    }
177    writedoc!(
178        code,
179        r#"
180            }})();
181        "#
182    )?;
183
184    Ok(Code::cell(code.build()))
185}