Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ serde_json = "1.0.138"
serde_path_to_error = "0.1.16"
serde_qs = "0.13.0"
serde_with = "3.12.0"
sha2 = "0.10.2"
smallvec = { version = "1.15.1", features = [
"serde",
"const_generics",
Expand Down
28 changes: 9 additions & 19 deletions crates/next-api/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ use crate::{
module_graph::{ClientReferencesGraphs, NextDynamicGraphs, ServerActionsGraphs},
nft_json::NftJsonAsset,
paths::{
all_paths_in_root, all_server_paths, get_asset_paths_from_root, get_js_paths_from_root,
all_asset_paths, all_paths_in_root, get_asset_paths_from_root, get_js_paths_from_root,
get_wasm_paths_from_root, paths_to_bindings, wasm_paths_to_bindings,
},
project::{BaseAndFullModuleGraph, Project},
Expand Down Expand Up @@ -2007,24 +2007,14 @@ impl Endpoint for AppEndpoint {

async move {
let output = self.output();
let output_assets = output.output_assets();
let output = output.await?;
let node_root = &*this.app_project.project().node_root().await?;
let project = this.app_project.project();
let node_root = project.node_root().owned().await?;
let client_relative_root = project.client_relative_path().owned().await?;

let (server_paths, client_paths) = if this
.app_project
.project()
.next_mode()
.await?
.is_development()
{
let node_root = this.app_project.project().node_root().owned().await?;
let server_paths = all_server_paths(output_assets, node_root).owned().await?;
let output_assets = output.output_assets();

let client_relative_root = this
.app_project
.project()
.client_relative_path()
let (server_paths, client_paths) = if project.next_mode().await?.is_development() {
let server_paths = all_asset_paths(output_assets, node_root.clone(), None)
.owned()
.await?;
let client_paths = all_paths_in_root(output_assets, client_relative_root)
Expand All @@ -2035,7 +2025,7 @@ impl Endpoint for AppEndpoint {
(vec![], vec![])
};

let written_endpoint = match *output {
let written_endpoint = match *output.await? {
AppEndpointOutput::NodeJs { rsc_chunk, .. } => EndpointOutputPaths::NodeJs {
server_entry_path: node_root
.get_path_to(&*rsc_chunk.path().await?)
Expand All @@ -2054,7 +2044,7 @@ impl Endpoint for AppEndpoint {
EndpointOutput {
output_assets: output_assets.to_resolved().await?,
output_paths: written_endpoint.resolved_cell(),
project: this.app_project.project().to_resolved().await?,
project: project.to_resolved().await?,
}
.cell(),
)
Expand Down
6 changes: 4 additions & 2 deletions crates/next-api/src/instrumentation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use turbopack_core::{
use crate::{
nft_json::NftJsonAsset,
paths::{
all_server_paths, get_js_paths_from_root, get_wasm_paths_from_root, wasm_paths_to_bindings,
all_asset_paths, get_js_paths_from_root, get_wasm_paths_from_root, wasm_paths_to_bindings,
},
project::Project,
route::{Endpoint, EndpointOutput, EndpointOutputPaths, ModuleGraphs},
Expand Down Expand Up @@ -210,7 +210,9 @@ impl Endpoint for InstrumentationEndpoint {

let server_paths = if this.project.next_mode().await?.is_development() {
let node_root = this.project.node_root().owned().await?;
all_server_paths(output_assets, node_root).owned().await?
all_asset_paths(output_assets, node_root, None)
.owned()
.await?
} else {
vec![]
};
Expand Down
6 changes: 4 additions & 2 deletions crates/next-api/src/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use turbopack_core::{
use crate::{
nft_json::NftJsonAsset,
paths::{
all_paths_in_root, all_server_paths, get_asset_paths_from_root, get_js_paths_from_root,
all_asset_paths, all_paths_in_root, get_asset_paths_from_root, get_js_paths_from_root,
get_wasm_paths_from_root, paths_to_bindings, wasm_paths_to_bindings,
},
project::Project,
Expand Down Expand Up @@ -337,7 +337,9 @@ impl Endpoint for MiddlewareEndpoint {

let (server_paths, client_paths) = if this.project.next_mode().await?.is_development() {
let node_root = this.project.node_root().owned().await?;
let server_paths = all_server_paths(output_assets, node_root).owned().await?;
let server_paths = all_asset_paths(output_assets, node_root, None)
.owned()
.await?;

// Middleware could in theory have a client path (e.g. `new URL`).
let client_relative_root = this.project.client_relative_path().owned().await?;
Expand Down
32 changes: 10 additions & 22 deletions crates/next-api/src/pages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ use crate::{
module_graph::{NextDynamicGraphs, validate_pages_css_imports},
nft_json::NftJsonAsset,
paths::{
all_paths_in_root, all_server_paths, get_asset_paths_from_root, get_js_paths_from_root,
all_asset_paths, all_paths_in_root, get_asset_paths_from_root, get_js_paths_from_root,
get_wasm_paths_from_root, paths_to_bindings, wasm_paths_to_bindings,
},
project::Project,
Expand Down Expand Up @@ -1601,26 +1601,15 @@ impl Endpoint for PageEndpoint {
}
};
async move {
let output = self.output().await?;
let output_assets = self.output().output_assets();
let output = self.output();
let project = this.pages_project.project();
let node_root = project.node_root().owned().await?;
let client_relative_root = project.client_relative_path().owned().await?;

let node_root = this.pages_project.project().node_root().owned().await?;
let output_assets = output.output_assets();

let (server_paths, client_paths) = if this
.pages_project
.project()
.next_mode()
.await?
.is_development()
{
let server_paths = all_server_paths(output_assets, node_root.clone())
.owned()
.await?;

let client_relative_root = this
.pages_project
.project()
.client_relative_path()
let (server_paths, client_paths) = if project.next_mode().await?.is_development() {
let server_paths = all_asset_paths(output_assets, node_root.clone(), None)
.owned()
.await?;
let client_paths = all_paths_in_root(output_assets, client_relative_root)
Expand All @@ -1631,8 +1620,7 @@ impl Endpoint for PageEndpoint {
(vec![], vec![])
};

let node_root = node_root.clone();
let written_endpoint = match *output {
let written_endpoint = match *output.await? {
PageEndpointOutput::NodeJs { entry_chunk, .. } => {
// Only set server_entry_path if pages should be created
let pages_structure = this.pages_structure.await?;
Expand Down Expand Up @@ -1661,7 +1649,7 @@ impl Endpoint for PageEndpoint {
EndpointOutput {
output_assets: output_assets.to_resolved().await?,
output_paths: written_endpoint.resolved_cell(),
project: this.pages_project.project().to_resolved().await?,
project: project.to_resolved().await?,
}
.cell(),
)
Expand Down
57 changes: 34 additions & 23 deletions crates/next-api/src/paths.rs
Original file line number Diff line number Diff line change
@@ -1,74 +1,85 @@
use anyhow::Result;
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, encode_hex};
use turbopack_core::{
asset::Asset,
output::{OutputAsset, OutputAssets},
reference::all_assets_from_entries,
};
use turbopack_wasm::wasm_edge_var_name;

/// A reference to a server file with content hash for change detection
/// A reference to an output asset with content hash for change detection
#[turbo_tasks::value]
#[derive(Debug, Clone)]
pub struct ServerPath {
pub struct AssetPath {
/// Relative to the root_path
pub path: RcStr,
pub content_hash: u64,
pub content_hash: RcStr,
}

/// A list of server paths
/// A list of asset paths
#[turbo_tasks::value(transparent)]
pub struct ServerPaths(Vec<ServerPath>);
pub struct AssetPaths(Vec<AssetPath>);

#[turbo_tasks::value(transparent)]
pub struct OptionServerPath(Option<ServerPath>);
pub struct OptionAssetPath(Option<AssetPath>);

#[turbo_tasks::function]
async fn server_path(
async fn asset_path(
asset: Vc<Box<dyn OutputAsset>>,
node_root: FileSystemPath,
) -> Result<Vc<OptionServerPath>> {
should_content_hash: Option<HashAlgorithm>,
) -> Result<Vc<OptionAssetPath>> {
Ok(Vc::cell(
if let Some(path) = node_root.get_path_to(&*asset.path().await?) {
let content_hash = *asset.content().hash().await?;
Some(ServerPath {
let hash = if let Some(algorithm) = should_content_hash {
asset
.content()
.content_hash(algorithm)
.owned()
.await?
.context("asset content not found")?
} else {
encode_hex(*asset.content().hash().await?).into()
};
Some(AssetPath {
path: RcStr::from(path),
content_hash,
content_hash: hash,
})
} else {
None
},
))
}

/// Return a list of all server paths with filename and hash for all output
/// assets references from the `assets` list. Server paths are identified by
/// being inside `node_root`.
/// 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_server_paths(
pub async fn all_asset_paths(
assets: Vc<OutputAssets>,
node_root: FileSystemPath,
) -> Result<Vc<ServerPaths>> {
should_content_hash: Option<HashAlgorithm>,
) -> Result<Vc<AssetPaths>> {
let span = tracing::info_span!(
"collect all server paths",
"collect all asset paths",
assets_count = tracing::field::Empty,
server_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 server_paths = all_assets
let asset_paths = all_assets
.iter()
.map(|&asset| server_path(*asset, node_root.clone()).owned())
.map(|&asset| asset_path(*asset, node_root.clone(), should_content_hash).owned())
.try_flat_join()
.await?;
span.record("server_assets_count", server_paths.len());
Ok(Vc::cell(server_paths))
span.record("asset_paths_count", asset_paths.len());
Ok(Vc::cell(asset_paths))
}
.instrument(span_clone)
.await
Expand Down
6 changes: 3 additions & 3 deletions crates/next-api/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use turbopack_core::{
output::OutputAssets,
};

use crate::{operation::OptionEndpoint, paths::ServerPath, project::Project};
use crate::{operation::OptionEndpoint, paths::AssetPath, project::Project};

#[derive(
TraceRawVcs, PartialEq, Eq, ValueDebugFormat, Clone, Debug, NonLocalValue, Encode, Decode,
Expand Down Expand Up @@ -267,11 +267,11 @@ pub enum EndpointOutputPaths {
NodeJs {
/// Relative to the root_path
server_entry_path: RcStr,
server_paths: Vec<ServerPath>,
server_paths: Vec<AssetPath>,
client_paths: Vec<RcStr>,
},
Edge {
server_paths: Vec<ServerPath>,
server_paths: Vec<AssetPath>,
client_paths: Vec<RcStr>,
},
NotFound,
Expand Down
27 changes: 9 additions & 18 deletions crates/next-core/src/next_app/metadata/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use indoc::formatdoc;
use turbo_rcstr::RcStr;
use turbo_tasks::{ResolvedVc, Vc};
use turbo_tasks_fs::{File, FileContent, FileSystemPath};
use turbo_tasks_hash::HashAlgorithm;
use turbopack_core::{
asset::AssetContent,
context::AssetContext,
Expand All @@ -33,13 +34,8 @@ async fn dynamic_image_metadata_with_generator_source(
let stem = stem.unwrap_or_default();
let ext = path.extension();

let hash_query = format!(
"?{:x}",
path.read()
.content_hash()
.await?
.context("metadata file not found")?
);
let hash = path.read().content_hash(HashAlgorithm::default()).await?;
let hash = hash.as_ref().context("metadata file not found")?;

let use_numeric_sizes = ty == "twitter" || ty == "openGraph";
let sizes = if use_numeric_sizes {
Expand Down Expand Up @@ -70,7 +66,7 @@ async fn dynamic_image_metadata_with_generator_source(
const data = {{
alt: imageMetadata.alt,
type: imageMetadata.contentType || 'image/png',
url: imageUrl + (idParam ? ('/' + idParam) : '') + {hash_query},
url: imageUrl + (idParam ? ('/' + idParam) : '') + '?' + {hash},
}}
const {{ size }} = imageMetadata
if (size) {{
Expand All @@ -91,7 +87,7 @@ async fn dynamic_image_metadata_with_generator_source(
pathname_prefix = StringifyJs(&page.to_string()),
page_segment = StringifyJs(stem),
sizes = sizes,
hash_query = StringifyJs(&hash_query),
hash = StringifyJs(&hash),
};

let file = File::from(code);
Expand All @@ -113,13 +109,8 @@ async fn dynamic_image_metadata_without_generator_source(
let stem = stem.unwrap_or_default();
let ext = path.extension();

let hash_query = format!(
"?{:x}",
path.read()
.content_hash()
.await?
.context("metadata file not found")?
);
let hash = path.read().content_hash(HashAlgorithm::default()).await?;
let hash = hash.as_ref().context("metadata file not found")?;

let use_numeric_sizes = ty == "twitter" || ty == "openGraph";
let sizes = if use_numeric_sizes {
Expand Down Expand Up @@ -148,7 +139,7 @@ async fn dynamic_image_metadata_without_generator_source(
const data = {{
alt: imageMetadata.alt,
type: imageMetadata.contentType || 'image/png',
url: imageUrl + (idParam ? ('/' + idParam) : '') + {hash_query},
url: imageUrl + (idParam ? ('/' + idParam) : '') + '?' + {hash},
}}
const {{ size }} = imageMetadata
if (size) {{
Expand All @@ -165,7 +156,7 @@ async fn dynamic_image_metadata_without_generator_source(
pathname_prefix = StringifyJs(&page.to_string()),
page_segment = StringifyJs(stem),
sizes = sizes,
hash_query = StringifyJs(&hash_query),
hash = StringifyJs(&hash),
};

let file = File::from(code);
Expand Down
Loading
Loading