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: 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.clone();
40 Ok(Vc::cell(
41 all_assets
42 .iter()
43 .map(|&asset| {
44 let node_root = node_root.clone();
45
46 async move {
47 Ok(
48 if let Some(path) = node_root.get_path_to(&*asset.path().await?) {
49 let content_hash = match *asset.content().await? {
50 AssetContent::File(file) => *file.hash().await?,
51 AssetContent::Redirect { .. } => 0,
52 };
53 Some(ServerPath {
54 path: path.to_string(),
55 content_hash,
56 })
57 } else {
58 None
59 },
60 )
61 }
62 })
63 .try_flat_join()
64 .await?,
65 ))
66 }
67 .instrument(span)
68 .await
69}
70
71#[turbo_tasks::function]
74pub async fn all_paths_in_root(
75 assets: Vc<OutputAssets>,
76 root: FileSystemPath,
77) -> Result<Vc<Vec<RcStr>>> {
78 let all_assets = &*all_assets_from_entries(assets).await?;
79
80 Ok(Vc::cell(
81 get_paths_from_root(&root, all_assets, |_| true).await?,
82 ))
83}
84
85pub(crate) async fn get_paths_from_root(
86 root: &FileSystemPath,
87 output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
88 filter: impl FnOnce(&str) -> bool + Copy,
89) -> Result<Vec<RcStr>> {
90 output_assets
91 .into_iter()
92 .map(move |&file| async move {
93 let path = &*file.path().await?;
94 let Some(relative) = root.get_path_to(path) else {
95 return Ok(None);
96 };
97
98 Ok(if filter(relative) {
99 Some(relative.into())
100 } else {
101 None
102 })
103 })
104 .try_flat_join()
105 .await
106}
107
108pub(crate) async fn get_js_paths_from_root(
109 root: &FileSystemPath,
110 output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
111) -> Result<Vec<RcStr>> {
112 get_paths_from_root(root, output_assets, |path| path.ends_with(".js")).await
113}
114
115pub(crate) async fn get_wasm_paths_from_root(
116 root: &FileSystemPath,
117 output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
118) -> Result<Vec<(RcStr, ResolvedVc<Box<dyn OutputAsset>>)>> {
119 output_assets
120 .into_iter()
121 .map(move |&file| async move {
122 let path = &*file.path().await?;
123 let Some(relative) = root.get_path_to(path) else {
124 return Ok(None);
125 };
126
127 Ok(if relative.ends_with(".wasm") {
128 Some((relative.into(), file))
129 } else {
130 None
131 })
132 })
133 .try_flat_join()
134 .await
135}
136
137pub(crate) async fn get_asset_paths_from_root(
138 root: &FileSystemPath,
139 output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
140) -> Result<Vec<RcStr>> {
141 get_paths_from_root(root, output_assets, |path| {
142 !path.ends_with(".js") && !path.ends_with(".map") && !path.ends_with(".wasm")
143 })
144 .await
145}
146
147pub(crate) async fn get_font_paths_from_root(
148 root: &FileSystemPath,
149 output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
150) -> Result<Vec<RcStr>> {
151 get_paths_from_root(root, output_assets, |path| {
152 path.ends_with(".woff")
153 || path.ends_with(".woff2")
154 || path.ends_with(".eot")
155 || path.ends_with(".ttf")
156 || path.ends_with(".otf")
157 })
158 .await
159}
160
161pub(crate) async fn wasm_paths_to_bindings(
162 paths: impl IntoIterator<Item = (RcStr, ResolvedVc<Box<dyn OutputAsset>>)>,
163) -> Result<Vec<AssetBinding>> {
164 paths
165 .into_iter()
166 .map(async |(path, asset)| {
167 Ok(AssetBinding {
168 name: wasm_edge_var_name(Vc::upcast(*asset)).owned().await?,
169 file_path: path,
170 })
171 })
172 .try_join()
173 .await
174}
175
176pub(crate) fn paths_to_bindings(paths: Vec<RcStr>) -> Vec<AssetBinding> {
177 paths
178 .into_iter()
179 .map(|path| AssetBinding {
180 name: path.clone(),
181 file_path: path,
182 })
183 .collect()
184}