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: Option<RcStr>,
21    chunk_suffix_path: 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).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            // TODO
71            runtime_backend_code.push("browser/runtime/dom/runtime-backend-dom.ts");
72        }
73
74        #[cfg(feature = "test")]
75        (_, RuntimeType::Dummy) => {
76            panic!("This configuration is not supported in the browser runtime")
77        }
78    };
79
80    let mut code: CodeBuilder = CodeBuilder::default();
81    let relative_root_path = output_root_to_root_path;
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
84        .as_ref()
85        .map_or_else(|| "", |f| f.as_str());
86
87    writedoc!(
88        code,
89        r#"
90            (() => {{
91            if (!Array.isArray(globalThis.TURBOPACK)) {{
92                return;
93            }}
94
95            const CHUNK_BASE_PATH = {};
96            const CHUNK_SUFFIX_PATH = {};
97            const RELATIVE_ROOT_PATH = {};
98            const RUNTIME_PUBLIC_PATH = {};
99        "#,
100        StringifyJs(chunk_base_path),
101        StringifyJs(chunk_suffix_path),
102        StringifyJs(relative_root_path.as_str()),
103        StringifyJs(chunk_base_path),
104    )?;
105
106    code.push_code(&*shared_runtime_utils_code.await?);
107    for runtime_code in runtime_base_code {
108        code.push_code(
109            &*embed_static_code(asset_context, runtime_code.into(), generate_source_map).await?,
110        );
111    }
112
113    if *environment.supports_commonjs_externals().await? {
114        code.push_code(
115            &*embed_static_code(
116                asset_context,
117                rcstr!("shared-node/base-externals-utils.ts"),
118                generate_source_map,
119            )
120            .await?,
121        );
122    }
123    if *environment.node_externals().await? {
124        code.push_code(
125            &*embed_static_code(
126                asset_context,
127                rcstr!("shared-node/node-externals-utils.ts"),
128                generate_source_map,
129            )
130            .await?,
131        );
132    }
133    if *environment.supports_wasm().await? {
134        code.push_code(
135            &*embed_static_code(
136                asset_context,
137                rcstr!("shared-node/node-wasm-utils.ts"),
138                generate_source_map,
139            )
140            .await?,
141        );
142    }
143
144    for backend_code in runtime_backend_code {
145        code.push_code(
146            &*embed_static_code(asset_context, backend_code.into(), generate_source_map).await?,
147        );
148    }
149
150    // Registering chunks and chunk lists depends on the BACKEND variable, which is set by the
151    // specific runtime code, hence it must be appended after it.
152    writedoc!(
153        code,
154        r#"
155            const chunksToRegister = globalThis.TURBOPACK;
156            globalThis.TURBOPACK = {{ push: registerChunk }};
157            chunksToRegister.forEach(registerChunk);
158        "#
159    )?;
160    if matches!(runtime_type, RuntimeType::Development) {
161        writedoc!(
162            code,
163            r#"
164            const chunkListsToRegister = globalThis.TURBOPACK_CHUNK_LISTS || [];
165            chunkListsToRegister.forEach(registerChunkList);
166            globalThis.TURBOPACK_CHUNK_LISTS = {{ push: registerChunkList }};
167        "#
168        )?;
169    }
170    writedoc!(
171        code,
172        r#"
173            }})();
174        "#
175    )?;
176
177    Ok(Code::cell(code.build()))
178}