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#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, TraceRawVcs, NonLocalValue)]
18pub struct ServerPath {
19 pub path: String,
21 pub content_hash: u64,
22}
23
24#[turbo_tasks::value(transparent)]
26pub struct ServerPaths(Vec<ServerPath>);
27
28#[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#[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}