diff --git a/crates/next-core/src/next_build.rs b/crates/next-core/src/next_build.rs index cb2260c228c07..b9d477f9ef66f 100644 --- a/crates/next-core/src/next_build.rs +++ b/crates/next-core/src/next_build.rs @@ -18,12 +18,14 @@ pub async fn get_postcss_package_mapping(project_path: Vc) -> Vc #[turbo_tasks::function] pub async fn get_external_next_compiled_package_mapping( + project_path: Vc, package_name: Vc, ) -> Result> { Ok(ImportMapping::Alternatives(vec![ImportMapping::External( Some(format!("next/dist/compiled/{}", &*package_name.await?).into()), ExternalType::CommonJs, - ExternalTraced::Traced, + ExternalTraced::Traced(project_path), + None, ) .into()]) .cell()) diff --git a/crates/next-core/src/next_import_map.rs b/crates/next-core/src/next_import_map.rs index cb36a12e69dbe..0e80ad3a25ad3 100644 --- a/crates/next-core/src/next_import_map.rs +++ b/crates/next-core/src/next_import_map.rs @@ -239,7 +239,7 @@ pub async fn get_next_client_import_map( /// Computes the Next-specific client import map. #[turbo_tasks::function] -pub fn get_next_build_import_map() -> Vc { +pub fn get_next_build_import_map(project_path: Vc) -> Vc { let mut import_map = ImportMap::empty(); insert_package_alias( @@ -248,8 +248,13 @@ pub fn get_next_build_import_map() -> Vc { next_js_fs().root(), ); - let external = - ImportMapping::External(None, ExternalType::CommonJs, ExternalTraced::Traced).cell(); + let external = ImportMapping::External( + None, + ExternalType::CommonJs, + ExternalTraced::Traced(project_path), + None, + ) + .cell(); import_map.insert_exact_alias("next", external); import_map.insert_wildcard_alias("next/", external); @@ -259,7 +264,8 @@ pub fn get_next_build_import_map() -> Vc { ImportMapping::External( Some("styled-jsx/style.js".into()), ExternalType::CommonJs, - ExternalTraced::Traced, + ExternalTraced::Traced(project_path), + None, ) .cell(), ); @@ -324,8 +330,13 @@ pub async fn get_next_server_import_map( let ty = ty.into_value(); - let external: Vc = - ImportMapping::External(None, ExternalType::CommonJs, ExternalTraced::Traced).cell(); + let external: Vc = ImportMapping::External( + None, + ExternalType::CommonJs, + ExternalTraced::Traced(project_path), + None, + ) + .cell(); import_map.insert_exact_alias("next/dist/server/require-hook", external); match ty { @@ -343,7 +354,8 @@ pub async fn get_next_server_import_map( ImportMapping::External( Some("styled-jsx/style.js".into()), ExternalType::CommonJs, - ExternalTraced::Traced, + ExternalTraced::Traced(project_path), + None, ) .cell(), ); @@ -565,11 +577,11 @@ async fn insert_next_server_special_aliases( ) -> Result<()> { let external_cjs_if_node = move |context_dir: Vc, request: &str| match runtime { NextRuntime::Edge => request_to_import_mapping(context_dir, request), - NextRuntime::NodeJs => external_request_to_cjs_import_mapping(request), + NextRuntime::NodeJs => external_request_to_cjs_import_mapping(context_dir, request), }; let external_esm_if_node = move |context_dir: Vc, request: &str| match runtime { NextRuntime::Edge => request_to_import_mapping(context_dir, request), - NextRuntime::NodeJs => external_request_to_esm_import_mapping(request), + NextRuntime::NodeJs => external_request_to_esm_import_mapping(context_dir, request), }; import_map.insert_exact_alias( @@ -1097,22 +1109,30 @@ fn request_to_import_mapping(context_path: Vc, request: &str) -> /// Creates a direct import mapping to the result of resolving an external /// request. -fn external_request_to_cjs_import_mapping(request: &str) -> Vc { +fn external_request_to_cjs_import_mapping( + context_dir: Vc, + request: &str, +) -> Vc { ImportMapping::External( Some(request.into()), ExternalType::CommonJs, - ExternalTraced::Traced, + ExternalTraced::Traced(context_dir), + Some(context_dir), ) .into() } /// Creates a direct import mapping to the result of resolving an external /// request. -fn external_request_to_esm_import_mapping(request: &str) -> Vc { +fn external_request_to_esm_import_mapping( + context_dir: Vc, + request: &str, +) -> Vc { ImportMapping::External( Some(request.into()), ExternalType::EcmaScriptModule, - ExternalTraced::Traced, + ExternalTraced::Traced(context_dir), + Some(context_dir), ) .into() } diff --git a/crates/next-core/src/next_server/resolve.rs b/crates/next-core/src/next_server/resolve.rs index 5504612847812..e0989bd2f6fb1 100644 --- a/crates/next-core/src/next_server/resolve.rs +++ b/crates/next-core/src/next_server/resolve.rs @@ -420,7 +420,7 @@ impl AfterResolvePlugin for ExternalCjsModulesResolvePlugin { ResolveResult::primary(ResolveResultItem::External { name: request_str.into(), ty: external_type, - traced: ExternalTraced::Traced, + traced: ExternalTraced::Traced(self.project_path), }) .cell(), )) diff --git a/crates/next-core/src/next_shared/resolve.rs b/crates/next-core/src/next_shared/resolve.rs index d7ca0163a8947..bff9bd715a6a3 100644 --- a/crates/next-core/src/next_shared/resolve.rs +++ b/crates/next-core/src/next_shared/resolve.rs @@ -204,14 +204,14 @@ pub(crate) fn get_invalid_styled_jsx_resolve_plugin( #[turbo_tasks::value] pub(crate) struct NextExternalResolvePlugin { - root: Vc, + project_path: Vc, } #[turbo_tasks::value_impl] impl NextExternalResolvePlugin { #[turbo_tasks::function] - pub fn new(root: Vc) -> Vc { - NextExternalResolvePlugin { root }.cell() + pub fn new(project_path: Vc) -> Vc { + NextExternalResolvePlugin { project_path }.cell() } } @@ -220,7 +220,7 @@ impl AfterResolvePlugin for NextExternalResolvePlugin { #[turbo_tasks::function] fn after_resolve_condition(&self) -> Vc { AfterResolvePluginCondition::new( - self.root.root(), + self.project_path.root(), Glob::new("**/next/dist/**/*.{external,runtime.dev,runtime.prod}.js".into()), ) } @@ -245,7 +245,7 @@ impl AfterResolvePlugin for NextExternalResolvePlugin { ResolveResult::primary(ResolveResultItem::External { name: specifier.clone(), ty: ExternalType::CommonJs, - traced: ExternalTraced::Traced, + traced: ExternalTraced::Traced(self.project_path), }) .into(), ))) diff --git a/crates/next-core/src/next_shared/webpack_rules/mod.rs b/crates/next-core/src/next_shared/webpack_rules/mod.rs index 3efc4f554a84e..e3d7e23677589 100644 --- a/crates/next-core/src/next_shared/webpack_rules/mod.rs +++ b/crates/next-core/src/next_shared/webpack_rules/mod.rs @@ -2,10 +2,10 @@ use anyhow::Result; use turbo_tasks::{RcStr, Vc}; use turbo_tasks_fs::FileSystemPath; use turbopack::module_options::WebpackLoadersOptions; -use turbopack_core::resolve::options::ImportMapping; +use turbopack_core::resolve::{options::ImportMapping, ExternalTraced, ExternalType}; use self::{babel::maybe_add_babel_loader, sass::maybe_add_sass_loader}; -use crate::{next_build::get_external_next_compiled_package_mapping, next_config::NextConfig}; +use crate::next_config::NextConfig; pub(crate) mod babel; pub(crate) mod sass; @@ -33,6 +33,13 @@ pub async fn webpack_loader_options( } #[turbo_tasks::function] -fn loader_runner_package_mapping() -> Vc { - get_external_next_compiled_package_mapping(Vc::cell("loader-runner".into())) +async fn loader_runner_package_mapping() -> Result> { + Ok(ImportMapping::Alternatives(vec![ImportMapping::External( + Some("next/dist/compiled/loader-runner".into()), + ExternalType::CommonJs, + ExternalTraced::Untraced, + None, + ) + .into()]) + .cell()) } diff --git a/turbopack/crates/turbopack-core/src/resolve/mod.rs b/turbopack/crates/turbopack-core/src/resolve/mod.rs index a16d9dbbce870..f14c0fc30e992 100644 --- a/turbopack/crates/turbopack-core/src/resolve/mod.rs +++ b/turbopack/crates/turbopack-core/src/resolve/mod.rs @@ -38,6 +38,7 @@ use crate::{ raw_module::RawModule, reference_type::ReferenceType, resolve::{ + node::{node_cjs_resolve_options, node_esm_resolve_options}, pattern::{read_matches, PatternMatch}, plugin::AfterResolvePlugin, }, @@ -374,20 +375,21 @@ impl ModuleResolveResult { } } -#[derive( - Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, TraceRawVcs, TaskInput, -)] +#[derive(Copy, Clone)] +#[turbo_tasks::value(shared)] pub enum ExternalTraced { Untraced, - Traced, + Traced(Vc), } -impl Display for ExternalTraced { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - ExternalTraced::Untraced => write!(f, "Untraced"), - ExternalTraced::Traced => write!(f, "Traced"), - } +impl ExternalTraced { + async fn as_string(&self) -> Result { + Ok(match self { + ExternalTraced::Untraced => "untraced".to_string(), + ExternalTraced::Traced(context) => { + format!("traced from {}", context.to_string().await?) + } + }) } } @@ -411,7 +413,7 @@ impl Display for ExternalType { } #[turbo_tasks::value(shared)] -#[derive(Clone, Debug)] +#[derive(Clone)] pub enum ResolveResultItem { Source(Vc>), External { @@ -470,7 +472,7 @@ impl RequestKey { } #[turbo_tasks::value(shared)] -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct ResolveResult { pub primary: FxIndexMap, pub affecting_sources: Vec>>, @@ -506,7 +508,7 @@ impl ValueToString for ResolveResult { } => { result.push_str("external "); result.push_str(s); - write!(result, " ({}, {})", ty, traced)?; + write!(result, " ({}, {})", ty, traced.as_string().await?)?; } ResolveResultItem::Ignore => { result.push_str("ignore"); @@ -2601,6 +2603,48 @@ async fn resolve_import_map_result( )) } } + ImportMapResult::External(name, ty, traced, primary_alt) => { + let result = Some( + ResolveResult::primary(ResolveResultItem::External { + name: name.clone(), + ty: *ty, + traced: *traced, + }) + .cell(), + ); + + if let Some(context_dir) = primary_alt { + let request = Request::parse_string(name.clone()); + + // We must avoid cycles during resolving + if request.resolve().await? == original_request + && context_dir.resolve().await? == original_lookup_path + { + None + } else { + let resolve_internal = resolve_internal( + *context_dir, + request, + match ty { + ExternalType::Url => options, + // TODO is that root correct? + ExternalType::CommonJs => node_cjs_resolve_options(context_dir.root()), + ExternalType::EcmaScriptModule => { + node_esm_resolve_options(context_dir.root()) + } + }, + ); + + if *resolve_internal.is_unresolvable().await? { + None + } else { + result + } + } + } else { + result + } + } ImportMapResult::Alternatives(list) => { let results = list .iter() diff --git a/turbopack/crates/turbopack-core/src/resolve/options.rs b/turbopack/crates/turbopack-core/src/resolve/options.rs index 2972593ce819c..368c12544080e 100644 --- a/turbopack/crates/turbopack-core/src/resolve/options.rs +++ b/turbopack/crates/turbopack-core/src/resolve/options.rs @@ -94,7 +94,14 @@ pub enum ResolveInPackage { #[derive(Clone)] pub enum ImportMapping { // If specified, the optional name overrides the request, importing that external instead - External(Option, ExternalType, ExternalTraced), + // If the last option is a path, this behaves like PrimaryAlternative, only making it external + // if the request is resolvable from the directory. + External( + Option, + ExternalType, + ExternalTraced, + Option>, + ), /// An already resolved result that will be returned directly. Direct(ResolvedVc), /// A request alias that will be resolved first, and fall back to resolving @@ -112,7 +119,12 @@ pub enum ImportMapping { #[turbo_tasks::value(shared)] #[derive(Clone)] pub enum ReplacedImportMapping { - External(Option, ExternalType, ExternalTraced), + External( + Option, + ExternalType, + ExternalTraced, + Option>, + ), Direct(Vc), PrimaryAlternative(Pattern, Option>), Ignore, @@ -147,8 +159,8 @@ impl AliasTemplate for Vc { Box::pin(async move { let this = &*self.await?; Ok(match this { - ImportMapping::External(name, ty, traced) => { - ReplacedImportMapping::External(name.clone(), *ty, *traced) + ImportMapping::External(name, ty, traced, primary_alt) => { + ReplacedImportMapping::External(name.clone(), *ty, *traced, *primary_alt) } ImportMapping::PrimaryAlternative(name, context) => { ReplacedImportMapping::PrimaryAlternative((*name).clone().into(), *context) @@ -174,15 +186,16 @@ impl AliasTemplate for Vc { Box::pin(async move { let this = &*self.await?; Ok(match this { - ImportMapping::External(name, ty, traced) => { + ImportMapping::External(name, ty, traced, primary_alt) => { if let Some(name) = name { ReplacedImportMapping::External( capture.spread_into_star(name).as_string().map(|s| s.into()), *ty, *traced, + *primary_alt, ) } else { - ReplacedImportMapping::External(None, *ty, *traced) + ReplacedImportMapping::External(None, *ty, *traced, *primary_alt) } } ImportMapping::PrimaryAlternative(name, context) => { @@ -306,9 +319,15 @@ pub struct ResolvedMap { } #[turbo_tasks::value(shared)] -#[derive(Clone, Debug)] +#[derive(Clone)] pub enum ImportMapResult { Result(Vc), + External( + RcStr, + ExternalType, + ExternalTraced, + Option>, + ), Alias(Vc, Option>), Alternatives(Vec), NoEntry, @@ -321,24 +340,20 @@ async fn import_mapping_to_result( ) -> Result { Ok(match &*mapping.await? { ReplacedImportMapping::Direct(result) => ImportMapResult::Result(*result), - ReplacedImportMapping::External(name, ty, traced) => ImportMapResult::Result( - ResolveResult::primary({ - let name = if let Some(name) = name { + ReplacedImportMapping::External(name, ty, traced, primary_alt) => { + ImportMapResult::External( + if let Some(name) = name { name.clone() } else if let Some(request) = request.await?.request() { request } else { bail!("Cannot resolve external reference without request") - }; - - ResolveResultItem::External { - name, - ty: *ty, - traced: *traced, - } - }) - .cell(), - ), + }, + *ty, + *traced, + *primary_alt, + ) + } ReplacedImportMapping::Ignore => { ImportMapResult::Result(ResolveResult::primary(ResolveResultItem::Ignore).into()) } @@ -368,6 +383,7 @@ impl ValueToString for ImportMapResult { async fn to_string(&self) -> Result> { match self { ImportMapResult::Result(_) => Ok(Vc::cell("Resolved by import map".into())), + ImportMapResult::External(_, _, _, _) => Ok(Vc::cell("TODO external".into())), ImportMapResult::Alias(request, context) => { let s = if let Some(path) = context { let path = path.to_string().await?; diff --git a/turbopack/crates/turbopack-resolve/src/resolve.rs b/turbopack/crates/turbopack-resolve/src/resolve.rs index b07f45cd52545..3f0ea7ad94a8e 100644 --- a/turbopack/crates/turbopack-resolve/src/resolve.rs +++ b/turbopack/crates/turbopack-resolve/src/resolve.rs @@ -106,13 +106,23 @@ async fn base_resolve_options( for req in NODE_EXTERNALS { direct_mappings.insert( AliasPattern::exact(req), - ImportMapping::External(None, ExternalType::CommonJs, ExternalTraced::Untraced) - .into(), + ImportMapping::External( + None, + ExternalType::CommonJs, + ExternalTraced::Untraced, + None, + ) + .into(), ); direct_mappings.insert( AliasPattern::exact(format!("node:{req}")), - ImportMapping::External(None, ExternalType::CommonJs, ExternalTraced::Untraced) - .into(), + ImportMapping::External( + None, + ExternalType::CommonJs, + ExternalTraced::Untraced, + None, + ) + .into(), ); } } @@ -124,13 +134,19 @@ async fn base_resolve_options( Some(format!("node:{req}").into()), ExternalType::CommonJs, ExternalTraced::Untraced, + None, ) .into(), ); direct_mappings.insert( AliasPattern::exact(format!("node:{req}")), - ImportMapping::External(None, ExternalType::CommonJs, ExternalTraced::Untraced) - .into(), + ImportMapping::External( + None, + ExternalType::CommonJs, + ExternalTraced::Untraced, + None, + ) + .into(), ); } } diff --git a/turbopack/crates/turbopack-tests/tests/execution.rs b/turbopack/crates/turbopack-tests/tests/execution.rs index 42dada9f9930c..c0a9dd20eaa80 100644 --- a/turbopack/crates/turbopack-tests/tests/execution.rs +++ b/turbopack/crates/turbopack-tests/tests/execution.rs @@ -278,6 +278,7 @@ async fn run_test(prepared_test: Vc) -> Result> Some("*".into()), ExternalType::EcmaScriptModule, ExternalTraced::Untraced, + None, ) .cell(), ); diff --git a/turbopack/crates/turbopack/src/lib.rs b/turbopack/crates/turbopack/src/lib.rs index 429c2e6255399..d6f99b4a7abcf 100644 --- a/turbopack/crates/turbopack/src/lib.rs +++ b/turbopack/crates/turbopack/src/lib.rs @@ -710,7 +710,8 @@ impl AssetContext for ModuleAssetContext { #[turbo_tasks::function] async fn process_resolve_result( self: Vc, - origin_path: Vc, + // TODO this can probably be removed + _origin_path: Vc, result: Vc, reference_type: Value, ignore_unknown: bool, @@ -751,16 +752,17 @@ impl AssetContext for ModuleAssetContext { let replacement = if replace_externals { let additional_refs = match traced { // TODO can we get away without module_context.enable_tracing ? - ExternalTraced::Traced => { - if let Some(out_dir) = self + ExternalTraced::Traced(tracing_root) => { + if self .module_options_context() .await? .enable_tracing .as_ref() + .is_some() { let externals_context = externals_tracing_module_context(ty); - let out_dir = out_dir.join("index".into()); + let out_dir = tracing_root.join("index".into()); let external_result = (externals_context .resolve_asset(