next_api/
paths.rs

1use anyhow::Result;
2use next_core::{all_assets_from_entries, next_manifests::AssetBinding};
3use serde::{Deserialize, Serialize};
4use tracing::Instrument;
5use turbo_rcstr::RcStr;
6use turbo_tasks::{
7    NonLocalValue, ResolvedVc, TryFlatJoinIterExt, TryJoinIterExt, Vc, trace::TraceRawVcs,
8};
9use turbo_tasks_fs::FileSystemPath;
10use turbopack_core::{
11    asset::{Asset, AssetContent},
12    output::{OutputAsset, OutputAssets},
13};
14use turbopack_wasm::wasm_edge_var_name;
15
16/// A reference to a server file with content hash for change detection
17#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, TraceRawVcs, NonLocalValue)]
18pub struct ServerPath {
19    /// Relative to the root_path
20    pub path: String,
21    pub content_hash: u64,
22}
23
24/// A list of server paths
25#[turbo_tasks::value(transparent)]
26pub struct ServerPaths(Vec<ServerPath>);
27
28/// Return a list of all server paths with filename and hash for all output
29/// assets references from the `assets` list. Server paths are identified by
30/// being inside `node_root`.
31#[turbo_tasks::function]
32pub async fn all_server_paths(
33    assets: Vc<OutputAssets>,
34    node_root: Vc<FileSystemPath>,
35) -> Result<Vc<ServerPaths>> {
36    let span = tracing::info_span!("all_server_paths");
37    async move {
38        let all_assets = all_assets_from_entries(assets).await?;
39        let node_root = &node_root.await?;
40        Ok(Vc::cell(
41            all_assets
42                .iter()
43                .map(|&asset| async move {
44                    Ok(
45                        if let Some(path) = node_root.get_path_to(&*asset.path().await?) {
46                            let content_hash = match *asset.content().await? {
47                                AssetContent::File(file) => *file.hash().await?,
48                                AssetContent::Redirect { .. } => 0,
49                            };
50                            Some(ServerPath {
51                                path: path.to_string(),
52                                content_hash,
53                            })
54                        } else {
55                            None
56                        },
57                    )
58                })
59                .try_flat_join()
60                .await?,
61        ))
62    }
63    .instrument(span)
64    .await
65}
66
67/// Return a list of relative paths to `root` for all output assets references
68/// from the `assets` list which are located inside the root path.
69#[turbo_tasks::function]
70pub async fn all_paths_in_root(
71    assets: Vc<OutputAssets>,
72    root: Vc<FileSystemPath>,
73) -> Result<Vc<Vec<RcStr>>> {
74    let all_assets = &*all_assets_from_entries(assets).await?;
75    let root = &*root.await?;
76
77    Ok(Vc::cell(
78        get_paths_from_root(root, all_assets, |_| true).await?,
79    ))
80}
81
82pub(crate) async fn get_paths_from_root(
83    root: &FileSystemPath,
84    output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
85    filter: impl FnOnce(&str) -> bool + Copy,
86) -> Result<Vec<RcStr>> {
87    output_assets
88        .into_iter()
89        .map(move |&file| async move {
90            let path = &*file.path().await?;
91            let Some(relative) = root.get_path_to(path) else {
92                return Ok(None);
93            };
94
95            Ok(if filter(relative) {
96                Some(relative.into())
97            } else {
98                None
99            })
100        })
101        .try_flat_join()
102        .await
103}
104
105pub(crate) async fn get_js_paths_from_root(
106    root: &FileSystemPath,
107    output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
108) -> Result<Vec<RcStr>> {
109    get_paths_from_root(root, output_assets, |path| path.ends_with(".js")).await
110}
111
112pub(crate) async fn get_wasm_paths_from_root(
113    root: &FileSystemPath,
114    output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
115) -> Result<Vec<(RcStr, ResolvedVc<Box<dyn OutputAsset>>)>> {
116    output_assets
117        .into_iter()
118        .map(move |&file| async move {
119            let path = &*file.path().await?;
120            let Some(relative) = root.get_path_to(path) else {
121                return Ok(None);
122            };
123
124            Ok(if relative.ends_with(".wasm") {
125                Some((relative.into(), file))
126            } else {
127                None
128            })
129        })
130        .try_flat_join()
131        .await
132}
133
134pub(crate) async fn get_asset_paths_from_root(
135    root: &FileSystemPath,
136    output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
137) -> Result<Vec<RcStr>> {
138    get_paths_from_root(root, output_assets, |path| {
139        !path.ends_with(".js") && !path.ends_with(".map") && !path.ends_with(".wasm")
140    })
141    .await
142}
143
144pub(crate) async fn get_font_paths_from_root(
145    root: &FileSystemPath,
146    output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
147) -> Result<Vec<RcStr>> {
148    get_paths_from_root(root, output_assets, |path| {
149        path.ends_with(".woff")
150            || path.ends_with(".woff2")
151            || path.ends_with(".eot")
152            || path.ends_with(".ttf")
153            || path.ends_with(".otf")
154    })
155    .await
156}
157
158pub(crate) async fn wasm_paths_to_bindings(
159    paths: impl IntoIterator<Item = (RcStr, ResolvedVc<Box<dyn OutputAsset>>)>,
160) -> Result<Vec<AssetBinding>> {
161    paths
162        .into_iter()
163        .map(async |(path, asset)| {
164            Ok(AssetBinding {
165                name: wasm_edge_var_name(Vc::upcast(*asset)).owned().await?,
166                file_path: path,
167            })
168        })
169        .try_join()
170        .await
171}
172
173pub(crate) fn paths_to_bindings(paths: Vec<RcStr>) -> Vec<AssetBinding> {
174    paths
175        .into_iter()
176        .map(|path| AssetBinding {
177            name: path.clone(),
178            file_path: path,
179        })
180        .collect()
181}