next_api/
paths.rs

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