next_api/
paths.rs

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