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: 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.clone();
40        Ok(Vc::cell(
41            all_assets
42                .iter()
43                .map(|&asset| {
44                    let node_root = node_root.clone();
45
46                    async move {
47                        Ok(
48                            if let Some(path) = node_root.get_path_to(&*asset.path().await?) {
49                                let content_hash = match *asset.content().await? {
50                                    AssetContent::File(file) => *file.hash().await?,
51                                    AssetContent::Redirect { .. } => 0,
52                                };
53                                Some(ServerPath {
54                                    path: path.to_string(),
55                                    content_hash,
56                                })
57                            } else {
58                                None
59                            },
60                        )
61                    }
62                })
63                .try_flat_join()
64                .await?,
65        ))
66    }
67    .instrument(span)
68    .await
69}
70
71/// Return a list of relative paths to `root` for all output assets references
72/// from the `assets` list which are located inside the root path.
73#[turbo_tasks::function]
74pub async fn all_paths_in_root(
75    assets: Vc<OutputAssets>,
76    root: FileSystemPath,
77) -> Result<Vc<Vec<RcStr>>> {
78    let all_assets = &*all_assets_from_entries(assets).await?;
79
80    Ok(Vc::cell(
81        get_paths_from_root(&root, all_assets, |_| true).await?,
82    ))
83}
84
85pub(crate) async fn get_paths_from_root(
86    root: &FileSystemPath,
87    output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
88    filter: impl FnOnce(&str) -> bool + Copy,
89) -> Result<Vec<RcStr>> {
90    output_assets
91        .into_iter()
92        .map(move |&file| async move {
93            let path = &*file.path().await?;
94            let Some(relative) = root.get_path_to(path) else {
95                return Ok(None);
96            };
97
98            Ok(if filter(relative) {
99                Some(relative.into())
100            } else {
101                None
102            })
103        })
104        .try_flat_join()
105        .await
106}
107
108pub(crate) async fn get_js_paths_from_root(
109    root: &FileSystemPath,
110    output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
111) -> Result<Vec<RcStr>> {
112    get_paths_from_root(root, output_assets, |path| path.ends_with(".js")).await
113}
114
115pub(crate) async fn get_wasm_paths_from_root(
116    root: &FileSystemPath,
117    output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
118) -> Result<Vec<(RcStr, ResolvedVc<Box<dyn OutputAsset>>)>> {
119    output_assets
120        .into_iter()
121        .map(move |&file| async move {
122            let path = &*file.path().await?;
123            let Some(relative) = root.get_path_to(path) else {
124                return Ok(None);
125            };
126
127            Ok(if relative.ends_with(".wasm") {
128                Some((relative.into(), file))
129            } else {
130                None
131            })
132        })
133        .try_flat_join()
134        .await
135}
136
137pub(crate) async fn get_asset_paths_from_root(
138    root: &FileSystemPath,
139    output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
140) -> Result<Vec<RcStr>> {
141    get_paths_from_root(root, output_assets, |path| {
142        !path.ends_with(".js") && !path.ends_with(".map") && !path.ends_with(".wasm")
143    })
144    .await
145}
146
147pub(crate) async fn get_font_paths_from_root(
148    root: &FileSystemPath,
149    output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
150) -> Result<Vec<RcStr>> {
151    get_paths_from_root(root, output_assets, |path| {
152        path.ends_with(".woff")
153            || path.ends_with(".woff2")
154            || path.ends_with(".eot")
155            || path.ends_with(".ttf")
156            || path.ends_with(".otf")
157    })
158    .await
159}
160
161pub(crate) async fn wasm_paths_to_bindings(
162    paths: impl IntoIterator<Item = (RcStr, ResolvedVc<Box<dyn OutputAsset>>)>,
163) -> Result<Vec<AssetBinding>> {
164    paths
165        .into_iter()
166        .map(async |(path, asset)| {
167            Ok(AssetBinding {
168                name: wasm_edge_var_name(Vc::upcast(*asset)).owned().await?,
169                file_path: path,
170            })
171        })
172        .try_join()
173        .await
174}
175
176pub(crate) fn paths_to_bindings(paths: Vec<RcStr>) -> Vec<AssetBinding> {
177    paths
178        .into_iter()
179        .map(|path| AssetBinding {
180            name: path.clone(),
181            file_path: path,
182        })
183        .collect()
184}