next_core/
emit.rs

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