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
21 changes: 10 additions & 11 deletions crates/next-core/src/next_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ pub struct NextConfig {
experimental: ExperimentalConfig,
images: ImageConfig,
page_extensions: Vec<RcStr>,
instrumentation_client_inject: Option<Vec<RcStr>>,
react_compiler: Option<ReactCompilerOptionsOrBoolean>,
react_production_profiling: Option<bool>,
react_strict_mode: Option<bool>,
Expand Down Expand Up @@ -1219,7 +1220,6 @@ pub struct ExperimentalConfig {
/// This field is kept for backwards compatibility during migration.
cache_components: Option<bool>,
use_cache: Option<bool>,
root_params: Option<bool>,
runtime_server_deployment_id: Option<bool>,
supports_immutable_assets: Option<bool>,

Expand Down Expand Up @@ -1796,6 +1796,15 @@ impl NextConfig {
Vc::cell(extensions)
}

#[turbo_tasks::function]
pub fn instrumentation_client_inject(&self) -> Vc<Vec<RcStr>> {
Vc::cell(
self.instrumentation_client_inject
.clone()
.unwrap_or_default(),
)
}

#[turbo_tasks::function]
pub fn is_global_not_found_enabled(&self) -> Vc<bool> {
Vc::cell(self.experimental.global_not_found.unwrap_or_default())
Expand Down Expand Up @@ -2169,16 +2178,6 @@ impl NextConfig {
)
}

#[turbo_tasks::function]
pub fn enable_root_params(&self) -> Vc<bool> {
Vc::cell(
self.experimental
.root_params
// rootParams should be enabled implicitly in cacheComponents.
.unwrap_or(self.cache_components.unwrap_or(false)),
)
}

#[turbo_tasks::function]
pub fn is_using_adapter(&self) -> Vc<bool> {
Vc::cell(self.adapter_path.is_some())
Expand Down
103 changes: 75 additions & 28 deletions crates/next-core/src/next_import_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,21 @@ use next_taskless::{EDGE_NODE_EXTERNALS, NODE_EXTERNALS};
use rustc_hash::FxHashMap;
use turbo_rcstr::{RcStr, rcstr};
use turbo_tasks::{FxIndexMap, ResolvedVc, Vc, fxindexmap};
use turbo_tasks_fs::{FileSystem, FileSystemPath, to_sys_path};
use turbo_tasks_fs::{FileContent, FileSystem, FileSystemPath, to_sys_path};
use turbopack_core::{
asset::AssetContent,
issue::{Issue, IssueExt, IssueSeverity, IssueStage, StyledString},
reference_type::{CommonJsReferenceSubType, ReferenceType},
resolve::{
AliasPattern, ExternalTraced, ExternalType, ResolveAliasMap, SubpathValue,
AliasPattern, ExternalTraced, ExternalType, ResolveAliasMap, ResolveResult, SubpathValue,
node::node_cjs_resolve_options,
options::{ConditionValue, ImportMap, ImportMapping, ResolvedMap},
parse::Request,
pattern::Pattern,
resolve,
},
source::Source,
virtual_source::VirtualSource,
};
use turbopack_node::execution_context::ExecutionContext;

Expand Down Expand Up @@ -209,13 +211,7 @@ pub async fn get_next_client_import_map(
rcstr!("next/dist/compiled/server-only") => rcstr!("next/dist/compiled/server-only/index"),
rcstr!("next/dist/compiled/client-only") => rcstr!("next/dist/compiled/client-only/index"),},
);
insert_next_root_params_mapping(
&mut import_map,
next_config.enable_root_params(),
Either::Right(ty.clone()),
None,
)
.await?;
insert_next_root_params_mapping(&mut import_map, Either::Right(ty.clone()), None).await?;

match ty {
ClientContextType::Pages { .. }
Expand All @@ -231,7 +227,7 @@ pub async fn get_next_client_import_map(
ClientContextType::Other => {}
}

insert_instrumentation_client_alias(&mut import_map, project_path).await?;
insert_instrumentation_client_alias(&mut import_map, project_path, next_config).await?;

insert_server_only_error_alias(&mut import_map);

Expand Down Expand Up @@ -745,13 +741,7 @@ async fn insert_next_server_special_aliases(
}
}

insert_next_root_params_mapping(
import_map,
next_config.enable_root_params(),
Either::Left(ty),
collected_root_params,
)
.await?;
insert_next_root_params_mapping(import_map, Either::Left(ty), collected_root_params).await?;

import_map.insert_exact_alias(
rcstr!("@vercel/og"),
Expand Down Expand Up @@ -1376,24 +1366,81 @@ fn insert_package_alias(import_map: &mut ImportMap, prefix: &str, package_root:
);
}

/// Handles instrumentation-client.ts bundling logic
/// Handles instrumentation-client.ts bundling logic.
///
/// Resolves the `private-next-instrumentation-client` alias to a virtual module
/// that first requires each entry of `instrumentationClientInject` for side
/// effects (in array order) and then re-exports the user's
/// `instrumentation-client.{pageExt}` file via the
/// `private-next-instrumentation-client-user` alias.
async fn insert_instrumentation_client_alias(
import_map: &mut ImportMap,
project_path: FileSystemPath,
next_config: Vc<NextConfig>,
) -> Result<()> {
let user_file_alternatives = vec![
request_to_import_mapping(project_path.clone(), rcstr!("./src/instrumentation-client")),
request_to_import_mapping(
project_path.clone(),
rcstr!("./src/instrumentation-client.ts"),
),
request_to_import_mapping(project_path.clone(), rcstr!("./instrumentation-client")),
request_to_import_mapping(project_path.clone(), rcstr!("./instrumentation-client.ts")),
ImportMapping::Ignore.resolved_cell(),
];

let injects = next_config.instrumentation_client_inject().await?;

if injects.is_empty() {
insert_alias_to_alternatives(
import_map,
rcstr!("private-next-instrumentation-client"),
user_file_alternatives,
);
return Ok(());
}

// The user file is reached through a separate alias so the existing
// alternative resolution stays unchanged.
insert_alias_to_alternatives(
import_map,
rcstr!("private-next-instrumentation-client-user"),
user_file_alternatives,
);

let injects = injects
.iter()
.map(|s| s.as_str())
.chain(std::iter::once("private-next-instrumentation-client-user"));

let mut body = String::new();
for (i, spec) in injects.clone().enumerate() {
body.push_str(&format!(
"var mod_{i} = require({});\n",
serde_json::to_string(spec)?
));
}
body.push_str("module.exports = { onRouterTransitionStart(url, type) {\n");
for (i, _) in injects.enumerate() {
body.push_str(&format!(
" mod_{i}?.onRouterTransitionStart?.(url, type);\n"
));
}
body.push_str("}};\n");

let virtual_source = VirtualSource::new(
project_path.join("__next_instrumentation_client.js")?,
AssetContent::file(FileContent::Content(body.into()).cell()),
)
.to_resolved()
.await?;

import_map.insert_exact_alias(
rcstr!("private-next-instrumentation-client"),
vec![
request_to_import_mapping(project_path.clone(), rcstr!("./src/instrumentation-client")),
request_to_import_mapping(
project_path.clone(),
rcstr!("./src/instrumentation-client.ts"),
),
request_to_import_mapping(project_path.clone(), rcstr!("./instrumentation-client")),
request_to_import_mapping(project_path.clone(), rcstr!("./instrumentation-client.ts")),
ImportMapping::Ignore.resolved_cell(),
],
ImportMapping::Direct(
ResolveResult::source(ResolvedVc::upcast(virtual_source)).resolved_cell(),
)
.resolved_cell(),
);

Ok(())
Expand Down
107 changes: 42 additions & 65 deletions crates/next-core/src/next_root_params/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,20 @@ use crate::{

pub async fn insert_next_root_params_mapping(
import_map: &mut ImportMap,
is_root_params_enabled: Vc<bool>,
ty: Either<ServerContextType, ClientContextType>,
collected_root_params: Option<Vc<CollectedRootParams>>,
) -> Result<()> {
import_map.insert_exact_alias(
"next/root-params",
get_next_root_params_mapping(
is_root_params_enabled,
EitherTaskInput(ty),
collected_root_params,
)
.to_resolved()
.await?,
get_next_root_params_mapping(EitherTaskInput(ty), collected_root_params)
.to_resolved()
.await?,
);
Ok(())
}

#[turbo_tasks::function]
async fn get_next_root_params_mapping(
is_root_params_enabled: Vc<bool>,
ty: EitherTaskInput<ServerContextType, ClientContextType>,
collected_root_params: Option<Vc<CollectedRootParams>>,
) -> Result<Vc<ImportMapping>> {
Expand All @@ -61,7 +55,7 @@ async fn get_next_root_params_mapping(
// `collected_root_params` changes, the resolve options will remain the same, and
// only the mapping result will be invalidated.
let mapping = ImportMapping::Dynamic(ResolvedVc::upcast(
NextRootParamsMapper::new(is_root_params_enabled, ty, collected_root_params)
NextRootParamsMapper::new(ty, collected_root_params)
.to_resolved()
.await?,
));
Expand All @@ -70,7 +64,6 @@ async fn get_next_root_params_mapping(

#[turbo_tasks::value]
struct NextRootParamsMapper {
is_root_params_enabled: ResolvedVc<bool>,
#[bincode(with = "turbo_bincode::either")]
context_type: Either<ServerContextType, ClientContextType>,
collected_root_params: Option<ResolvedVc<CollectedRootParams>>,
Expand All @@ -80,12 +73,10 @@ struct NextRootParamsMapper {
impl NextRootParamsMapper {
#[turbo_tasks::function]
pub fn new(
is_root_params_enabled: ResolvedVc<bool>,
context_type: EitherTaskInput<ServerContextType, ClientContextType>,
collected_root_params: Option<ResolvedVc<CollectedRootParams>>,
) -> Vc<Self> {
NextRootParamsMapper {
is_root_params_enabled,
context_type: context_type.0,
collected_root_params,
}
Expand All @@ -95,61 +86,47 @@ impl NextRootParamsMapper {
#[turbo_tasks::function]
async fn import_map_result(self: Vc<Self>) -> Result<Vc<ImportMapResult>> {
let this = self.await?;
Ok({
if !(*this.is_root_params_enabled.await?) {
Ok(match &this.context_type {
Either::Left(server_ty) => match &server_ty {
ServerContextType::AppRSC { .. } | ServerContextType::AppRoute { .. } => {
let collected_root_params = *this.collected_root_params.ok_or_else(|| {
anyhow!(
"Invariant: Root params should have been collected for context {:?}. \
This is a bug in Next.js.",
server_ty.clone()
)
})?;
Self::valid_import_map_result(collected_root_params)
}
ServerContextType::PagesApi { .. }
| ServerContextType::Instrumentation { .. }
| ServerContextType::Middleware { .. } => {
// There's no sensible way to use root params outside of the app
// directory. TODO: make sure this error is consistent with webpack
Self::invalid_import_map_result(
"'next/root-params' can only be used inside the App Directory.".into(),
)
}
_ => {
// In general, the compiler should prevent importing 'next/root-params'
// from client modules, but it doesn't catch everything. If an import
// slips through our validation, make it error.
Self::invalid_import_map_result(
"'next/root-params' cannot be imported from a Client Component module. It \
should only be used from a Server Component."
.into(),
)
}
},
Either::Right(_) => {
// In general, the compiler should prevent importing 'next/root-params' from
// client modules, but it doesn't catch everything. If an import slips
// through our validation, make it error.
Self::invalid_import_map_result(
"'next/root-params' can only be imported when `experimental.rootParams` is \
enabled."
"'next/root-params' cannot be imported from a Client Component module. It \
should only be used from a Server Component."
.into(),
)
} else {
match &this.context_type {
Either::Left(server_ty) => match &server_ty {
ServerContextType::AppRSC { .. } | ServerContextType::AppRoute { .. } => {
let collected_root_params =
*this.collected_root_params.ok_or_else(|| {
anyhow!(
"Invariant: Root params should have been collected for \
context {:?}. This is a bug in Next.js.",
server_ty.clone()
)
})?;
Self::valid_import_map_result(collected_root_params)
}
ServerContextType::PagesApi { .. }
| ServerContextType::Instrumentation { .. }
| ServerContextType::Middleware { .. } => {
// There's no sensible way to use root params outside of the app
// directory. TODO: make sure this error is
// consistent with webpack
Self::invalid_import_map_result(
"'next/root-params' can only be used inside the App Directory."
.into(),
)
}
_ => {
// In general, the compiler should prevent importing 'next/root-params'
// from client modules, but it doesn't catch
// everything. If an import slips through
// our validation, make it error.
Self::invalid_import_map_result(
"'next/root-params' cannot be imported from a Client Component \
module. It should only be used from a Server Component."
.into(),
)
}
},
Either::Right(_) => {
// In general, the compiler should prevent importing 'next/root-params' from
// client modules, but it doesn't catch everything. If an
// import slips through our validation, make it error.
Self::invalid_import_map_result(
"'next/root-params' cannot be imported from a Client Component \
module. It should only be used from a Server Component."
.into(),
)
}
}
}
})
}
Expand Down
Loading
Loading