Skip to content

Commit

Permalink
Turbopack: add support for an asset prefix (and basePath in Next.js)
Browse files Browse the repository at this point in the history
Depends on vercel/turbo#6036

This constructs a general asset prefix (depending on if basePath and/or assetPrefix is defined), and sets it on Next's and Turbopack's chunking context.

Test Plan: Use a `create-next-app` with corresponding Next.js PR and basePath and assetPrefix specified.

Tested:
- Browser (async) chunk loading
- Static asset loading on server and browser
  • Loading branch information
wbinnssmith committed Sep 26, 2023
1 parent dbc65e8 commit ee4acf7
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 36 deletions.
22 changes: 17 additions & 5 deletions packages/next-swc/crates/next-api/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,17 @@ impl Project {
}

#[turbo_tasks::function]
pub(super) fn client_relative_path(self: Vc<Self>) -> Vc<FileSystemPath> {
self.client_root().join("_next".to_string())
pub(super) async fn client_relative_path(self: Vc<Self>) -> Result<Vc<FileSystemPath>> {
let next_config = self.next_config().await?;
Ok(self
.client_root()
.join(
next_config
.base_path
.clone()
.unwrap_or_else(|| "".to_string()),
)
.join("_next".to_string()))
}

#[turbo_tasks::function]
Expand Down Expand Up @@ -381,7 +390,8 @@ impl Project {
let this = self.await?;
Ok(get_client_chunking_context(
self.project_path(),
self.client_root(),
self.client_relative_path(),
self.next_config().computed_asset_prefix(),
self.client_compile_time_info().environment(),
this.mode,
))
Expand All @@ -392,7 +402,9 @@ impl Project {
get_server_chunking_context(
self.project_path(),
self.node_root(),
self.client_root(),
// self.client_root(),
self.client_relative_path(),
self.next_config().computed_asset_prefix(),
self.server_compile_time_info().environment(),
)
}
Expand All @@ -402,7 +414,7 @@ impl Project {
get_edge_chunking_context(
self.project_path(),
self.node_root(),
self.client_root(),
self.client_relative_path(),
self.edge_compile_time_info().environment(),
)
}
Expand Down
5 changes: 5 additions & 0 deletions packages/next-swc/crates/next-build/src/next_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ pub(crate) async fn next_build(options: TransientInstance<BuildOptions>) -> Resu
let client_chunking_context = get_client_chunking_context(
project_root,
client_root,
next_config.computed_asset_prefix(),
client_compile_time_info.environment(),
mode,
);
Expand All @@ -253,6 +254,10 @@ pub(crate) async fn next_build(options: TransientInstance<BuildOptions>) -> Resu
project_root,
node_root,
client_root,
{
let asset_prefix = &*next_config.computed_asset_prefix().await?;
Vc::cell(Some(asset_prefix.to_string()))
},
server_compile_time_info.environment(),
);
// TODO(alexkirsz) This should be the same chunking context. The layer should
Expand Down
26 changes: 14 additions & 12 deletions packages/next-swc/crates/next-core/src/next_client/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,38 +309,40 @@ pub async fn get_client_module_options_context(
}

#[turbo_tasks::function]
pub fn get_client_chunking_context(
pub async fn get_client_chunking_context(
project_path: Vc<FileSystemPath>,
client_root: Vc<FileSystemPath>,
asset_prefix: Vc<String>,
environment: Vc<Environment>,
mode: NextMode,
) -> Vc<Box<dyn EcmascriptChunkingContext>> {
let output_root = match mode {
NextMode::DevServer => client_root,
NextMode::Development | NextMode::Build => client_root.join("_next".to_string()),
};
) -> Result<Vc<Box<dyn EcmascriptChunkingContext>>> {
let builder = DevChunkingContext::builder(
project_path,
output_root,
client_root.join("_next/static/chunks".to_string()),
client_root,
client_root.join("static/chunks".to_string()),
get_client_assets_path(client_root),
environment,
);

let asset_prefix = &*asset_prefix.await?;
let asset_prefix: Vc<Option<String>> = Vc::cell(Some(asset_prefix.to_owned()));
let builder = match mode {
NextMode::DevServer => builder.hot_module_replacement(),
NextMode::Development => builder
.hot_module_replacement()
.chunk_base_path(Vc::cell(Some("_next/".to_string()))),
NextMode::Build => builder.chunk_base_path(Vc::cell(Some("_next/".to_string()))),
.chunk_base_path(asset_prefix)
.asset_prefix(asset_prefix),
NextMode::Build => builder
.chunk_base_path(asset_prefix)
.asset_prefix(asset_prefix),
};

Vc::upcast(builder.build())
Ok(Vc::upcast(builder.build()))
}

#[turbo_tasks::function]
pub fn get_client_assets_path(client_root: Vc<FileSystemPath>) -> Vc<FileSystemPath> {
client_root.join("_next/static/media".to_string())
client_root.join("static/media".to_string())
}

#[turbo_tasks::function]
Expand Down
17 changes: 17 additions & 0 deletions packages/next-swc/crates/next-core/src/next_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,23 @@ impl NextConfig {
self.await?.skip_trailing_slash_redirect.unwrap_or(false),
))
}

/// Returns the final asset prefix. If an assetPrefix is set, it's used.
/// Otherwise, the basePath is used.
#[turbo_tasks::function]
pub async fn computed_asset_prefix(self: Vc<Self>) -> Result<Vc<String>> {
let this = self.await?;

Ok(Vc::cell(
if let Some(asset_prefix) = &this.asset_prefix {
asset_prefix.to_string()
} else if let Some(base_path) = &this.base_path {
base_path.to_string()
} else {
"".to_string()
} + "/_next/",
))
}
}

fn next_configs() -> Vc<Vec<String>> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ pub fn get_server_chunking_context(
// TODO(alexkirsz) Is this even necessary? Are assets not always on the client chunking context
// anyway?
client_root: Vc<FileSystemPath>,
asset_prefix: Vc<Option<String>>,
environment: Vc<Environment>,
) -> Vc<BuildChunkingContext> {
// TODO(alexkirsz) This should return a trait that can be implemented by the
Expand All @@ -611,10 +612,12 @@ pub fn get_server_chunking_context(
BuildChunkingContext::builder(
project_path,
node_root,
client_root,
node_root.join("server/chunks".to_string()),
client_root.join("_next/static/media".to_string()),
client_root.join("static/media".to_string()),
environment,
)
.asset_prefix(asset_prefix)
.minify_type(MinifyType::NoMinify)
.build()
}
8 changes: 6 additions & 2 deletions packages/next-swc/crates/next-core/src/page_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,12 @@ fn page_loader_chunk_reference_description() -> Vc<String> {
impl OutputAsset for PageLoaderAsset {
#[turbo_tasks::function]
async fn ident(&self) -> Result<Vc<AssetIdent>> {
Ok(AssetIdent::from_path(self.server_root.join(format!(
"_next/static/chunks/pages{}",
let root = match *self.rebase_prefix_path.await? {
Some(prefix) => prefix,
None => self.server_root,
};
Ok(AssetIdent::from_path(root.join(format!(
"static/chunks/pages{}",
get_asset_path_from_pathname(&self.pathname.await?, ".js")
))))
}
Expand Down
17 changes: 1 addition & 16 deletions packages/next/src/client/next-dev-turbopack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,11 @@ initialize({
})
.then(({ assetPrefix }) => {
// for the page loader
async function loadPageChunk(chunkData: any) {
if (typeof chunkData === 'string') {
const fullPath = assetPrefix + chunkData

await __turbopack_load__(fullPath)
} else {
let fullChunkData = {
...chunkData,
path: assetPrefix + chunkData.path,
}

await __turbopack_load__(fullChunkData)
}
}

;(self as any).__turbopack_load_page_chunks__ = (
page: string,
chunksData: any
) => {
const chunkPromises = chunksData.map(loadPageChunk)
const chunkPromises = chunksData.map(__turbopack_load__)

Promise.all(chunkPromises).catch((err) =>
console.error('failed to load chunks for page ' + page, err)
Expand Down

0 comments on commit ee4acf7

Please sign in to comment.