Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Turbopack: Add __turbopack_load_by_url__ #76814

Merged
merged 24 commits into from
Mar 6, 2025
Merged
Show file tree
Hide file tree
Changes from 23 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
1 change: 1 addition & 0 deletions crates/next-api/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,7 @@ impl Project {
self.client_relative_path(),
Vc::cell("/ROOT".into()),
self.next_config().computed_asset_prefix(),
self.next_config().chunk_suffix_path(),
self.client_compile_time_info().environment(),
self.next_mode(),
self.module_id_strategy(),
Expand Down
2 changes: 2 additions & 0 deletions crates/next-core/src/next_client/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ pub async fn get_client_chunking_context(
client_root: ResolvedVc<FileSystemPath>,
client_root_to_root_path: ResolvedVc<RcStr>,
asset_prefix: ResolvedVc<Option<RcStr>>,
chunk_suffix_path: ResolvedVc<Option<RcStr>>,
environment: ResolvedVc<Environment>,
mode: Vc<NextMode>,
module_id_strategy: ResolvedVc<Box<dyn ModuleIdStrategy>>,
Expand All @@ -437,6 +438,7 @@ pub async fn get_client_chunking_context(
next_mode.runtime_type(),
)
.chunk_base_path(asset_prefix)
.chunk_suffix_path(chunk_suffix_path)
.minify_type(if *minify.await? {
MinifyType::Minify {
mangle: !*no_mangling.await?,
Expand Down
15 changes: 12 additions & 3 deletions crates/next-core/src/next_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub struct NextConfig {
pub transpile_packages: Option<Vec<RcStr>>,
pub modularize_imports: Option<FxIndexMap<String, ModularizeImportPackageConfig>>,
pub dist_dir: Option<RcStr>,
pub deployment_id: Option<RcStr>,
sass_options: Option<serde_json::Value>,
pub trailing_slash: Option<bool>,
pub asset_prefix: Option<RcStr>,
Expand Down Expand Up @@ -670,9 +671,6 @@ pub struct ExperimentalConfig {
turbo: Option<ExperimentalTurboConfig>,
external_middleware_rewrites_resolve: Option<bool>,
scroll_restoration: Option<bool>,
use_deployment_id: Option<bool>,
use_deployment_id_server_actions: Option<bool>,
deployment_id: Option<RcStr>,
manual_client_base_path: Option<bool>,
optimistic_client_cache: Option<bool>,
middleware_prefetch: Option<MiddlewarePrefetchType>,
Expand Down Expand Up @@ -1393,6 +1391,17 @@ impl NextConfig {
)))
}

/// Returns the suffix to use for chunk loading.
#[turbo_tasks::function]
pub async fn chunk_suffix_path(self: Vc<Self>) -> Result<Vc<Option<RcStr>>> {
let this = self.await?;

match &this.deployment_id {
Some(deployment_id) => Ok(Vc::cell(Some(format!("?dpl={}", deployment_id).into()))),
None => Ok(Vc::cell(None)),
}
}

#[turbo_tasks::function]
pub fn enable_ppr(&self) -> Vc<bool> {
Vc::cell(
Expand Down
12 changes: 11 additions & 1 deletion crates/next-core/src/next_manifests/client_reference_manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,19 @@ impl ClientReferenceManifest {
async move {
let mut entry_manifest: ClientReferenceManifest = Default::default();
let mut references = FxIndexSet::default();
entry_manifest.module_loading.prefix = next_config
let chunk_suffix_path = next_config.chunk_suffix_path().await?;
let prefix_path = next_config
.computed_asset_prefix()
.await?
.as_ref()
.map(|p| p.clone())
.unwrap_or_default();
let suffix_path = chunk_suffix_path
.as_ref()
.map(|p| p.to_string())
.unwrap_or("".into());

entry_manifest.module_loading.prefix = prefix_path;

entry_manifest.module_loading.cross_origin = next_config
.await?
Expand Down Expand Up @@ -195,6 +202,7 @@ impl ClientReferenceManifest {
// It's possible that a chunk also emits CSS files, that will
// be handled separatedly.
.filter(|path| path.ends_with(".js"))
.map(|path| format!("{}{}", path, suffix_path))
.map(RcStr::from)
.collect::<Vec<_>>();

Expand Down Expand Up @@ -355,6 +363,8 @@ impl ClientReferenceManifest {

for (chunk, chunk_path) in client_chunks_with_path {
if let Some(path) = client_relative_path.get_path_to(&chunk_path) {
// The entry CSS files and entry JS files don't have prefix and suffix
// applied because it is added by Nex.js during rendering.
let path = path.into();
if chunk_path.extension_ref() == Some("css") {
entry_css_files_with_chunk.push((path, chunk));
Expand Down
6 changes: 4 additions & 2 deletions packages/next/src/server/app-render/get-asset-query-string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { AppRenderContext } from './app-render'

const isDev = process.env.NODE_ENV === 'development'
const isTurbopack = !!process.env.TURBOPACK

export function getAssetQueryString(
ctx: AppRenderContext,
addTimestamp: boolean
Expand All @@ -12,12 +13,13 @@ export function getAssetQueryString(
// reload assets when a new RSC response is received.
// Turbopack handles HMR of assets itself and react doesn't need to reload them
// so this approach is not needed for Turbopack.
if (isDev && !isTurbopack && addTimestamp) {
const shouldAddVersion = isDev && !isTurbopack && addTimestamp
if (shouldAddVersion) {
qs += `?v=${ctx.requestTimestamp}`
}

if (ctx.renderOpts.deploymentId) {
qs += `${isDev ? '&' : '?'}dpl=${ctx.renderOpts.deploymentId}`
qs += `${shouldAddVersion ? '&' : '?'}dpl=${ctx.renderOpts.deploymentId}`
}
return qs
}
16 changes: 11 additions & 5 deletions packages/next/taskfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -1814,11 +1814,17 @@ export async function copy_vendor_react(task_) {
// bundles have unique constraints like a runtime bundle. For browser builds this
// package will be bundled alongside user code and we don't need to introduce the extra
// indirection
if (
(file.base.startsWith('react-server-dom-turbopack-client') &&
!file.base.startsWith(
'react-server-dom-turbopack-client.browser'
)) ||

if (file.base.startsWith('react-server-dom-turbopack-client.browser')) {
const source = file.data.toString()
let newSource = source.replace(
/__turbopack_load__/g,
'__turbopack_load__'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preparation for changing this to __turbopack_load_by_url__.

)

file.data = newSource
} else if (
file.base.startsWith('react-server-dom-turbopack-client') ||
(file.base.startsWith('react-server-dom-turbopack-server') &&
!file.base.startsWith('react-server-dom-turbopack-server.browser'))
) {
Expand Down
7 changes: 3 additions & 4 deletions test/turbopack-build-tests-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -17873,7 +17873,8 @@
"runtimeError": false
},
"test/production/deployment-id-handling/deployment-id-handling.test.ts": {
"passed": [
"passed": [],
"failed": [
"deployment-id-handling disabled should not append dpl query to all assets for /",
"deployment-id-handling disabled should not append dpl query to all assets for /from-app",
"deployment-id-handling disabled should not append dpl query to all assets for /from-app/edge",
Expand All @@ -17883,9 +17884,7 @@
"deployment-id-handling enabled with CUSTOM_DEPLOYMENT_ID should have deployment id env available",
"deployment-id-handling enabled with NEXT_DEPLOYMENT_ID should contain deployment id in RSC payload request headers",
"deployment-id-handling enabled with NEXT_DEPLOYMENT_ID should contain deployment id in prefetch request",
"deployment-id-handling enabled with NEXT_DEPLOYMENT_ID should have deployment id env available"
],
"failed": [
"deployment-id-handling enabled with NEXT_DEPLOYMENT_ID should have deployment id env available",
"deployment-id-handling enabled with CUSTOM_DEPLOYMENT_ID should append dpl query to all assets correctly for /",
"deployment-id-handling enabled with CUSTOM_DEPLOYMENT_ID should append dpl query to all assets correctly for /from-app",
"deployment-id-handling enabled with CUSTOM_DEPLOYMENT_ID should append dpl query to all assets correctly for /from-app/edge",
Expand Down
14 changes: 14 additions & 0 deletions turbopack/crates/turbopack-browser/src/chunking_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ impl BrowserChunkingContextBuilder {
self
}

pub fn chunk_suffix_path(mut self, chunk_suffix_path: ResolvedVc<Option<RcStr>>) -> Self {
self.chunking_context.chunk_suffix_path = chunk_suffix_path;
self
}

pub fn runtime_type(mut self, runtime_type: RuntimeType) -> Self {
self.chunking_context.runtime_type = runtime_type;
self
Expand Down Expand Up @@ -135,6 +140,9 @@ pub struct BrowserChunkingContext {
/// Base path that will be prepended to all chunk URLs when loading them.
/// This path will not appear in chunk paths or chunk data.
chunk_base_path: ResolvedVc<Option<RcStr>>,
/// Suffix path that will be appended to all chunk URLs when loading them.
/// This path will not appear in chunk paths or chunk data.
chunk_suffix_path: ResolvedVc<Option<RcStr>>,
/// URL prefix that will be prepended to all static asset URLs when loading
/// them.
asset_base_path: ResolvedVc<Option<RcStr>>,
Expand Down Expand Up @@ -180,6 +188,7 @@ impl BrowserChunkingContext {
should_use_file_source_map_uris: false,
asset_root_path,
chunk_base_path: ResolvedVc::cell(None),
chunk_suffix_path: ResolvedVc::cell(None),
asset_base_path: ResolvedVc::cell(None),
enable_hot_module_replacement: false,
enable_tracing: false,
Expand Down Expand Up @@ -209,6 +218,11 @@ impl BrowserChunkingContext {
*self.chunk_base_path
}

/// Returns the asset suffix path.
pub fn chunk_suffix_path(&self) -> Vc<Option<RcStr>> {
*self.chunk_suffix_path
}

/// Returns the minify type.
pub fn source_maps_type(&self) -> SourceMapsType {
self.source_maps_type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ impl EcmascriptBrowserEvaluateChunk {
let runtime_code = turbopack_ecmascript_runtime::get_browser_runtime_code(
environment,
chunking_context.chunk_base_path(),
chunking_context.chunk_suffix_path(),
Value::new(chunking_context.runtime_type()),
output_root_to_root_path,
source_maps,
Expand All @@ -163,6 +164,7 @@ impl EcmascriptBrowserEvaluateChunk {
let runtime_code = turbopack_ecmascript_runtime::get_browser_runtime_code(
environment,
chunking_context.chunk_base_path(),
chunking_context.chunk_suffix_path(),
Value::new(chunking_context.runtime_type()),
output_root_to_root_path,
source_maps,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function connect({
throw new Error("A separate HMR handler was already registered");
}
globalThis.TURBOPACK_CHUNK_UPDATE_LISTENERS = {
push: ([chunkPath, callback]: [ChunkPath, UpdateCallback]) => {
push: ([chunkPath, callback]: [ChunkListPath, UpdateCallback]) => {
subscribeToChunkUpdate(chunkPath, sendMessage, callback);
},
};
Expand Down Expand Up @@ -202,7 +202,7 @@ function mergeChunkListChunks(
): Record<ChunkPath, ChunkUpdate> {
const chunks: Record<ChunkPath, ChunkUpdate> = {};

for (const [chunkPath, chunkUpdateA] of Object.entries(chunksA)) {
for (const [chunkPath, chunkUpdateA] of Object.entries(chunksA) as Array<[ChunkPath, ChunkUpdate]>) {
const chunkUpdateB = chunksB[chunkPath];
if (chunkUpdateB != null) {
const mergedUpdate = mergeChunkUpdates(chunkUpdateA, chunkUpdateB);
Expand All @@ -214,7 +214,7 @@ function mergeChunkListChunks(
}
}

for (const [chunkPath, chunkUpdateB] of Object.entries(chunksB)) {
for (const [chunkPath, chunkUpdateB] of Object.entries(chunksB) as Array<[ChunkPath, ChunkUpdate]>) {
if (chunks[chunkPath] == null) {
chunks[chunkPath] = chunkUpdateB;
}
Expand Down Expand Up @@ -280,7 +280,7 @@ function mergeEcmascriptChunksUpdates(

const chunks: Record<ChunkPath, EcmascriptMergedChunkUpdate> = {};

for (const [chunkPath, chunkUpdateA] of Object.entries(chunksA)) {
for (const [chunkPath, chunkUpdateA] of Object.entries(chunksA) as Array<[ChunkPath, EcmascriptMergedChunkUpdate]>) {
const chunkUpdateB = chunksB[chunkPath];
if (chunkUpdateB != null) {
const mergedUpdate = mergeEcmascriptChunkUpdates(
Expand All @@ -295,7 +295,7 @@ function mergeEcmascriptChunksUpdates(
}
}

for (const [chunkPath, chunkUpdateB] of Object.entries(chunksB)) {
for (const [chunkPath, chunkUpdateB] of Object.entries(chunksB) as Array<[ChunkPath, EcmascriptMergedChunkUpdate]>) {
if (chunks[chunkPath] == null) {
chunks[chunkPath] = chunkUpdateB;
}
Expand Down Expand Up @@ -530,13 +530,13 @@ function finalizeUpdate() {
}

function subscribeToChunkUpdate(
chunkPath: ChunkPath,
chunkListPath: ChunkListPath,
sendMessage: SendMessage,
callback: UpdateCallback
): () => void {
return subscribeToUpdate(
{
path: chunkPath,
path: chunkListPath,
},
sendMessage,
callback
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ function instantiateModule(id: ModuleId, source: SourceInfo): Module {
c: moduleCache,
M: moduleFactories,
l: loadChunk.bind(null, sourceInfo),
L: loadChunkByUrl.bind(null, sourceInfo),
w: loadWebAssembly.bind(null, sourceInfo),
u: loadWebAssemblyModule.bind(null, sourceInfo),
g: globalThis,
Expand Down
Loading
Loading