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