/
githubmirror
/
next.js
Обзор
Документация
Войти
/
githubmirror
/
next.js
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
canary
crates/next-api/src/paths.rs
205 строк
6 KB
Niklas Mischkulnig
Respect NEXT_HASH_SALT for server side assetsHashes (#95738)
15 июл 2026, 12:28
Не верифицирован
15 июл 2026, 12:28
b3480cf
Код
Авторство
О чём код?
use anyhow::{Context, Result}; use next_core::next_manifests::AssetBinding; use tracing::Instrument; use turbo_rcstr::RcStr; use turbo_tasks::{ResolvedVc, TryFlatJoinIterExt, TryJoinIterExt, Vc}; use turbo_tasks_fs::FileSystemPath; use turbo_tasks_hash::HashAlgorithm; use turbopack_core::{ asset::{Asset, no_hash_salt}, output::{OutputAsset, OutputAssets}, reference::all_assets_from_entries, }; use turbopack_wasm::wasm_edge_var_name; /// A reference to an output asset with content hash for change detection #[turbo_tasks::value] #[derive(Debug, Clone)] pub struct AssetPath { /// Relative to the root_path pub path: RcStr, pub content_hash: RcStr, } /// A list of asset paths #[turbo_tasks::value(transparent)] pub struct AssetPaths(Vec<AssetPath>); #[turbo_tasks::value(transparent)] pub struct OptionAssetPath(Option<AssetPath>); #[turbo_tasks::function] async fn asset_path( asset: Vc<Box<dyn OutputAsset>>, node_root: FileSystemPath, should_content_hash: Option<HashAlgorithm>, ) -> Result<Vc<OptionAssetPath>> { Ok(Vc::cell( if let Some(path) = node_root.get_path_to(&*asset.path().await?) { let hash = if let Some(algorithm) = should_content_hash { asset .content() .content_hash(no_hash_salt(), algorithm) .owned() .await? .context("asset content not found")? } else { asset .content() .hash(no_hash_salt(), HashAlgorithm::Xxh3Hash128Hex) .owned() .await? }; Some(AssetPath { path: RcStr::from(path), content_hash: hash, }) } else { None }, )) } /// Return a list of all asset paths with filename and hash for all output /// assets references from the `assets` list. Only paths inside `node_root` are included. #[turbo_tasks::function] pub async fn all_asset_paths( assets: Vc<OutputAssets>, node_root: FileSystemPath, should_content_hash: Option<HashAlgorithm>, ) -> Result<Vc<AssetPaths>> { let span = tracing::info_span!( "collect all asset paths", assets_count = tracing::field::Empty, asset_paths_count = tracing::field::Empty ); let span_clone = span.clone(); async move { let all_assets = all_assets_from_entries(assets).await?; span.record("assets_count", all_assets.len()); let asset_paths = all_assets .iter() .map(|&asset| asset_path(*asset, node_root.clone(), should_content_hash).owned()) .try_flat_join() .await?; span.record("asset_paths_count", asset_paths.len()); Ok(Vc::cell(asset_paths)) } .instrument(span_clone) .await } /// Return a list of relative paths to `root` for all output assets references /// from the `assets` list which are located inside the root path. #[turbo_tasks::function] pub async fn all_paths_in_root( assets: Vc<OutputAssets>, root: FileSystemPath, ) -> Result<Vc<Vec<RcStr>>> { let all_assets = all_assets_from_entries(assets).await?; Ok(Vc::cell( get_paths_from_root(&root, all_assets, |_| true).await?, )) } pub(crate) async fn get_paths_from_root( root: &FileSystemPath, output_assets: impl IntoIterator<Item = ResolvedVc<Box<dyn OutputAsset>>>, filter: impl FnOnce(&str) -> bool + Copy, ) -> Result<Vec<RcStr>> { output_assets .into_iter() .map(move |file| async move { let path = &*file.path().await?; let Some(relative) = root.get_path_to(path) else { return Ok(None); }; Ok(if filter(relative) { Some(relative.into()) } else { None }) }) .try_flat_join() .await } pub(crate) async fn get_js_paths_from_root( root: &FileSystemPath, output_assets: impl IntoIterator<Item = ResolvedVc<Box<dyn OutputAsset>>>, ) -> Result<Vec<RcStr>> { get_paths_from_root(root, output_assets, |path| path.ends_with(".js")).await } pub(crate) async fn get_wasm_paths_from_root( root: &FileSystemPath, output_assets: impl IntoIterator<Item = ResolvedVc<Box<dyn OutputAsset>>>, ) -> Result<Vec<(RcStr, ResolvedVc<Box<dyn OutputAsset>>)>> { output_assets .into_iter() .map(move |file| async move { let path = &*file.path().await?; let Some(relative) = root.get_path_to(path) else { return Ok(None); }; Ok(if relative.ends_with(".wasm") { Some((relative.into(), file)) } else { None }) }) .try_flat_join() .await } pub(crate) async fn get_asset_paths_from_root( root: &FileSystemPath, output_assets: impl IntoIterator<Item = ResolvedVc<Box<dyn OutputAsset>>>, ) -> Result<Vec<RcStr>> { get_paths_from_root(root, output_assets, |path| { !path.ends_with(".js") && !path.ends_with(".map") && !path.ends_with(".wasm") }) .await } pub(crate) async fn get_font_paths_from_root( root: &FileSystemPath, output_assets: impl IntoIterator<Item = ResolvedVc<Box<dyn OutputAsset>>>, ) -> Result<Vec<RcStr>> { get_paths_from_root(root, output_assets, |path| { path.ends_with(".woff") || path.ends_with(".woff2") || path.ends_with(".eot") || path.ends_with(".ttf") || path.ends_with(".otf") }) .await } pub(crate) async fn wasm_paths_to_bindings( paths: impl IntoIterator<Item = (RcStr, ResolvedVc<Box<dyn OutputAsset>>)>, ) -> Result<Vec<AssetBinding>> { paths .into_iter() .map(async |(path, asset)| { Ok(AssetBinding { name: wasm_edge_var_name(Vc::upcast(*asset)).owned().await?, file_path: path, }) }) .try_join() .await } pub(crate) fn paths_to_bindings(paths: Vec<RcStr>) -> Vec<AssetBinding> { paths .into_iter() .map(|path| AssetBinding { name: path.clone(), file_path: path, }) .collect() }