1#![feature(min_specialization)]
2#![feature(arbitrary_self_types)]
3#![feature(arbitrary_self_types_pointers)]
4
5use std::{iter::once, thread::available_parallelism};
6
7use anyhow::{Result, bail};
8pub use node_entry::{NodeEntry, NodeRenderingEntries, NodeRenderingEntry};
9use rustc_hash::FxHashMap;
10use turbo_rcstr::{RcStr, rcstr};
11use turbo_tasks::{
12 FxIndexSet, ResolvedVc, TryJoinIterExt, Vc,
13 graph::{AdjacencyMap, GraphTraversal},
14};
15use turbo_tasks_env::ProcessEnv;
16use turbo_tasks_fs::{File, FileSystemPath, to_sys_path};
17use turbopack_core::{
18 asset::{Asset, AssetContent},
19 changed::content_changed,
20 chunk::{ChunkingContext, ChunkingContextExt, EvaluatableAsset, EvaluatableAssets},
21 module::Module,
22 module_graph::{ModuleGraph, chunk_group_info::ChunkGroupEntry},
23 output::{OutputAsset, OutputAssets, OutputAssetsSet},
24 source_map::GenerateSourceMap,
25 virtual_output::VirtualOutputAsset,
26};
27
28use self::{pool::NodeJsPool, source_map::StructuredError};
29
30pub mod debug;
31pub mod embed_js;
32pub mod evaluate;
33pub mod execution_context;
34mod heap_queue;
35mod node_entry;
36mod pool;
37pub mod render;
38pub mod route_matcher;
39pub mod source_map;
40pub mod transforms;
41
42#[turbo_tasks::function]
43async fn emit(
44 intermediate_asset: Vc<Box<dyn OutputAsset>>,
45 intermediate_output_path: FileSystemPath,
46) -> Result<()> {
47 for asset in internal_assets(intermediate_asset, intermediate_output_path).await? {
48 let _ = asset
49 .content()
50 .write(asset.path().owned().await?)
51 .resolve()
52 .await?;
53 }
54 Ok(())
55}
56
57#[derive(Debug)]
60#[turbo_tasks::value]
61struct SeparatedAssets {
62 internal_assets: ResolvedVc<OutputAssetsSet>,
63 external_asset_entrypoints: ResolvedVc<OutputAssetsSet>,
64}
65
66#[turbo_tasks::function]
70async fn internal_assets(
71 intermediate_asset: ResolvedVc<Box<dyn OutputAsset>>,
72 intermediate_output_path: FileSystemPath,
73) -> Result<Vc<OutputAssetsSet>> {
74 Ok(
75 *separate_assets_operation(intermediate_asset, intermediate_output_path)
76 .read_strongly_consistent()
77 .await?
78 .internal_assets,
79 )
80}
81
82#[turbo_tasks::value(transparent)]
83pub struct AssetsForSourceMapping(FxHashMap<String, ResolvedVc<Box<dyn GenerateSourceMap>>>);
84
85#[turbo_tasks::function]
88async fn internal_assets_for_source_mapping(
89 intermediate_asset: Vc<Box<dyn OutputAsset>>,
90 intermediate_output_path: FileSystemPath,
91) -> Result<Vc<AssetsForSourceMapping>> {
92 let internal_assets =
93 internal_assets(intermediate_asset, intermediate_output_path.clone()).await?;
94 let intermediate_output_path = intermediate_output_path.clone();
95 let mut internal_assets_for_source_mapping = FxHashMap::default();
96 for asset in internal_assets.iter() {
97 if let Some(generate_source_map) =
98 ResolvedVc::try_sidecast::<Box<dyn GenerateSourceMap>>(*asset)
99 && let Some(path) = intermediate_output_path.get_path_to(&*asset.path().await?)
100 {
101 internal_assets_for_source_mapping.insert(path.to_string(), generate_source_map);
102 }
103 }
104 Ok(Vc::cell(internal_assets_for_source_mapping))
105}
106
107#[turbo_tasks::function]
110pub async fn external_asset_entrypoints(
111 module: Vc<Box<dyn EvaluatableAsset>>,
112 runtime_entries: Vc<EvaluatableAssets>,
113 chunking_context: Vc<Box<dyn ChunkingContext>>,
114 intermediate_output_path: FileSystemPath,
115) -> Result<Vc<OutputAssetsSet>> {
116 Ok(*separate_assets_operation(
117 get_intermediate_asset(chunking_context, module, runtime_entries)
118 .to_resolved()
119 .await?,
120 intermediate_output_path,
121 )
122 .read_strongly_consistent()
123 .await?
124 .external_asset_entrypoints)
125}
126
127#[turbo_tasks::function(operation)]
130async fn separate_assets_operation(
131 intermediate_asset: ResolvedVc<Box<dyn OutputAsset>>,
132 intermediate_output_path: FileSystemPath,
133) -> Result<Vc<SeparatedAssets>> {
134 let intermediate_output_path = intermediate_output_path.clone();
135 #[derive(PartialEq, Eq, Hash, Clone, Copy)]
136 enum Type {
137 Internal(ResolvedVc<Box<dyn OutputAsset>>),
138 External(ResolvedVc<Box<dyn OutputAsset>>),
139 }
140 let get_asset_children = |asset| {
141 let intermediate_output_path = intermediate_output_path.clone();
142 async move {
143 let Type::Internal(asset) = asset else {
144 return Ok(Vec::new());
145 };
146 asset
147 .references()
148 .await?
149 .iter()
150 .map(|asset| async {
151 if asset.path().await?.is_inside_ref(&intermediate_output_path) {
156 Ok(Type::Internal(*asset))
157 } else {
158 Ok(Type::External(*asset))
159 }
160 })
161 .try_join()
162 .await
163 }
164 };
165
166 let graph = AdjacencyMap::new()
167 .skip_duplicates()
168 .visit(once(Type::Internal(intermediate_asset)), get_asset_children)
169 .await
170 .completed()?
171 .into_inner();
172
173 let mut internal_assets = FxIndexSet::default();
174 let mut external_asset_entrypoints = FxIndexSet::default();
175
176 for item in graph.into_postorder_topological() {
177 match item {
178 Type::Internal(asset) => {
179 internal_assets.insert(asset);
180 }
181 Type::External(asset) => {
182 external_asset_entrypoints.insert(asset);
183 }
184 }
185 }
186
187 Ok(SeparatedAssets {
188 internal_assets: ResolvedVc::cell(internal_assets),
189 external_asset_entrypoints: ResolvedVc::cell(external_asset_entrypoints),
190 }
191 .cell())
192}
193
194fn emit_package_json(dir: FileSystemPath) -> Result<Vc<()>> {
198 Ok(emit(
199 Vc::upcast(VirtualOutputAsset::new(
200 dir.join("package.json")?,
201 AssetContent::file(File::from("{\"type\": \"commonjs\"}").into()),
202 )),
203 dir,
204 ))
205}
206
207#[turbo_tasks::function(operation)]
209pub async fn get_renderer_pool_operation(
210 cwd: FileSystemPath,
211 env: ResolvedVc<Box<dyn ProcessEnv>>,
212 intermediate_asset: ResolvedVc<Box<dyn OutputAsset>>,
213 intermediate_output_path: FileSystemPath,
214 output_root: FileSystemPath,
215 project_dir: FileSystemPath,
216 debug: bool,
217) -> Result<Vc<NodeJsPool>> {
218 emit_package_json(intermediate_output_path.clone())?.await?;
219
220 emit(*intermediate_asset, output_root.clone())
221 .as_side_effect()
222 .await?;
223 let assets_for_source_mapping =
224 internal_assets_for_source_mapping(*intermediate_asset, output_root.clone());
225
226 let entrypoint = intermediate_asset.path().owned().await?;
227
228 let Some(cwd) = to_sys_path(cwd.clone()).await? else {
229 bail!(
230 "can only render from a disk filesystem, but `cwd = {}`",
231 cwd.value_to_string().await?
232 );
233 };
234 let Some(entrypoint) = to_sys_path(entrypoint.clone()).await? else {
235 bail!(
236 "can only render from a disk filesystem, but `entrypoint = {}`",
237 entrypoint.value_to_string().await?
238 );
239 };
240 content_changed(*ResolvedVc::upcast(intermediate_asset)).await?;
242
243 Ok(NodeJsPool::new(
244 cwd,
245 entrypoint,
246 env.read_all()
247 .await?
248 .iter()
249 .map(|(k, v)| (k.clone(), v.clone()))
250 .collect(),
251 assets_for_source_mapping.to_resolved().await?,
252 output_root,
253 project_dir,
254 available_parallelism().map_or(1, |v| v.get()),
255 debug,
256 )
257 .cell())
258}
259
260#[turbo_tasks::function]
262pub async fn get_intermediate_asset(
263 chunking_context: Vc<Box<dyn ChunkingContext>>,
264 main_entry: ResolvedVc<Box<dyn EvaluatableAsset>>,
265 other_entries: Vc<EvaluatableAssets>,
266) -> Result<Vc<Box<dyn OutputAsset>>> {
267 Ok(Vc::upcast(
268 chunking_context.root_entry_chunk_group_asset(
269 chunking_context
270 .chunk_path(None, main_entry.ident(), None, rcstr!(".js"))
271 .owned()
272 .await?,
273 other_entries.with_entry(*main_entry),
274 ModuleGraph::from_modules(
275 Vc::cell(vec![ChunkGroupEntry::Entry(
276 other_entries
277 .await?
278 .into_iter()
279 .copied()
280 .chain(std::iter::once(main_entry))
281 .map(ResolvedVc::upcast)
282 .collect(),
283 )]),
284 false,
285 ),
286 OutputAssets::empty(),
287 ),
288 ))
289}
290
291#[derive(Clone, Debug)]
292#[turbo_tasks::value(shared)]
293pub struct ResponseHeaders {
294 pub status: u16,
295 pub headers: Vec<(RcStr, RcStr)>,
296}
297
298pub fn register() {
299 turbo_tasks::register();
300 turbo_tasks_bytes::register();
301 turbo_tasks_fs::register();
302 turbopack_dev_server::register();
303 turbopack_ecmascript::register();
304 include!(concat!(env!("OUT_DIR"), "/register.rs"));
305}