next_core/
emit.rs

1use anyhow::Result;
2use rustc_hash::FxHashSet;
3use tracing::Instrument;
4use turbo_tasks::{
5    ResolvedVc, TryFlatJoinIterExt, ValueToString, Vc,
6    graph::{AdjacencyMap, GraphTraversal},
7};
8use turbo_tasks_fs::{FileSystemPath, rebase};
9use turbopack_core::{
10    asset::Asset,
11    output::{OutputAsset, OutputAssets},
12};
13
14/// Emits all assets transitively reachable from the given chunks, that are
15/// inside the node root or the client root.
16///
17/// Assets inside the given client root are rebased to the given client output
18/// path.
19#[turbo_tasks::function]
20pub async fn emit_all_assets(
21    assets: Vc<OutputAssets>,
22    node_root: Vc<FileSystemPath>,
23    client_relative_path: Vc<FileSystemPath>,
24    client_output_path: Vc<FileSystemPath>,
25) -> Result<()> {
26    let _ = emit_assets(
27        all_assets_from_entries(assets),
28        node_root,
29        client_relative_path,
30        client_output_path,
31    )
32    .resolve()
33    .await?;
34    Ok(())
35}
36
37/// Emits all assets transitively reachable from the given chunks, that are
38/// inside the node root or the client root.
39///
40/// Assets inside the given client root are rebased to the given client output
41/// path.
42#[turbo_tasks::function]
43pub async fn emit_assets(
44    assets: Vc<OutputAssets>,
45    node_root: Vc<FileSystemPath>,
46    client_relative_path: Vc<FileSystemPath>,
47    client_output_path: Vc<FileSystemPath>,
48) -> Result<()> {
49    let _: Vec<Vc<()>> = assets
50        .await?
51        .iter()
52        .copied()
53        .map(|asset| async move {
54            let path = asset.path();
55            let span = tracing::info_span!("emit asset", name = %path.to_string().await?);
56            async move {
57                let path = path.await?;
58                Ok(if path.is_inside_ref(&*node_root.await?) {
59                    Some(emit(*asset))
60                } else if path.is_inside_ref(&*client_relative_path.await?) {
61                    // Client assets are emitted to the client output path, which is prefixed
62                    // with _next. We need to rebase them to remove that
63                    // prefix.
64                    Some(emit_rebase(
65                        *asset,
66                        client_relative_path,
67                        client_output_path,
68                    ))
69                } else {
70                    None
71                })
72            }
73            .instrument(span)
74            .await
75        })
76        .try_flat_join()
77        .await?;
78    Ok(())
79}
80
81#[turbo_tasks::function]
82async fn emit(asset: Vc<Box<dyn OutputAsset>>) -> Result<()> {
83    let _ = asset.content().write(asset.path()).resolve().await?;
84    Ok(())
85}
86
87#[turbo_tasks::function]
88async fn emit_rebase(
89    asset: Vc<Box<dyn OutputAsset>>,
90    from: Vc<FileSystemPath>,
91    to: Vc<FileSystemPath>,
92) -> Result<()> {
93    let path = rebase(asset.path(), from, to);
94    let content = asset.content();
95    let _ = content
96        .resolve()
97        .await?
98        .write(path.resolve().await?)
99        .resolve()
100        .await?;
101    Ok(())
102}
103
104/// Walks the asset graph from multiple assets and collect all referenced
105/// assets.
106#[turbo_tasks::function]
107pub async fn all_assets_from_entries(entries: Vc<OutputAssets>) -> Result<Vc<OutputAssets>> {
108    Ok(Vc::cell(
109        AdjacencyMap::new()
110            .skip_duplicates()
111            .visit(entries.await?.iter().copied(), get_referenced_assets)
112            .await
113            .completed()?
114            .into_inner()
115            .into_postorder_topological()
116            .collect::<FxHashSet<_>>()
117            .into_iter()
118            .collect(),
119    ))
120}
121
122/// Computes the list of all chunk children of a given chunk.
123async fn get_referenced_assets(
124    asset: ResolvedVc<Box<dyn OutputAsset>>,
125) -> Result<impl Iterator<Item = ResolvedVc<Box<dyn OutputAsset>>> + Send> {
126    Ok(asset
127        .references()
128        .await?
129        .iter()
130        .copied()
131        .collect::<Vec<_>>()
132        .into_iter())
133}