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#[turbo_tasks::value]
16#[derive(Debug, Clone)]
17pub struct ServerPath {
18 pub path: RcStr,
20 pub content_hash: u64,
21}
22
23#[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#[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#[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}