turbopack_nodejs/ecmascript/node/entry/
runtime.rs1use std::io::Write;
2
3use anyhow::Result;
4use indoc::writedoc;
5use turbo_rcstr::rcstr;
6use turbo_tasks::{ResolvedVc, ValueToString, Vc, turbobail};
7use turbo_tasks_fs::{File, FileContent, FileSystem, FileSystemPath};
8use turbopack_core::{
9 asset::{Asset, AssetContent},
10 chunk::ChunkingContext,
11 code_builder::{Code, CodeBuilder},
12 ident::AssetIdent,
13 output::{OutputAsset, OutputAssetsReference, OutputAssetsWithReferenced},
14 source_map::{GenerateSourceMap, SourceMapAsset},
15};
16use turbopack_ecmascript::utils::StringifyJs;
17use turbopack_ecmascript_runtime::RuntimeType;
18
19use crate::NodeJsChunkingContext;
20
21#[turbo_tasks::value(shared)]
23#[derive(ValueToString)]
24#[value_to_string("Ecmascript Build Node Runtime Chunk")]
25pub struct EcmascriptBuildNodeRuntimeChunk {
26 chunking_context: ResolvedVc<NodeJsChunkingContext>,
27 include_async_module_runtime: bool,
28}
29
30#[turbo_tasks::value_impl]
31impl EcmascriptBuildNodeRuntimeChunk {
32 #[turbo_tasks::function]
34 pub fn new(
35 chunking_context: ResolvedVc<NodeJsChunkingContext>,
36 include_async_module_runtime: bool,
37 ) -> Vc<Self> {
38 EcmascriptBuildNodeRuntimeChunk {
39 chunking_context,
40 include_async_module_runtime,
41 }
42 .cell()
43 }
44
45 #[turbo_tasks::function]
46 async fn code(self: Vc<Self>) -> Result<Vc<Code>> {
47 let this = self.await?;
48
49 let output_root_to_root_path = this.chunking_context.output_root_to_root_path().await?;
50 let output_root = this.chunking_context.output_root().await?;
51 let generate_source_map = *this
52 .chunking_context
53 .reference_chunk_source_maps(Vc::upcast(self))
54 .await?;
55 let runtime_path = self.path().await?;
56 let runtime_public_path = if let Some(path) = output_root.get_path_to(&runtime_path) {
57 path
58 } else {
59 turbobail!("runtime path {runtime_path} is not in output root {output_root}");
60 };
61
62 let mut code = CodeBuilder::default();
63 let asset_prefix = this.chunking_context.asset_prefix().await?;
64 let asset_prefix = asset_prefix.as_deref().unwrap_or("/");
65
66 let worker_config = Vc::upcast::<Box<dyn ChunkingContext>>(*this.chunking_context)
68 .worker_configuration_options()
69 .await?;
70
71 writedoc!(
72 code,
73 r#"
74 var RUNTIME_PUBLIC_PATH = {};
75 var RELATIVE_ROOT_PATH = {};
76 var ASSET_PREFIX = {};
77 "#,
78 StringifyJs(runtime_public_path),
79 StringifyJs(output_root_to_root_path.as_str()),
80 StringifyJs(asset_prefix),
81 )?;
82
83 if !worker_config.forwarded_globals.is_empty() {
85 writedoc!(
86 code,
87 r#"
88 // Apply forwarded globals from workerData if running in a worker thread
89 if (typeof require !== 'undefined') {{
90 try {{
91 var {{ workerData }} = require('worker_threads');
92 if (workerData?.__turbopack_globals__) {{
93 Object.assign(globalThis, workerData.__turbopack_globals__);
94 // Remove internal data so it's not visible to user code
95 delete workerData.__turbopack_globals__;
96 }}
97 }} catch (_) {{
98 // Not in a worker thread context, ignore
99 }}
100 }}
101 "#,
102 )?;
103 }
104
105 let asset_context =
106 turbopack::get_runtime_asset_context(this.chunking_context.environment());
107
108 match *this.chunking_context.runtime_type().await? {
109 RuntimeType::Development => {
110 let runtime_code = turbopack_ecmascript_runtime::get_nodejs_runtime_code(
111 asset_context,
112 RuntimeType::Development,
113 this.include_async_module_runtime,
114 generate_source_map,
115 );
116 code.push_code(&*runtime_code.await?);
117 }
118 RuntimeType::Production => {
119 let runtime_code = turbopack_ecmascript_runtime::get_nodejs_runtime_code(
120 asset_context,
121 RuntimeType::Production,
122 this.include_async_module_runtime,
123 generate_source_map,
124 );
125 code.push_code(&*runtime_code.await?);
126 }
127 #[cfg(feature = "test")]
128 RuntimeType::Dummy => {
129 let runtime_code = turbopack_ecmascript_runtime::get_dummy_runtime_code();
130 code.push_code(&runtime_code);
131 }
132 }
133
134 Ok(Code::cell(code.build()))
135 }
136
137 #[turbo_tasks::function]
138 async fn ident_for_path(self: Vc<Self>) -> Result<Vc<AssetIdent>> {
139 Ok(AssetIdent::from_path(
140 turbopack_ecmascript_runtime::embed_fs()
141 .root()
142 .await?
143 .join("runtime.js")?,
144 )
145 .into_vc())
146 }
147
148 #[turbo_tasks::function]
149 async fn source_map(self: Vc<Self>) -> Result<Vc<SourceMapAsset>> {
150 let this = self.await?;
151 Ok(SourceMapAsset::new(
152 Vc::upcast(*this.chunking_context),
153 self.ident_for_path(),
154 Vc::upcast(self),
155 ))
156 }
157}
158
159#[turbo_tasks::value_impl]
160impl OutputAssetsReference for EcmascriptBuildNodeRuntimeChunk {
161 #[turbo_tasks::function]
162 async fn references(self: Vc<Self>) -> Result<Vc<OutputAssetsWithReferenced>> {
163 let this = self.await?;
164 let mut references = vec![];
165
166 if *this
167 .chunking_context
168 .reference_chunk_source_maps(Vc::upcast(self))
169 .await?
170 {
171 references.push(ResolvedVc::upcast(self.source_map().to_resolved().await?))
172 }
173
174 Ok(OutputAssetsWithReferenced::from_assets(Vc::cell(
175 references,
176 )))
177 }
178}
179
180#[turbo_tasks::value_impl]
181impl OutputAsset for EcmascriptBuildNodeRuntimeChunk {
182 #[turbo_tasks::function]
183 async fn path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
184 let this = self.await?;
185 let ident = self.ident_for_path();
186
187 Ok(this
188 .chunking_context
189 .chunk_path(Some(Vc::upcast(self)), ident, None, rcstr!(".js")))
190 }
191}
192
193#[turbo_tasks::value_impl]
194impl Asset for EcmascriptBuildNodeRuntimeChunk {
195 #[turbo_tasks::function]
196 async fn content(self: Vc<Self>) -> Result<Vc<AssetContent>> {
197 Ok(AssetContent::file(
198 FileContent::Content(File::from(
199 self.code()
200 .to_rope_with_magic_comments(|| self.source_map())
201 .await?,
202 ))
203 .cell(),
204 ))
205 }
206}
207
208#[turbo_tasks::value_impl]
209impl GenerateSourceMap for EcmascriptBuildNodeRuntimeChunk {
210 #[turbo_tasks::function]
211 fn generate_source_map(self: Vc<Self>) -> Vc<FileContent> {
212 self.code().generate_source_map()
213 }
214}