1use anyhow::{Context, 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 turbo_tasks_hash::{HashAlgorithm, encode_hex};
8use turbopack_core::{
9 asset::Asset,
10 output::{OutputAsset, OutputAssets},
11 reference::all_assets_from_entries,
12};
13use turbopack_wasm::wasm_edge_var_name;
14
15#[turbo_tasks::value]
17#[derive(Debug, Clone)]
18pub struct AssetPath {
19 pub path: RcStr,
21 pub content_hash: RcStr,
22}
23
24#[turbo_tasks::value(transparent)]
26pub struct AssetPaths(Vec<AssetPath>);
27
28#[turbo_tasks::value(transparent)]
29pub struct OptionAssetPath(Option<AssetPath>);
30
31#[turbo_tasks::function]
32async fn asset_path(
33 asset: Vc<Box<dyn OutputAsset>>,
34 node_root: FileSystemPath,
35 should_content_hash: Option<HashAlgorithm>,
36) -> Result<Vc<OptionAssetPath>> {
37 Ok(Vc::cell(
38 if let Some(path) = node_root.get_path_to(&*asset.path().await?) {
39 let hash = if let Some(algorithm) = should_content_hash {
40 asset
41 .content()
42 .content_hash(algorithm)
43 .owned()
44 .await?
45 .context("asset content not found")?
46 } else {
47 encode_hex(*asset.content().hash().await?).into()
48 };
49 Some(AssetPath {
50 path: RcStr::from(path),
51 content_hash: hash,
52 })
53 } else {
54 None
55 },
56 ))
57}
58
59#[turbo_tasks::function]
62pub async fn all_asset_paths(
63 assets: Vc<OutputAssets>,
64 node_root: FileSystemPath,
65 should_content_hash: Option<HashAlgorithm>,
66) -> Result<Vc<AssetPaths>> {
67 let span = tracing::info_span!(
68 "collect all asset paths",
69 assets_count = tracing::field::Empty,
70 asset_paths_count = tracing::field::Empty
71 );
72 let span_clone = span.clone();
73 async move {
74 let all_assets = all_assets_from_entries(assets).await?;
75 span.record("assets_count", all_assets.len());
76 let asset_paths = all_assets
77 .iter()
78 .map(|&asset| asset_path(*asset, node_root.clone(), should_content_hash).owned())
79 .try_flat_join()
80 .await?;
81 span.record("asset_paths_count", asset_paths.len());
82 Ok(Vc::cell(asset_paths))
83 }
84 .instrument(span_clone)
85 .await
86}
87
88#[turbo_tasks::function]
91pub async fn all_paths_in_root(
92 assets: Vc<OutputAssets>,
93 root: FileSystemPath,
94) -> Result<Vc<Vec<RcStr>>> {
95 let all_assets = &*all_assets_from_entries(assets).await?;
96
97 Ok(Vc::cell(
98 get_paths_from_root(&root, all_assets, |_| true).await?,
99 ))
100}
101
102pub(crate) async fn get_paths_from_root(
103 root: &FileSystemPath,
104 output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
105 filter: impl FnOnce(&str) -> bool + Copy,
106) -> Result<Vec<RcStr>> {
107 output_assets
108 .into_iter()
109 .map(move |&file| async move {
110 let path = &*file.path().await?;
111 let Some(relative) = root.get_path_to(path) else {
112 return Ok(None);
113 };
114
115 Ok(if filter(relative) {
116 Some(relative.into())
117 } else {
118 None
119 })
120 })
121 .try_flat_join()
122 .await
123}
124
125pub(crate) async fn get_js_paths_from_root(
126 root: &FileSystemPath,
127 output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
128) -> Result<Vec<RcStr>> {
129 get_paths_from_root(root, output_assets, |path| path.ends_with(".js")).await
130}
131
132pub(crate) async fn get_wasm_paths_from_root(
133 root: &FileSystemPath,
134 output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
135) -> Result<Vec<(RcStr, ResolvedVc<Box<dyn OutputAsset>>)>> {
136 output_assets
137 .into_iter()
138 .map(move |&file| async move {
139 let path = &*file.path().await?;
140 let Some(relative) = root.get_path_to(path) else {
141 return Ok(None);
142 };
143
144 Ok(if relative.ends_with(".wasm") {
145 Some((relative.into(), file))
146 } else {
147 None
148 })
149 })
150 .try_flat_join()
151 .await
152}
153
154pub(crate) async fn get_asset_paths_from_root(
155 root: &FileSystemPath,
156 output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
157) -> Result<Vec<RcStr>> {
158 get_paths_from_root(root, output_assets, |path| {
159 !path.ends_with(".js") && !path.ends_with(".map") && !path.ends_with(".wasm")
160 })
161 .await
162}
163
164pub(crate) async fn get_font_paths_from_root(
165 root: &FileSystemPath,
166 output_assets: impl IntoIterator<Item = &ResolvedVc<Box<dyn OutputAsset>>>,
167) -> Result<Vec<RcStr>> {
168 get_paths_from_root(root, output_assets, |path| {
169 path.ends_with(".woff")
170 || path.ends_with(".woff2")
171 || path.ends_with(".eot")
172 || path.ends_with(".ttf")
173 || path.ends_with(".otf")
174 })
175 .await
176}
177
178pub(crate) async fn wasm_paths_to_bindings(
179 paths: impl IntoIterator<Item = (RcStr, ResolvedVc<Box<dyn OutputAsset>>)>,
180) -> Result<Vec<AssetBinding>> {
181 paths
182 .into_iter()
183 .map(async |(path, asset)| {
184 Ok(AssetBinding {
185 name: wasm_edge_var_name(Vc::upcast(*asset)).owned().await?,
186 file_path: path,
187 })
188 })
189 .try_join()
190 .await
191}
192
193pub(crate) fn paths_to_bindings(paths: Vec<RcStr>) -> Vec<AssetBinding> {
194 paths
195 .into_iter()
196 .map(|path| AssetBinding {
197 name: path.clone(),
198 file_path: path,
199 })
200 .collect()
201}