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