From 1d8e3ca3ab7bcb8c70adc05add238252a8c00c2b Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 4 Dec 2024 14:15:12 +0100 Subject: [PATCH 01/12] remove the next.js tarballs after the test has finished --- test/lib/create-next-install.js | 4 ---- test/lib/next-modes/base.ts | 9 +++++++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/test/lib/create-next-install.js b/test/lib/create-next-install.js index 05669cb3cb7ab..093e9aa9852c0 100644 --- a/test/lib/create-next-install.js +++ b/test/lib/create-next-install.js @@ -174,10 +174,6 @@ async function createNextInstall({ .traceAsyncFn(() => installDependencies(installDir, tmpDir)) } - if (!keepRepoDir && tmpRepoDir) { - await fs.remove(tmpRepoDir) - } - return { installDir, pkgPaths, diff --git a/test/lib/next-modes/base.ts b/test/lib/next-modes/base.ts index 9172124ebfff6..6dfe3f8b94ac8 100644 --- a/test/lib/next-modes/base.ts +++ b/test/lib/next-modes/base.ts @@ -64,6 +64,7 @@ export class NextInstance { protected resolutions?: PackageJson['resolutions'] protected events: { [eventName: string]: Set } = {} public testDir: string + tmpRepoDir: string protected isStopping: boolean = false protected isDestroyed: boolean = false protected childProcess?: ChildProcess @@ -220,16 +221,17 @@ export class NextInstance { recursive: true, }) } else { - const { installDir } = await createNextInstall({ + const { installDir, tmpRepoDir } = await createNextInstall({ parentSpan: rootSpan, dependencies: finalDependencies, resolutions: this.resolutions ?? null, installCommand: this.installCommand, packageJson: this.packageJson, dirSuffix: this.dirSuffix, - keepRepoDir: Boolean(process.env.NEXT_TEST_SKIP_CLEANUP), + keepRepoDir: true, }) this.testDir = installDir + this.tmpRepoDir = tmpRepoDir } require('console').log('created next.js install, writing test files') } @@ -463,6 +465,9 @@ export class NextInstance { if (!process.env.NEXT_TEST_SKIP_CLEANUP) { // Faster than `await fs.rm`. Benchmark before change. rmSync(this.testDir, { recursive: true, force: true }) + if (this.tmpRepoDir) { + rmSync(this.tmpRepoDir, { recursive: true, force: true }) + } } require('console').timeEnd(`destroyed next instance`) } catch (err) { From 458de3773b5dfb8cf838bf2f47714c2b214ca93d Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 4 Dec 2024 11:10:36 +0100 Subject: [PATCH 02/12] consistently use root_path as context_path for the ChunkingContext --- crates/next-api/src/project.rs | 12 +++++------ crates/next-core/src/next_server/context.rs | 8 +++---- .../turbopack-browser/src/chunking_context.rs | 15 +++++++------ .../turbopack-cli/src/dev/web_entry_source.rs | 14 ++++++------- .../src/chunk/chunking_context.rs | 4 ++-- .../crates/turbopack-css/src/chunk/mod.rs | 7 +++---- .../turbopack-ecmascript/src/chunk/item.rs | 2 +- .../crates/turbopack-node/src/evaluate.rs | 13 ++++-------- .../turbopack-node/src/source_map/mod.rs | 14 ++++--------- .../turbopack-node/src/transforms/webpack.rs | 21 +++---------------- .../turbopack-nodejs/src/chunking_context.rs | 15 +++++++------ 11 files changed, 48 insertions(+), 77 deletions(-) diff --git a/crates/next-api/src/project.rs b/crates/next-api/src/project.rs index a5f5d5513deb0..eeb3c5e132b22 100644 --- a/crates/next-api/src/project.rs +++ b/crates/next-api/src/project.rs @@ -693,7 +693,7 @@ impl Project { let node_execution_chunking_context = Vc::upcast( NodeJsChunkingContext::builder( - self.project_path().to_resolved().await?, + self.project_root_path().to_resolved().await?, node_root, node_root, node_root.join("build/chunks".into()).to_resolved().await?, @@ -820,7 +820,7 @@ impl Project { #[turbo_tasks::function] pub(super) fn client_chunking_context(self: Vc) -> Vc> { get_client_chunking_context( - self.project_path(), + self.project_root_path(), self.client_relative_path(), self.next_config().computed_asset_prefix(), self.client_compile_time_info().environment(), @@ -838,7 +838,7 @@ impl Project { if client_assets { get_server_chunking_context_with_client_assets( self.next_mode(), - self.project_path(), + self.project_root_path(), self.node_root(), self.client_relative_path(), self.next_config().computed_asset_prefix(), @@ -849,7 +849,7 @@ impl Project { } else { get_server_chunking_context( self.next_mode(), - self.project_path(), + self.project_root_path(), self.node_root(), self.server_compile_time_info().environment(), self.module_id_strategy(), @@ -866,7 +866,7 @@ impl Project { if client_assets { get_edge_chunking_context_with_client_assets( self.next_mode(), - self.project_path(), + self.project_root_path(), self.node_root(), self.client_relative_path(), self.next_config().computed_asset_prefix(), @@ -877,7 +877,7 @@ impl Project { } else { get_edge_chunking_context( self.next_mode(), - self.project_path(), + self.project_root_path(), self.node_root(), self.edge_compile_time_info().environment(), self.module_id_strategy(), diff --git a/crates/next-core/src/next_server/context.rs b/crates/next-core/src/next_server/context.rs index 6e61e3cd32bbd..e0587ff0814d4 100644 --- a/crates/next-core/src/next_server/context.rs +++ b/crates/next-core/src/next_server/context.rs @@ -974,7 +974,7 @@ pub fn get_server_runtime_entries( #[turbo_tasks::function] pub async fn get_server_chunking_context_with_client_assets( mode: Vc, - project_path: ResolvedVc, + root_path: ResolvedVc, node_root: ResolvedVc, client_root: ResolvedVc, asset_prefix: ResolvedVc>, @@ -987,7 +987,7 @@ pub async fn get_server_chunking_context_with_client_assets( // different server chunking contexts. OR the build chunking context should // support both production and development modes. let mut builder = NodeJsChunkingContext::builder( - project_path, + root_path, node_root, client_root, node_root @@ -1019,7 +1019,7 @@ pub async fn get_server_chunking_context_with_client_assets( #[turbo_tasks::function] pub async fn get_server_chunking_context( mode: Vc, - project_path: ResolvedVc, + root_path: ResolvedVc, node_root: ResolvedVc, environment: ResolvedVc, module_id_strategy: ResolvedVc>, @@ -1030,7 +1030,7 @@ pub async fn get_server_chunking_context( // different server chunking contexts. OR the build chunking context should // support both production and development modes. let mut builder = NodeJsChunkingContext::builder( - project_path, + root_path, node_root, node_root, node_root.join("server/chunks".into()).to_resolved().await?, diff --git a/turbopack/crates/turbopack-browser/src/chunking_context.rs b/turbopack/crates/turbopack-browser/src/chunking_context.rs index 372b3888361a1..f80d6149d2daf 100644 --- a/turbopack/crates/turbopack-browser/src/chunking_context.rs +++ b/turbopack/crates/turbopack-browser/src/chunking_context.rs @@ -112,9 +112,8 @@ impl BrowserChunkingContextBuilder { #[derive(Debug, Clone, Hash)] pub struct BrowserChunkingContext { name: Option, - /// This path get stripped off of chunk paths before generating output asset - /// paths. - context_path: ResolvedVc, + /// The root path of the project + root_path: ResolvedVc, /// Whether to write file sources as file:// paths in source maps should_use_file_source_map_uris: bool, /// This path is used to compute the url to request chunks from @@ -153,7 +152,7 @@ pub struct BrowserChunkingContext { impl BrowserChunkingContext { pub fn builder( - context_path: ResolvedVc, + root_path: ResolvedVc, output_root: ResolvedVc, client_root: ResolvedVc, chunk_root_path: ResolvedVc, @@ -164,7 +163,7 @@ impl BrowserChunkingContext { BrowserChunkingContextBuilder { chunking_context: BrowserChunkingContext { name: None, - context_path, + root_path, output_root, client_root, chunk_root_path, @@ -278,8 +277,8 @@ impl ChunkingContext for BrowserChunkingContext { } #[turbo_tasks::function] - fn context_path(&self) -> Vc { - *self.context_path + fn root_path(&self) -> Vc { + *self.root_path } #[turbo_tasks::function] @@ -299,7 +298,7 @@ impl ChunkingContext for BrowserChunkingContext { extension: RcStr, ) -> Result> { let root_path = self.chunk_root_path; - let name = ident.output_name(*self.context_path, extension).await?; + let name = ident.output_name(*self.root_path, extension).await?; Ok(root_path.join(name.clone_value())) } diff --git a/turbopack/crates/turbopack-cli/src/dev/web_entry_source.rs b/turbopack/crates/turbopack-cli/src/dev/web_entry_source.rs index 4e2f9404972c4..e879bb8cd6cdd 100644 --- a/turbopack/crates/turbopack-cli/src/dev/web_entry_source.rs +++ b/turbopack/crates/turbopack-cli/src/dev/web_entry_source.rs @@ -32,13 +32,13 @@ use crate::{ #[turbo_tasks::function] pub async fn get_client_chunking_context( - project_path: ResolvedVc, + root_path: ResolvedVc, server_root: ResolvedVc, environment: ResolvedVc, ) -> Result>> { Ok(Vc::upcast( BrowserChunkingContext::builder( - project_path, + root_path, server_root, server_root, server_root.join("/_chunks".into()).to_resolved().await?, @@ -92,7 +92,7 @@ pub async fn get_client_runtime_entries( #[turbo_tasks::function] pub async fn create_web_entry_source( - project_path: Vc, + root_path: Vc, execution_context: Vc, entry_requests: Vec>, server_root: Vc, @@ -103,14 +103,14 @@ pub async fn create_web_entry_source( ) -> Result>> { let compile_time_info = get_client_compile_time_info(browserslist_query, node_env); let asset_context = - get_client_asset_context(project_path, execution_context, compile_time_info, node_env); + get_client_asset_context(root_path, execution_context, compile_time_info, node_env); let chunking_context = - get_client_chunking_context(project_path, server_root, compile_time_info.environment()); - let entries = get_client_runtime_entries(project_path, node_env); + get_client_chunking_context(root_path, server_root, compile_time_info.environment()); + let entries = get_client_runtime_entries(root_path, node_env); let runtime_entries = entries.resolve_entries(asset_context); - let origin = PlainResolveOrigin::new(asset_context, project_path.join("_".into())); + let origin = PlainResolveOrigin::new(asset_context, root_path.join("_".into())); let entries = entry_requests .into_iter() .map(|request| async move { diff --git a/turbopack/crates/turbopack-core/src/chunk/chunking_context.rs b/turbopack/crates/turbopack-core/src/chunk/chunking_context.rs index 828151f5e6628..9672475006443 100644 --- a/turbopack/crates/turbopack-core/src/chunk/chunking_context.rs +++ b/turbopack/crates/turbopack-core/src/chunk/chunking_context.rs @@ -71,8 +71,8 @@ pub struct EntryChunkGroupResult { pub trait ChunkingContext { fn name(self: Vc) -> Vc; fn should_use_file_source_map_uris(self: Vc) -> Vc; - // Often the project root - fn context_path(self: Vc) -> Vc; + // The root path of the project + fn root_path(self: Vc) -> Vc; fn output_root(self: Vc) -> Vc; // TODO remove this, a chunking context should not be bound to a specific diff --git a/turbopack/crates/turbopack-css/src/chunk/mod.rs b/turbopack/crates/turbopack-css/src/chunk/mod.rs index 3cd40c7f49789..16bfd39255ce1 100644 --- a/turbopack/crates/turbopack-css/src/chunk/mod.rs +++ b/turbopack/crates/turbopack-css/src/chunk/mod.rs @@ -84,10 +84,9 @@ impl CssChunk { { let source_map = content.source_map.map(|m| m.generate_source_map()); match source_map { - Some(map) => { - (*(fileify_source_map(map, self.chunking_context().context_path()).await?)) - .map(ResolvedVc::upcast) - } + Some(map) => (*(fileify_source_map(map, self.chunking_context().root_path()) + .await?)) + .map(ResolvedVc::upcast), None => None, } } else { diff --git a/turbopack/crates/turbopack-ecmascript/src/chunk/item.rs b/turbopack/crates/turbopack-ecmascript/src/chunk/item.rs index d8061333cfecc..884575eb32585 100644 --- a/turbopack/crates/turbopack-ecmascript/src/chunk/item.rs +++ b/turbopack/crates/turbopack-ecmascript/src/chunk/item.rs @@ -48,7 +48,7 @@ impl EcmascriptChunkItemContent { Ok(EcmascriptChunkItemContent { rewrite_source_path: if *chunking_context.should_use_file_source_map_uris().await? { - Some(chunking_context.context_path().to_resolved().await?) + Some(chunking_context.root_path().to_resolved().await?) } else { None }, diff --git a/turbopack/crates/turbopack-node/src/evaluate.rs b/turbopack/crates/turbopack-node/src/evaluate.rs index 5899446c5292a..75ea1a96fb9ba 100644 --- a/turbopack/crates/turbopack-node/src/evaluate.rs +++ b/turbopack/crates/turbopack-node/src/evaluate.rs @@ -251,7 +251,7 @@ pub async fn get_evaluate_pool( env.iter().map(|(k, v)| (k.clone(), v.clone())).collect(), assets_for_source_mapping, output_root, - chunking_context.context_path().root().to_resolved().await?, + chunking_context.root_path().to_resolved().await?, available_parallelism().map_or(1, |v| v.get()), debug, ); @@ -612,12 +612,7 @@ impl EvaluateContext for BasicEvaluateContext { context_ident: self.context_ident_for_issue, assets_for_source_mapping: pool.assets_for_source_mapping, assets_root: pool.assets_root, - project_dir: self - .chunking_context - .context_path() - .root() - .to_resolved() - .await?, + root_path: self.chunking_context.root_path().to_resolved().await?, } .resolved_cell() .emit(); @@ -668,7 +663,7 @@ pub struct EvaluationIssue { pub error: StructuredError, pub assets_for_source_mapping: ResolvedVc, pub assets_root: ResolvedVc, - pub project_dir: ResolvedVc, + pub root_path: ResolvedVc, } #[turbo_tasks::value_impl] @@ -696,7 +691,7 @@ impl Issue for EvaluationIssue { .print( *self.assets_for_source_mapping, *self.assets_root, - *self.project_dir, + *self.root_path, FormattingMode::Plain, ) .await? diff --git a/turbopack/crates/turbopack-node/src/source_map/mod.rs b/turbopack/crates/turbopack-node/src/source_map/mod.rs index 6fece6de245b9..88aef900433ab 100644 --- a/turbopack/crates/turbopack-node/src/source_map/mod.rs +++ b/turbopack/crates/turbopack-node/src/source_map/mod.rs @@ -275,7 +275,7 @@ impl StructuredError { &self, assets_for_source_mapping: Vc, root: Vc, - project_dir: Vc, + root_path: Vc, formatting_mode: FormattingMode, ) -> Result { let mut message = String::new(); @@ -295,8 +295,7 @@ impl StructuredError { for frame in &self.stack { let frame = frame.unmangle_identifiers(magic); let resolved = - resolve_source_mapping(assets_for_source_mapping, root, project_dir.root(), &frame) - .await; + resolve_source_mapping(assets_for_source_mapping, root, root_path, &frame).await; write_resolved( &mut message, resolved, @@ -310,13 +309,8 @@ impl StructuredError { if let Some(cause) = &self.cause { message.write_str("\nCaused by: ")?; message.write_str( - &Box::pin(cause.print( - assets_for_source_mapping, - root, - project_dir, - formatting_mode, - )) - .await?, + &Box::pin(cause.print(assets_for_source_mapping, root, root_path, formatting_mode)) + .await?, )?; } diff --git a/turbopack/crates/turbopack-node/src/transforms/webpack.rs b/turbopack/crates/turbopack-node/src/transforms/webpack.rs index 2851a8da9a668..9687c3f9d216a 100644 --- a/turbopack/crates/turbopack-node/src/transforms/webpack.rs +++ b/turbopack/crates/turbopack-node/src/transforms/webpack.rs @@ -449,12 +449,7 @@ impl EvaluateContext for WebpackLoaderContext { context_ident: self.context_ident_for_issue, assets_for_source_mapping: pool.assets_for_source_mapping, assets_root: pool.assets_root, - project_dir: self - .chunking_context - .context_path() - .root() - .to_resolved() - .await?, + root_path: self.chunking_context.root_path().to_resolved().await?, } .resolved_cell() .emit(); @@ -499,12 +494,7 @@ impl EvaluateContext for WebpackLoaderContext { severity: severity.resolved_cell(), assets_for_source_mapping: pool.assets_for_source_mapping, assets_root: pool.assets_root, - project_dir: self - .chunking_context - .context_path() - .root() - .to_resolved() - .await?, + project_dir: self.chunking_context.root_path().to_resolved().await?, } .resolved_cell() .emit(); @@ -595,12 +585,7 @@ impl EvaluateContext for WebpackLoaderContext { }, assets_for_source_mapping: pool.assets_for_source_mapping, assets_root: pool.assets_root, - project_dir: self - .chunking_context - .context_path() - .root() - .to_resolved() - .await?, + project_dir: self.chunking_context.root_path().to_resolved().await?, } .resolved_cell() .emit(); diff --git a/turbopack/crates/turbopack-nodejs/src/chunking_context.rs b/turbopack/crates/turbopack-nodejs/src/chunking_context.rs index 1ba976bebbe13..ef6b12ed684b2 100644 --- a/turbopack/crates/turbopack-nodejs/src/chunking_context.rs +++ b/turbopack/crates/turbopack-nodejs/src/chunking_context.rs @@ -83,9 +83,8 @@ impl NodeJsChunkingContextBuilder { #[turbo_tasks::value(serialization = "auto_for_input")] #[derive(Debug, Clone, Hash)] pub struct NodeJsChunkingContext { - /// This path get stripped off of chunk paths before generating output asset - /// paths. - context_path: ResolvedVc, + /// The root path of the project + root_path: ResolvedVc, /// This path is used to compute the url to request chunks or assets from output_root: ResolvedVc, /// This path is used to compute the url to request chunks or assets from @@ -115,7 +114,7 @@ pub struct NodeJsChunkingContext { impl NodeJsChunkingContext { /// Creates a new chunking context builder. pub fn builder( - context_path: ResolvedVc, + root_path: ResolvedVc, output_root: ResolvedVc, client_root: ResolvedVc, chunk_root_path: ResolvedVc, @@ -125,7 +124,7 @@ impl NodeJsChunkingContext { ) -> NodeJsChunkingContextBuilder { NodeJsChunkingContextBuilder { chunking_context: NodeJsChunkingContext { - context_path, + root_path, output_root, client_root, chunk_root_path, @@ -199,8 +198,8 @@ impl ChunkingContext for NodeJsChunkingContext { } #[turbo_tasks::function] - fn context_path(&self) -> Vc { - *self.context_path + fn root_path(&self) -> Vc { + *self.root_path } #[turbo_tasks::function] @@ -247,7 +246,7 @@ impl ChunkingContext for NodeJsChunkingContext { extension: RcStr, ) -> Result> { let root_path = *self.chunk_root_path; - let name = ident.output_name(*self.context_path, extension).await?; + let name = ident.output_name(*self.root_path, extension).await?; Ok(root_path.join(name.clone_value())) } From d5aa221881d44bb4f272db595ce5b36aa9fa7160 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 4 Dec 2024 18:13:33 +0100 Subject: [PATCH 03/12] extract get_relative_path_to --- turbopack/crates/turbo-tasks-fs/src/lib.rs | 78 ++++++++++++++-------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/turbopack/crates/turbo-tasks-fs/src/lib.rs b/turbopack/crates/turbo-tasks-fs/src/lib.rs index 25a00267201f1..c3540178402fa 100644 --- a/turbopack/crates/turbo-tasks-fs/src/lib.rs +++ b/turbopack/crates/turbo-tasks-fs/src/lib.rs @@ -944,6 +944,38 @@ impl ValueToString for DiskFileSystem { } } +pub fn get_relative_path_to(path: &str, other_path: &str) -> String { + fn split(s: &str) -> impl Iterator { + let empty = s.is_empty(); + let mut iterator = s.split('/'); + if empty { + iterator.next(); + } + iterator + } + + let mut self_segments = split(path).peekable(); + let mut other_segments = split(other_path).peekable(); + while self_segments.peek() == other_segments.peek() { + self_segments.next(); + if other_segments.next().is_none() { + return ".".to_string(); + } + } + let mut result = Vec::new(); + if self_segments.peek().is_none() { + result.push("."); + } else { + while self_segments.next().is_some() { + result.push(".."); + } + } + for segment in other_segments { + result.push(segment); + } + result.join("/") +} + #[turbo_tasks::value] #[derive(Debug, Clone)] pub struct FileSystemPath { @@ -1004,34 +1036,8 @@ impl FileSystemPath { if self.fs != other.fs { return None; } - fn split(s: &str) -> impl Iterator { - let empty = s.is_empty(); - let mut iterator = s.split('/'); - if empty { - iterator.next(); - } - iterator - } - let mut self_segments = split(&self.path).peekable(); - let mut other_segments = split(&other.path).peekable(); - while self_segments.peek() == other_segments.peek() { - self_segments.next(); - if other_segments.next().is_none() { - return Some(".".into()); - } - } - let mut result = Vec::new(); - if self_segments.peek().is_none() { - result.push("."); - } else { - while self_segments.next().is_some() { - result.push(".."); - } - } - for segment in other_segments { - result.push(segment); - } - Some(result.join("/").into()) + + Some(get_relative_path_to(&self.path, &other.path).into()) } /// Returns the final component of the FileSystemPath, or an empty string @@ -2353,6 +2359,22 @@ pub fn register() { mod tests { use super::*; + #[test] + fn test_get_relative_path_to() { + assert_eq!(get_relative_path_to("a/b/c", "a/b/c").as_str(), "."); + assert_eq!(get_relative_path_to("a/c/d", "a/b/c").as_str(), "../../b/c"); + assert_eq!(get_relative_path_to("", "a/b/c").as_str(), "./a/b/c"); + assert_eq!(get_relative_path_to("a/b/c", "").as_str(), "../../.."); + assert_eq!( + get_relative_path_to("a/b/c", "c/b/a").as_str(), + "../../../c/b/a" + ); + assert_eq!( + get_relative_path_to("file:///a/b/c", "file:///c/b/a").as_str(), + "../../../c/b/a" + ); + } + #[tokio::test] async fn with_extension() { crate::register(); From 72a42793eba954e58b4ca4b2d697d2c0d23ba0dd Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 4 Dec 2024 19:12:47 +0100 Subject: [PATCH 04/12] trace source map results relative to cwd --- crates/napi/src/next_api/project.rs | 54 ++++++++++++------- crates/next-api/src/project.rs | 2 +- .../next/src/build/swc/generated-native.d.ts | 3 +- packages/next/src/build/swc/index.ts | 9 +++- packages/next/src/build/swc/types.ts | 3 +- .../server/middleware-turbopack.ts | 5 +- 6 files changed, 50 insertions(+), 26 deletions(-) diff --git a/crates/napi/src/next_api/project.rs b/crates/napi/src/next_api/project.rs index 2f92a63c7673c..7280375a6d712 100644 --- a/crates/napi/src/next_api/project.rs +++ b/crates/napi/src/next_api/project.rs @@ -28,7 +28,8 @@ use turbo_tasks::{ get_effects, Completion, Effects, ReadRef, ResolvedVc, TransientInstance, UpdateInfo, Vc, }; use turbo_tasks_fs::{ - util::uri_from_file, DiskFileSystem, FileContent, FileSystem, FileSystemPath, + get_relative_path_to, util::uri_from_file, DiskFileSystem, FileContent, FileSystem, + FileSystemPath, }; use turbopack_core::{ diagnostics::PlainDiagnostic, @@ -1084,6 +1085,7 @@ pub async fn get_source_map( pub async fn project_trace_source( #[napi(ts_arg_type = "{ __napiType: \"Project\" }")] project: External, frame: StackFrame, + current_directory_file_url: String, ) -> napi::Result> { let turbo_tasks = project.turbo_tasks.clone(); let container = project.container; @@ -1120,27 +1122,39 @@ pub async fn project_trace_source( } }; - let project_path_uri = - uri_from_file(project.container.project().project_path(), None).await? + "/"; - let (source_file, is_internal) = - if let Some(source_file) = original_file.strip_prefix(&project_path_uri) { - // Client code uses file:// - (source_file, false) - } else if let Some(source_file) = - original_file.strip_prefix(&*SOURCE_MAP_PREFIX_PROJECT) - { - // Server code uses turbopack://[project] - // TODO should this also be file://? - (source_file, false) - } else if let Some(source_file) = original_file.strip_prefix(SOURCE_MAP_PREFIX) { - // All other code like turbopack://[turbopack] is internal code - (source_file, true) - } else { - bail!("Original file ({}) outside project", original_file) - }; + let project_root_uri = + uri_from_file(project.container.project().project_root_path(), None).await? + "/"; + let (source_file, is_internal) = if original_file.starts_with(&project_root_uri) { + // Client code uses file:// + ( + get_relative_path_to(¤t_directory_file_url, &original_file), + false, + ) + } else if let Some(source_file) = + original_file.strip_prefix(&*SOURCE_MAP_PREFIX_PROJECT) + { + // Server code uses turbopack://[project] + // TODO should this also be file://? + ( + get_relative_path_to( + ¤t_directory_file_url, + &format!("{}/{}", project_root_uri, source_file), + ), + false, + ) + } else if let Some(source_file) = original_file.strip_prefix(SOURCE_MAP_PREFIX) { + // All other code like turbopack://[turbopack] is internal code + (source_file.to_string(), true) + } else { + bail!( + "Original file ({}) outside project ({})", + original_file, + project_root_uri + ) + }; Ok(Some(StackFrame { - file: source_file.to_string(), + file: source_file, method_name: name.as_ref().map(ToString::to_string), line, column, diff --git a/crates/next-api/src/project.rs b/crates/next-api/src/project.rs index eeb3c5e132b22..85eb07f6497aa 100644 --- a/crates/next-api/src/project.rs +++ b/crates/next-api/src/project.rs @@ -631,7 +631,7 @@ impl Project { } #[turbo_tasks::function] - fn project_root_path(self: Vc) -> Vc { + pub fn project_root_path(self: Vc) -> Vc { self.project_fs().root() } diff --git a/packages/next/src/build/swc/generated-native.d.ts b/packages/next/src/build/swc/generated-native.d.ts index 93f6e7f40c9a5..a9b127ed89566 100644 --- a/packages/next/src/build/swc/generated-native.d.ts +++ b/packages/next/src/build/swc/generated-native.d.ts @@ -275,7 +275,8 @@ export interface StackFrame { } export declare function projectTraceSource( project: { __napiType: 'Project' }, - frame: StackFrame + frame: StackFrame, + currentDirectoryFileUrl: string ): Promise export declare function projectGetSourceForAsset( project: { __napiType: 'Project' }, diff --git a/packages/next/src/build/swc/index.ts b/packages/next/src/build/swc/index.ts index 86455e1d0414d..da2f57f3fa1f7 100644 --- a/packages/next/src/build/swc/index.ts +++ b/packages/next/src/build/swc/index.ts @@ -746,9 +746,14 @@ function bindingToApi( } traceSource( - stackFrame: TurbopackStackFrame + stackFrame: TurbopackStackFrame, + currentDirectoryFileUrl: string ): Promise { - return binding.projectTraceSource(this._nativeProject, stackFrame) + return binding.projectTraceSource( + this._nativeProject, + stackFrame, + currentDirectoryFileUrl + ) } getSourceForAsset(filePath: string): Promise { diff --git a/packages/next/src/build/swc/types.ts b/packages/next/src/build/swc/types.ts index fb6ae80142f56..1ca7ded364dd2 100644 --- a/packages/next/src/build/swc/types.ts +++ b/packages/next/src/build/swc/types.ts @@ -207,7 +207,8 @@ export interface Project { getSourceMapSync(filePath: string): string | null traceSource( - stackFrame: TurbopackStackFrame + stackFrame: TurbopackStackFrame, + currentDirectoryFileUrl: string ): Promise updateInfoSubscribe( diff --git a/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts b/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts index 681f9bd766089..f07c63a85dc6b 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts @@ -18,6 +18,7 @@ import { SourceMapConsumer } from 'next/dist/compiled/source-map08' import type { Project, TurbopackStackFrame } from '../../../../build/swc/types' import { getSourceMapFromFile } from '../internal/helpers/get-source-map-from-file' import { findSourceMap, type SourceMapPayload } from 'node:module' +import { pathToFileURL } from 'node:url' function shouldIgnorePath(modulePath: string): boolean { return ( @@ -40,7 +41,9 @@ export async function batchedTraceSource( : undefined if (!file) return - const sourceFrame = await project.traceSource(frame) + const currentDirectoryFileUrl = pathToFileURL(process.cwd()).href + + const sourceFrame = await project.traceSource(frame, currentDirectoryFileUrl) if (!sourceFrame) { return { frame: { From 360489ece5289f7977e94a34ede413c208eb66fa Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 4 Dec 2024 20:58:22 +0100 Subject: [PATCH 05/12] fix reading source file --- crates/napi/src/next_api/project.rs | 63 ++++++++++--------- packages/next/src/build/swc/types.ts | 1 + .../server/middleware-turbopack.ts | 13 ++-- 3 files changed, 42 insertions(+), 35 deletions(-) diff --git a/crates/napi/src/next_api/project.rs b/crates/napi/src/next_api/project.rs index 7280375a6d712..88a329b69e46a 100644 --- a/crates/napi/src/next_api/project.rs +++ b/crates/napi/src/next_api/project.rs @@ -1016,6 +1016,7 @@ pub fn project_update_info_subscribe( pub struct StackFrame { pub is_server: bool, pub is_internal: Option, + pub original_file: Option, pub file: String, // 1-indexed, unlike source map tokens pub line: Option, @@ -1124,37 +1125,41 @@ pub async fn project_trace_source( let project_root_uri = uri_from_file(project.container.project().project_root_path(), None).await? + "/"; - let (source_file, is_internal) = if original_file.starts_with(&project_root_uri) { - // Client code uses file:// - ( - get_relative_path_to(¤t_directory_file_url, &original_file), - false, - ) - } else if let Some(source_file) = - original_file.strip_prefix(&*SOURCE_MAP_PREFIX_PROJECT) - { - // Server code uses turbopack://[project] - // TODO should this also be file://? - ( - get_relative_path_to( - ¤t_directory_file_url, - &format!("{}/{}", project_root_uri, source_file), - ), - false, - ) - } else if let Some(source_file) = original_file.strip_prefix(SOURCE_MAP_PREFIX) { - // All other code like turbopack://[turbopack] is internal code - (source_file.to_string(), true) - } else { - bail!( - "Original file ({}) outside project ({})", - original_file, - project_root_uri - ) - }; + let (file, original_file, is_internal) = + if let Some(source_file) = original_file.strip_prefix(&project_root_uri) { + // Client code uses file:// + ( + get_relative_path_to(¤t_directory_file_url, &original_file), + Some(source_file.to_string()), + false, + ) + } else if let Some(source_file) = + original_file.strip_prefix(&*SOURCE_MAP_PREFIX_PROJECT) + { + // Server code uses turbopack://[project] + // TODO should this also be file://? + ( + get_relative_path_to( + ¤t_directory_file_url, + &format!("{}/{}", project_root_uri, source_file), + ), + Some(source_file.to_string()), + false, + ) + } else if let Some(source_file) = original_file.strip_prefix(SOURCE_MAP_PREFIX) { + // All other code like turbopack://[turbopack] is internal code + (source_file.to_string(), None, true) + } else { + bail!( + "Original file ({}) outside project ({})", + original_file, + project_root_uri + ) + }; Ok(Some(StackFrame { - file: source_file, + file, + original_file, method_name: name.as_ref().map(ToString::to_string), line, column, diff --git a/packages/next/src/build/swc/types.ts b/packages/next/src/build/swc/types.ts index 1ca7ded364dd2..d79b05e70412a 100644 --- a/packages/next/src/build/swc/types.ts +++ b/packages/next/src/build/swc/types.ts @@ -169,6 +169,7 @@ export interface TurbopackStackFrame { isServer: boolean isInternal?: boolean file: string + originalFile?: string /** 1-indexed, unlike source map tokens */ line?: number /** 1-indexed, unlike source map tokens */ diff --git a/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts b/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts index f07c63a85dc6b..40b915e7b237f 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts @@ -59,20 +59,21 @@ export async function batchedTraceSource( } let source = null + const originalFile = sourceFrame.originalFile // Don't look up source for node_modules or internals. These can often be large bundled files. const ignored = - shouldIgnorePath(sourceFrame.file) || + shouldIgnorePath(originalFile ?? sourceFrame.file) || // isInternal means resource starts with turbopack://[turbopack] !!sourceFrame.isInternal - if (sourceFrame && sourceFrame.file && !ignored) { - let sourcePromise = currentSourcesByFile.get(sourceFrame.file) + if (originalFile && !ignored) { + let sourcePromise = currentSourcesByFile.get(originalFile) if (!sourcePromise) { - sourcePromise = project.getSourceForAsset(sourceFrame.file) - currentSourcesByFile.set(sourceFrame.file, sourcePromise) + sourcePromise = project.getSourceForAsset(originalFile) + currentSourcesByFile.set(originalFile, sourcePromise) setTimeout(() => { // Cache file reads for 100ms, as frames will often reference the same // files and can be large. - currentSourcesByFile.delete(sourceFrame.file!) + currentSourcesByFile.delete(originalFile!) }, 100) } source = await sourcePromise From 12644aa1827a3cfa6d3f863ff3377c7c8dffab41 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 4 Dec 2024 23:12:37 +0100 Subject: [PATCH 06/12] return relative paths with ./ prefix --- .../server/middleware-turbopack.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts b/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts index 40b915e7b237f..24d539f56f40c 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts @@ -19,6 +19,7 @@ import type { Project, TurbopackStackFrame } from '../../../../build/swc/types' import { getSourceMapFromFile } from '../internal/helpers/get-source-map-from-file' import { findSourceMap, type SourceMapPayload } from 'node:module' import { pathToFileURL } from 'node:url' +import { isAbsolute } from 'node:path' function shouldIgnorePath(modulePath: string): boolean { return ( @@ -235,10 +236,7 @@ async function nativeTraceSource( '', column: (originalPosition.column ?? 0) + 1, file: originalPosition.source?.startsWith('file://') - ? path.relative( - process.cwd(), - url.fileURLToPath(originalPosition.source) - ) + ? relativeToCwd(originalPosition.source) : originalPosition.source, lineNumber: originalPosition.line ?? 0, // TODO: c&p from async createOriginalStackFrame but why not frame.arguments? @@ -256,6 +254,17 @@ async function nativeTraceSource( return undefined } +function relativeToCwd(file: string): string { + const relPath = path.relative(process.cwd(), url.fileURLToPath(file)) + if (isAbsolute(relPath)) { + return relPath + } + if (relPath.startsWith('../')) { + return relPath + } + return './' + relPath +} + async function createOriginalStackFrame( project: Project, frame: TurbopackStackFrame From 7c89202e615cc4ea06eb06f752b5cbe1be79ea98 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Thu, 5 Dec 2024 16:11:36 +0100 Subject: [PATCH 07/12] improve error when sourcemap lookup throws --- .../components/react-dev-overlay/server/middleware-turbopack.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts b/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts index 24d539f56f40c..547fa97207eca 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts @@ -301,7 +301,7 @@ export function getOverlayMiddleware(project: Project) { try { originalStackFrame = await createOriginalStackFrame(project, frame) } catch (e: any) { - return internalServerError(res, e.message) + return internalServerError(res, e.stack) } if (!originalStackFrame) { From 2c369aac8b08cc5e370c44e8415ab44a7ba123e8 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 4 Dec 2024 15:10:26 +0100 Subject: [PATCH 08/12] add test case --- .../apps/web/app/layout.tsx | 8 + .../web/app/monorepo-package-rsc/page.tsx | 5 + .../web/app/monorepo-package-ssr/page.tsx | 7 + .../apps/web/app/page.tsx | 3 + .../apps/web/app/separate-file.ts | 1 + .../apps/web/app/source-maps-client/page.tsx | 18 ++ .../apps/web/app/source-maps-rsc/page.tsx | 11 + .../apps/web/app/source-maps-ssr/page.tsx | 13 + .../apps/web/next.config.js | 6 + .../apps/web/package.json | 12 + .../non-root-project-monorepo.test.ts | 223 ++++++++++++++++++ .../non-root-project-monorepo/package.json | 10 + .../packages/my-package/package.json | 4 + .../packages/my-package/typescript.ts | 1 + .../server-side-dev-errors/test/index.test.js | 44 ++-- 15 files changed, 339 insertions(+), 27 deletions(-) create mode 100644 test/e2e/app-dir/non-root-project-monorepo/apps/web/app/layout.tsx create mode 100644 test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-rsc/page.tsx create mode 100644 test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-ssr/page.tsx create mode 100644 test/e2e/app-dir/non-root-project-monorepo/apps/web/app/page.tsx create mode 100644 test/e2e/app-dir/non-root-project-monorepo/apps/web/app/separate-file.ts create mode 100644 test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-client/page.tsx create mode 100644 test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-rsc/page.tsx create mode 100644 test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-ssr/page.tsx create mode 100644 test/e2e/app-dir/non-root-project-monorepo/apps/web/next.config.js create mode 100644 test/e2e/app-dir/non-root-project-monorepo/apps/web/package.json create mode 100644 test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts create mode 100644 test/e2e/app-dir/non-root-project-monorepo/package.json create mode 100644 test/e2e/app-dir/non-root-project-monorepo/packages/my-package/package.json create mode 100644 test/e2e/app-dir/non-root-project-monorepo/packages/my-package/typescript.ts diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/layout.tsx b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/layout.tsx new file mode 100644 index 0000000000000..888614deda3ba --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/layout.tsx @@ -0,0 +1,8 @@ +import { ReactNode } from 'react' +export default function Root({ children }: { children: ReactNode }) { + return ( + + {children} + + ) +} diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-rsc/page.tsx b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-rsc/page.tsx new file mode 100644 index 0000000000000..1dcaf5b0c45ca --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-rsc/page.tsx @@ -0,0 +1,5 @@ +import { text } from 'my-package/typescript.ts' + +export default function Page() { + return

{text}

+} diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-ssr/page.tsx b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-ssr/page.tsx new file mode 100644 index 0000000000000..b66994548f6f5 --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-ssr/page.tsx @@ -0,0 +1,7 @@ +'use client' + +import { text } from 'my-package/typescript.ts' + +export default function Page() { + return

{text}

+} diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/page.tsx b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/page.tsx new file mode 100644 index 0000000000000..ff7159d9149fe --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/page.tsx @@ -0,0 +1,3 @@ +export default function Page() { + return

hello world

+} diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/separate-file.ts b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/separate-file.ts new file mode 100644 index 0000000000000..714323f771cd6 --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/separate-file.ts @@ -0,0 +1 @@ +throw new Error('Expected error') diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-client/page.tsx b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-client/page.tsx new file mode 100644 index 0000000000000..b0f2aca4f6f86 --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-client/page.tsx @@ -0,0 +1,18 @@ +'use client' + +import { useEffect } from 'react' + +export default function Page() { + useEffect(function effectCallback() { + innerFunction() + }) + return

Hello Source Maps

+} + +function innerFunction() { + innerArrowFunction() +} + +const innerArrowFunction = () => { + require('../separate-file') +} diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-rsc/page.tsx b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-rsc/page.tsx new file mode 100644 index 0000000000000..3d1e6c8d1b95c --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-rsc/page.tsx @@ -0,0 +1,11 @@ +export default async function Page({ searchParams }) { + innerFunction() +} + +function innerFunction() { + innerArrowFunction() +} + +const innerArrowFunction = () => { + require('../separate-file') +} diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-ssr/page.tsx b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-ssr/page.tsx new file mode 100644 index 0000000000000..d36ad3f94b77b --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-ssr/page.tsx @@ -0,0 +1,13 @@ +'use client' + +export default function Page() { + innerFunction() +} + +function innerFunction() { + innerArrowFunction() +} + +const innerArrowFunction = () => { + require('../separate-file') +} diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/next.config.js b/test/e2e/app-dir/non-root-project-monorepo/apps/web/next.config.js new file mode 100644 index 0000000000000..807126e4cf0bf --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/next.config.js @@ -0,0 +1,6 @@ +/** + * @type {import('next').NextConfig} + */ +const nextConfig = {} + +module.exports = nextConfig diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/package.json b/test/e2e/app-dir/non-root-project-monorepo/apps/web/package.json new file mode 100644 index 0000000000000..ec93f067d692b --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/package.json @@ -0,0 +1,12 @@ +{ + "name": "web", + "version": "0.0.0", + "dependencies": { + "my-package": "workspace:*" + }, + "scripts": { + "dev": "next dev", + "start": "next start", + "build": "next build" + } +} diff --git a/test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts b/test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts new file mode 100644 index 0000000000000..375bc068cb480 --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts @@ -0,0 +1,223 @@ +import { nextTestSetup, FileRef } from 'e2e-utils' +import { + assertHasRedbox, + assertNoRedbox, + getRedboxCallStack, + getRedboxSource, +} from 'next-test-utils' +import * as path from 'path' + +describe('non-root-project-monorepo', () => { + const { next, skipped, isTurbopack, isNextDev } = nextTestSetup({ + files: { + apps: new FileRef(path.resolve(__dirname, 'apps')), + packages: new FileRef(path.resolve(__dirname, 'packages')), + 'pnpm-workspace.yaml': `packages: + - 'apps/*' + - 'packages/*' + `, + }, + packageJson: require('./package.json'), + buildCommand: 'pnpm build', + startCommand: (global as any).isNextDev ? 'pnpm dev' : 'pnpm start', + installCommand: 'pnpm i', + skipDeployment: true, + }) + + if (skipped) { + return + } + + describe('monorepo-package', () => { + it('should work during RSC', async () => { + const $ = await next.render$('/monorepo-package-rsc') + expect($('p').text()).toBe('Hello Typescript') + }) + + it('should work during SSR', async () => { + const $ = await next.render$('/monorepo-package-ssr') + expect($('p').text()).toBe('Hello Typescript') + }) + + it('should work on client-side', async () => { + const browser = await next.browser('/monorepo-package-ssr') + expect(await browser.elementByCss('p').text()).toBe('Hello Typescript') + await assertNoRedbox(browser) + expect(await browser.elementByCss('p').text()).toBe('Hello Typescript') + await browser.close() + }) + }) + + if (isNextDev) { + describe('source-maps', () => { + function normalizeStackTrace(stack: string): string { + const isolatedPath = /file:\/\/.*\/next-install-[^/]+\//g + const nonIsolatedPath = + /file:\/\/.*\/test\/e2e\/app-dir\/non-root-project-monorepo\//g + return stack + .replaceAll(nonIsolatedPath, 'file:///') + .replaceAll(isolatedPath, 'file:///') + } + + it('should work on RSC', async () => { + const browser = await next.browser('/source-maps-rsc') + await assertHasRedbox(browser) + + if (isTurbopack) { + // TODO the function name should be hidden + expect(await getRedboxSource(browser)).toMatchInlineSnapshot(` + "./app/source-maps-rsc/page.tsx (9:28) @ innerArrowFunction + + 7 | } + 8 | + > 9 | const innerArrowFunction = () => { + | ^ + 10 | require('../separate-file') + 11 | } + 12 |" + `) + // TODO stacktrace-parser breaks in some cases with the rsc:// protocol + expect(normalizeStackTrace(await getRedboxCallStack(browser))) + .toMatchInlineSnapshot(` + " + [project]/apps/web/app/separate-file.ts [app-rsc] (ecmascript) (rsc://React/Server/file:///apps/web/.next/server/chunks/ssr/apps_web_8d1c0a._.js (7:7) + innerFunction + ./app/source-maps-rsc/page.tsx (6:3) + Page + ./app/source-maps-rsc/page.tsx (2:3)" + `) + } else { + // TODO the function name is incorrect + expect(await getRedboxSource(browser)).toMatchInlineSnapshot(` + "app/separate-file.ts (1:11) @ Error + + > 1 | throw new Error('Expected error') + | ^ + 2 |" + `) + // TODO webpack runtime code shouldn't be included in stack trace + expect(normalizeStackTrace(await getRedboxCallStack(browser))) + .toMatchInlineSnapshot(` + " + rsc)/./app/separate-file.ts (rsc://React/Server/file:///apps/web/.next/server/app/source-maps-rsc/page.js + __webpack_require__ + file:///apps/web/.next/server/webpack-runtime.js + require + app/source-maps-rsc/page.tsx (10:3) + innerArrowFunction + app/source-maps-rsc/page.tsx (6:3) + innerFunction + app/source-maps-rsc/page.tsx (2:3)" + `) + } + await browser.close() + }) + + it('should work on SSR', async () => { + const browser = await next.browser('/source-maps-ssr') + await assertHasRedbox(browser) + + if (isTurbopack) { + // TODO the function name should be hidden + expect(await getRedboxSource(browser)).toMatchInlineSnapshot(` + "./app/separate-file.ts (1:7) @ [project]/apps/web/app/separate-file.ts [app-client] (ecmascript) + + > 1 | throw new Error('Expected error') + | ^ + 2 |" + `) + expect(normalizeStackTrace(await getRedboxCallStack(browser))) + .toMatchInlineSnapshot(` + "innerArrowFunction + ./app/source-maps-ssr/page.tsx (11:28) + innerFunction + ./app/source-maps-ssr/page.tsx (8:3) + Page + ./app/source-maps-ssr/page.tsx (4:3)" + `) + } else { + // TODO the function name should be hidden + expect(await getRedboxSource(browser)).toMatchInlineSnapshot(` + "app/separate-file.ts (1:7) @ eval + + > 1 | throw new Error('Expected error') + | ^ + 2 |" + `) + // TODO webpack runtime code shouldn't be included in stack trace + expect(normalizeStackTrace(await getRedboxCallStack(browser))) + .toMatchInlineSnapshot(` + "./app/separate-file.ts + file:///apps/web/.next/static/chunks/app/source-maps-ssr/page.js (27:1) + options.factory + file:///apps/web/.next/static/chunks/webpack.js (700:31) + __webpack_require__ + file:///apps/web/.next/static/chunks/webpack.js (37:33) + fn + file:///apps/web/.next/static/chunks/webpack.js (357:21) + require + app/source-maps-ssr/page.tsx (12:3) + innerArrowFunction + app/source-maps-ssr/page.tsx (8:3) + innerFunction + app/source-maps-ssr/page.tsx (4:3)" + `) + } + await browser.close() + }) + + it('should work on client-side', async () => { + const browser = await next.browser('/source-maps-client') + await assertHasRedbox(browser) + + if (isTurbopack) { + // TODO the function name should be hidden + expect(await getRedboxSource(browser)).toMatchInlineSnapshot(` + "./app/separate-file.ts (1:7) @ [project]/apps/web/app/separate-file.ts [app-client] (ecmascript) + + > 1 | throw new Error('Expected error') + | ^ + 2 |" + `) + expect(normalizeStackTrace(await getRedboxCallStack(browser))) + .toMatchInlineSnapshot(` + "innerArrowFunction + ./app/source-maps-client/page.tsx (16:28) + innerFunction + ./app/source-maps-client/page.tsx (13:3) + effectCallback + ./app/source-maps-client/page.tsx (7:5)" + `) + } else { + // TODO the function name should be hidden + expect(await getRedboxSource(browser)).toMatchInlineSnapshot(` + "app/separate-file.ts (1:7) @ eval + + > 1 | throw new Error('Expected error') + | ^ + 2 |" + `) + // TODO webpack runtime code shouldn't be included in stack trace + expect(normalizeStackTrace(await getRedboxCallStack(browser))) + .toMatchInlineSnapshot(` + "./app/separate-file.ts + file:///apps/web/.next/static/chunks/app/source-maps-client/page.js (27:1) + options.factory + file:///apps/web/.next/static/chunks/webpack.js (712:31) + __webpack_require__ + file:///apps/web/.next/static/chunks/webpack.js (37:33) + fn + file:///apps/web/.next/static/chunks/webpack.js (369:21) + require + app/source-maps-client/page.tsx (17:3) + innerArrowFunction + app/source-maps-client/page.tsx (13:3) + innerFunction + app/source-maps-client/page.tsx (7:5)" + `) + } + await browser.close() + }) + }) + } +}) diff --git a/test/e2e/app-dir/non-root-project-monorepo/package.json b/test/e2e/app-dir/non-root-project-monorepo/package.json new file mode 100644 index 0000000000000..95dfb209e2315 --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/package.json @@ -0,0 +1,10 @@ +{ + "name": "monorepo-root", + "version": "0.0.0", + "private": true, + "scripts": { + "build": "pnpm i && pnpm run --dir apps/web build", + "start": "pnpm run --dir apps/web start", + "dev": "pnpm i && pnpm run --dir apps/web dev" + } +} diff --git a/test/e2e/app-dir/non-root-project-monorepo/packages/my-package/package.json b/test/e2e/app-dir/non-root-project-monorepo/packages/my-package/package.json new file mode 100644 index 0000000000000..a18ca3f579196 --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/packages/my-package/package.json @@ -0,0 +1,4 @@ +{ + "name": "my-package", + "version": "0.0.0" +} diff --git a/test/e2e/app-dir/non-root-project-monorepo/packages/my-package/typescript.ts b/test/e2e/app-dir/non-root-project-monorepo/packages/my-package/typescript.ts new file mode 100644 index 0000000000000..110c6254059b1 --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/packages/my-package/typescript.ts @@ -0,0 +1 @@ +export const text: string = 'Hello Typescript' diff --git a/test/integration/server-side-dev-errors/test/index.test.js b/test/integration/server-side-dev-errors/test/index.test.js index 6e84a5826c2b6..0dd632cf83eb8 100644 --- a/test/integration/server-side-dev-errors/test/index.test.js +++ b/test/integration/server-side-dev-errors/test/index.test.js @@ -66,10 +66,9 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + - '\n at getStaticProps (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/gsp.js:6:2)' + + '\n at getStaticProps (../../test/integration/server-side-dev-errors/pages/gsp.js:6:2)' + // Next.js internal frame. Feel free to adjust. // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules '\n at fn' @@ -118,11 +117,9 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. - '\n at getServerSideProps (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/gssp.js:6:2)' + + '\n at getServerSideProps (../../test/integration/server-side-dev-errors/pages/gssp.js:6:2)' + // Next.js internal frame. Feel free to adjust. // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules '\n at fn' @@ -171,11 +168,9 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. - '\n at getServerSideProps (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/blog/[slug].js:6:2)' + + '\n at getServerSideProps (../../test/integration/server-side-dev-errors/pages/blog/[slug].js:6:2)' + // Next.js internal frame. Feel free to adjust. // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules '\n at fn' @@ -224,11 +219,9 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. - '\n at handler (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/api/hello.js:2:2)' + + '\n at handler (../../test/integration/server-side-dev-errors/pages/api/hello.js:2:2)' + // Next.js internal frame. Feel free to adjust. // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules '\n at async' @@ -278,11 +271,9 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() // FIXME(veil): error repeated if (isTurbopack) { - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. - '\n at handler (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/api/blog/[slug].js:2:2)' + + '\n at handler (../../test/integration/server-side-dev-errors/pages/api/blog/[slug].js:2:2)' + // Next.js internal frame. Feel free to adjust. // Not ignore-listed because we're not in an isolated app and Next.js is symlinked so it's not in node_modules '\n at' @@ -329,10 +320,9 @@ describe('server-side dev errors', () => { .trim() // FIXME(veil): error repeated if (isTurbopack) { - // FIXME(veil): Paths include root twice. Bug in generated Turbopack sourcemaps. expect(stderrOutput).toMatchInlineSnapshot(` "Error: catch this rejection - at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-rejection.js:7:19) + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/pages/uncaught-rejection.js:7:19) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | Promise.reject(new Error('catch this rejection')) @@ -341,7 +331,7 @@ describe('server-side dev errors', () => { 9 | return { 10 | props: {}, ⨯ unhandledRejection: Error: catch this rejection - at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-rejection.js:7:19) + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/pages/uncaught-rejection.js:7:19) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | Promise.reject(new Error('catch this rejection')) @@ -350,7 +340,7 @@ describe('server-side dev errors', () => { 9 | return { 10 | props: {}, ⨯ unhandledRejection: Error: catch this rejection - at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-rejection.js:7:19) + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/pages/uncaught-rejection.js:7:19) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | Promise.reject(new Error('catch this rejection')) @@ -411,7 +401,7 @@ describe('server-side dev errors', () => { if (isTurbopack) { expect(stderrOutput).toMatchInlineSnapshot(` "Error: - at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | Promise.reject(new Error()) @@ -420,7 +410,7 @@ describe('server-side dev errors', () => { 9 | return { 10 | props: {}, ⨯ unhandledRejection: Error: - at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | Promise.reject(new Error()) @@ -429,7 +419,7 @@ describe('server-side dev errors', () => { 9 | return { 10 | props: {}, ⨯ unhandledRejection: Error: - at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/pages/uncaught-empty-rejection.js:7:19) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | Promise.reject(new Error()) @@ -489,7 +479,7 @@ describe('server-side dev errors', () => { if (isTurbopack) { expect(stderrOutput).toMatchInlineSnapshot(` "Error: catch this exception - at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-exception.js:7:10) + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/pages/uncaught-exception.js:7:10) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | throw new Error('catch this exception') @@ -498,7 +488,7 @@ describe('server-side dev errors', () => { 9 | return { 10 | props: {}, ⨯ uncaughtException: Error: catch this exception - at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-exception.js:7:10) + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/pages/uncaught-exception.js:7:10) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | throw new Error('catch this exception') @@ -507,7 +497,7 @@ describe('server-side dev errors', () => { 9 | return { 10 | props: {}, ⨯ uncaughtException: Error: catch this exception - at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-exception.js:7:10) + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/pages/uncaught-exception.js:7:10) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | throw new Error('catch this exception') @@ -567,7 +557,7 @@ describe('server-side dev errors', () => { if (isTurbopack) { expect(stderrOutput).toMatchInlineSnapshot(` "Error: - at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | throw new Error() @@ -576,7 +566,7 @@ describe('server-side dev errors', () => { 9 | return { 10 | props: {}, ⨯ uncaughtException: Error: - at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | throw new Error() @@ -585,7 +575,7 @@ describe('server-side dev errors', () => { 9 | return { 10 | props: {}, ⨯ uncaughtException: Error: - at Timeout._onTimeout (../../test/integration/server-side-dev-errors/test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) + at Timeout._onTimeout (../../test/integration/server-side-dev-errors/pages/uncaught-empty-exception.js:7:10) 5 | export async function getServerSideProps() { 6 | setTimeout(() => { > 7 | throw new Error() From 8e111e90be20ced1cb920ffeac37c6d60e118790 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Thu, 5 Dec 2024 16:52:35 +0100 Subject: [PATCH 09/12] restore test check --- .../server-side-dev-errors/test/index.test.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/integration/server-side-dev-errors/test/index.test.js b/test/integration/server-side-dev-errors/test/index.test.js index 0dd632cf83eb8..23e658726f77d 100644 --- a/test/integration/server-side-dev-errors/test/index.test.js +++ b/test/integration/server-side-dev-errors/test/index.test.js @@ -66,6 +66,10 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { + expect(stderrOutput).toStartWith( + ' ⨯ ../../test/integration/server-side-dev-errors/pages/gsp.js (6:3) @ getStaticProps' + + '\n ⨯ ../../test/integration/server-side-dev-errors/pages/gsp.js (6:3) @ getStaticProps' + ) expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + '\n at getStaticProps (../../test/integration/server-side-dev-errors/pages/gsp.js:6:2)' + @@ -117,6 +121,10 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { + expect(stderrOutput).toStartWith( + ' ⨯ ../../test/integration/server-side-dev-errors/pages/gssp.js (6:3) @ getServerSideProps' + + '\n ⨯ ../../test/integration/server-side-dev-errors/pages/gssp.js (6:3) @ getServerSideProps' + ) expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + '\n at getServerSideProps (../../test/integration/server-side-dev-errors/pages/gssp.js:6:2)' + @@ -168,6 +176,10 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { + expect(stderrOutput).toStartWith( + ' ⨯ ../../test/integration/server-side-dev-errors/pages/blog/[slug].js (6:3) @ getServerSideProps' + + '\n ⨯ ../../test/integration/server-side-dev-errors/pages/blog/[slug].js (6:3) @ getServerSideProps' + ) expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + '\n at getServerSideProps (../../test/integration/server-side-dev-errors/pages/blog/[slug].js:6:2)' + @@ -219,6 +231,10 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { + expect(stderrOutput).toStartWith( + ' ⨯ ../../test/integration/server-side-dev-errors/pages/api/hello.js (2:3) @ handler' + + '\n ⨯ ../../test/integration/server-side-dev-errors/pages/api/hello.js (2:3) @ handler' + ) expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + '\n at handler (../../test/integration/server-side-dev-errors/pages/api/hello.js:2:2)' + @@ -271,6 +287,10 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() // FIXME(veil): error repeated if (isTurbopack) { + expect(stderrOutput).toStartWith( + ' ⨯ ../../test/integration/server-side-dev-errors/pages/api/blog/[slug].js (2:3) @ handler' + + '\n ⨯ ../../test/integration/server-side-dev-errors/pages/api/blog/[slug].js (2:3) @ handler' + ) expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + '\n at handler (../../test/integration/server-side-dev-errors/pages/api/blog/[slug].js:2:2)' + From d67dde0712845c27af8deb3d5bab5a29379699e6 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Fri, 6 Dec 2024 08:41:31 +0100 Subject: [PATCH 10/12] don't add ./ to relative paths yet --- crates/napi/src/next_api/project.rs | 67 ++++++++++--------- .../server/middleware-turbopack.ts | 10 +-- .../non-root-project-monorepo.test.ts | 22 +++--- 3 files changed, 50 insertions(+), 49 deletions(-) diff --git a/crates/napi/src/next_api/project.rs b/crates/napi/src/next_api/project.rs index 88a329b69e46a..9738dcf949fa8 100644 --- a/crates/napi/src/next_api/project.rs +++ b/crates/napi/src/next_api/project.rs @@ -1125,37 +1125,44 @@ pub async fn project_trace_source( let project_root_uri = uri_from_file(project.container.project().project_root_path(), None).await? + "/"; - let (file, original_file, is_internal) = - if let Some(source_file) = original_file.strip_prefix(&project_root_uri) { - // Client code uses file:// - ( - get_relative_path_to(¤t_directory_file_url, &original_file), - Some(source_file.to_string()), - false, - ) - } else if let Some(source_file) = - original_file.strip_prefix(&*SOURCE_MAP_PREFIX_PROJECT) - { - // Server code uses turbopack://[project] - // TODO should this also be file://? - ( - get_relative_path_to( - ¤t_directory_file_url, - &format!("{}/{}", project_root_uri, source_file), - ), - Some(source_file.to_string()), - false, - ) - } else if let Some(source_file) = original_file.strip_prefix(SOURCE_MAP_PREFIX) { - // All other code like turbopack://[turbopack] is internal code - (source_file.to_string(), None, true) - } else { - bail!( - "Original file ({}) outside project ({})", - original_file, - project_root_uri + let (file, original_file, is_internal) = if let Some(source_file) = + original_file.strip_prefix(&project_root_uri) + { + // Client code uses file:// + ( + get_relative_path_to(¤t_directory_file_url, &original_file) + // TODO(sokra) remove this to include a ./ here to make it a relative path + .trim_start_matches("./") + .to_string(), + Some(source_file.to_string()), + false, + ) + } else if let Some(source_file) = + original_file.strip_prefix(&*SOURCE_MAP_PREFIX_PROJECT) + { + // Server code uses turbopack://[project] + // TODO should this also be file://? + ( + get_relative_path_to( + ¤t_directory_file_url, + &format!("{}/{}", project_root_uri, source_file), ) - }; + // TODO(sokra) remove this to include a ./ here to make it a relative path + .trim_start_matches("./") + .to_string(), + Some(source_file.to_string()), + false, + ) + } else if let Some(source_file) = original_file.strip_prefix(SOURCE_MAP_PREFIX) { + // All other code like turbopack://[turbopack] is internal code + (source_file.to_string(), None, true) + } else { + bail!( + "Original file ({}) outside project ({})", + original_file, + project_root_uri + ) + }; Ok(Some(StackFrame { file, diff --git a/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts b/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts index 547fa97207eca..d371b78769bc5 100644 --- a/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts +++ b/packages/next/src/client/components/react-dev-overlay/server/middleware-turbopack.ts @@ -19,7 +19,6 @@ import type { Project, TurbopackStackFrame } from '../../../../build/swc/types' import { getSourceMapFromFile } from '../internal/helpers/get-source-map-from-file' import { findSourceMap, type SourceMapPayload } from 'node:module' import { pathToFileURL } from 'node:url' -import { isAbsolute } from 'node:path' function shouldIgnorePath(modulePath: string): boolean { return ( @@ -256,13 +255,8 @@ async function nativeTraceSource( function relativeToCwd(file: string): string { const relPath = path.relative(process.cwd(), url.fileURLToPath(file)) - if (isAbsolute(relPath)) { - return relPath - } - if (relPath.startsWith('../')) { - return relPath - } - return './' + relPath + // TODO(sokra) include a ./ here to make it a relative path + return relPath } async function createOriginalStackFrame( diff --git a/test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts b/test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts index 375bc068cb480..82a84eab42ac4 100644 --- a/test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts +++ b/test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts @@ -66,7 +66,7 @@ describe('non-root-project-monorepo', () => { if (isTurbopack) { // TODO the function name should be hidden expect(await getRedboxSource(browser)).toMatchInlineSnapshot(` - "./app/source-maps-rsc/page.tsx (9:28) @ innerArrowFunction + "app/source-maps-rsc/page.tsx (9:28) @ innerArrowFunction 7 | } 8 | @@ -82,9 +82,9 @@ describe('non-root-project-monorepo', () => { " [project]/apps/web/app/separate-file.ts [app-rsc] (ecmascript) (rsc://React/Server/file:///apps/web/.next/server/chunks/ssr/apps_web_8d1c0a._.js (7:7) innerFunction - ./app/source-maps-rsc/page.tsx (6:3) + app/source-maps-rsc/page.tsx (6:3) Page - ./app/source-maps-rsc/page.tsx (2:3)" + app/source-maps-rsc/page.tsx (2:3)" `) } else { // TODO the function name is incorrect @@ -120,7 +120,7 @@ describe('non-root-project-monorepo', () => { if (isTurbopack) { // TODO the function name should be hidden expect(await getRedboxSource(browser)).toMatchInlineSnapshot(` - "./app/separate-file.ts (1:7) @ [project]/apps/web/app/separate-file.ts [app-client] (ecmascript) + "app/separate-file.ts (1:7) @ [project]/apps/web/app/separate-file.ts [app-client] (ecmascript) > 1 | throw new Error('Expected error') | ^ @@ -129,11 +129,11 @@ describe('non-root-project-monorepo', () => { expect(normalizeStackTrace(await getRedboxCallStack(browser))) .toMatchInlineSnapshot(` "innerArrowFunction - ./app/source-maps-ssr/page.tsx (11:28) + app/source-maps-ssr/page.tsx (11:28) innerFunction - ./app/source-maps-ssr/page.tsx (8:3) + app/source-maps-ssr/page.tsx (8:3) Page - ./app/source-maps-ssr/page.tsx (4:3)" + app/source-maps-ssr/page.tsx (4:3)" `) } else { // TODO the function name should be hidden @@ -173,7 +173,7 @@ describe('non-root-project-monorepo', () => { if (isTurbopack) { // TODO the function name should be hidden expect(await getRedboxSource(browser)).toMatchInlineSnapshot(` - "./app/separate-file.ts (1:7) @ [project]/apps/web/app/separate-file.ts [app-client] (ecmascript) + "app/separate-file.ts (1:7) @ [project]/apps/web/app/separate-file.ts [app-client] (ecmascript) > 1 | throw new Error('Expected error') | ^ @@ -182,11 +182,11 @@ describe('non-root-project-monorepo', () => { expect(normalizeStackTrace(await getRedboxCallStack(browser))) .toMatchInlineSnapshot(` "innerArrowFunction - ./app/source-maps-client/page.tsx (16:28) + app/source-maps-client/page.tsx (16:28) innerFunction - ./app/source-maps-client/page.tsx (13:3) + app/source-maps-client/page.tsx (13:3) effectCallback - ./app/source-maps-client/page.tsx (7:5)" + app/source-maps-client/page.tsx (7:5)" `) } else { // TODO the function name should be hidden From 2b7ca400381ceac49a16a59a913620bc07766441 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Fri, 6 Dec 2024 09:46:08 +0100 Subject: [PATCH 11/12] fix bug and test cases --- crates/napi/src/next_api/project.rs | 2 +- .../apps/web/app/monorepo-package-rsc/page.tsx | 2 +- .../apps/web/app/monorepo-package-ssr/page.tsx | 2 +- .../apps/web/app/source-maps-rsc/page.tsx | 6 +++++- .../apps/web/app/source-maps-ssr/page.tsx | 6 +++++- test/integration/css-minify/test/index.test.js | 2 +- 6 files changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/napi/src/next_api/project.rs b/crates/napi/src/next_api/project.rs index 9738dcf949fa8..709632ea6c0e0 100644 --- a/crates/napi/src/next_api/project.rs +++ b/crates/napi/src/next_api/project.rs @@ -1145,7 +1145,7 @@ pub async fn project_trace_source( ( get_relative_path_to( ¤t_directory_file_url, - &format!("{}/{}", project_root_uri, source_file), + &format!("{}{}", project_root_uri, source_file), ) // TODO(sokra) remove this to include a ./ here to make it a relative path .trim_start_matches("./") diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-rsc/page.tsx b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-rsc/page.tsx index 1dcaf5b0c45ca..9cf7e5c218293 100644 --- a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-rsc/page.tsx +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-rsc/page.tsx @@ -1,4 +1,4 @@ -import { text } from 'my-package/typescript.ts' +import { text } from 'my-package/typescript' export default function Page() { return

{text}

diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-ssr/page.tsx b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-ssr/page.tsx index b66994548f6f5..f0ec54bb6fd3d 100644 --- a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-ssr/page.tsx +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/monorepo-package-ssr/page.tsx @@ -1,6 +1,6 @@ 'use client' -import { text } from 'my-package/typescript.ts' +import { text } from 'my-package/typescript' export default function Page() { return

{text}

diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-rsc/page.tsx b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-rsc/page.tsx index 3d1e6c8d1b95c..d2aa3c4fec7ca 100644 --- a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-rsc/page.tsx +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-rsc/page.tsx @@ -1,5 +1,9 @@ export default async function Page({ searchParams }) { - innerFunction() + // We don't want the build to fail in production + if (process.env.NODE_ENV === 'development') { + innerFunction() + } + return

Hello Source Maps

} function innerFunction() { diff --git a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-ssr/page.tsx b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-ssr/page.tsx index d36ad3f94b77b..c2ff962855524 100644 --- a/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-ssr/page.tsx +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-ssr/page.tsx @@ -1,7 +1,11 @@ 'use client' export default function Page() { - innerFunction() + // We don't want the build to fail in production + if (process.env.NODE_ENV === 'development') { + innerFunction() + } + return

Hello Source Maps

} function innerFunction() { diff --git a/test/integration/css-minify/test/index.test.js b/test/integration/css-minify/test/index.test.js index e337977a7c083..32df7ba184271 100644 --- a/test/integration/css-minify/test/index.test.js +++ b/test/integration/css-minify/test/index.test.js @@ -25,7 +25,7 @@ function runTests() { "/* [project]/test/integration/css-minify/styles/global.css [client] (css) */ .a{--var-1:0;--var-2:0;--var-1:-50%;--var-2:-50%}.b{--var-1:0;--var-2:0;--var-2:-50%} - /*# sourceMappingURL=styles_global_411632.css.map*/ + /*# sourceMappingURL=test_integration_css-minify_styles_global_411632.css.map*/ " `) } else { From 8e343d79be93a06eca8ae1c1df6030be19ee3929 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Thu, 19 Dec 2024 10:59:43 +0100 Subject: [PATCH 12/12] fix test cases --- .../non-root-project-monorepo.test.ts | 90 +++++++++---------- .../server-side-dev-errors/test/index.test.js | 20 ----- 2 files changed, 45 insertions(+), 65 deletions(-) diff --git a/test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts b/test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts index 82a84eab42ac4..eb77983ea7c97 100644 --- a/test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts +++ b/test/e2e/app-dir/non-root-project-monorepo/non-root-project-monorepo.test.ts @@ -66,25 +66,25 @@ describe('non-root-project-monorepo', () => { if (isTurbopack) { // TODO the function name should be hidden expect(await getRedboxSource(browser)).toMatchInlineSnapshot(` - "app/source-maps-rsc/page.tsx (9:28) @ innerArrowFunction - - 7 | } - 8 | - > 9 | const innerArrowFunction = () => { - | ^ - 10 | require('../separate-file') - 11 | } - 12 |" + "app/source-maps-rsc/page.tsx (13:28) @ innerArrowFunction + + 11 | } + 12 | + > 13 | const innerArrowFunction = () => { + | ^ + 14 | require('../separate-file') + 15 | } + 16 |" `) // TODO stacktrace-parser breaks in some cases with the rsc:// protocol expect(normalizeStackTrace(await getRedboxCallStack(browser))) .toMatchInlineSnapshot(` - " - [project]/apps/web/app/separate-file.ts [app-rsc] (ecmascript) (rsc://React/Server/file:///apps/web/.next/server/chunks/ssr/apps_web_8d1c0a._.js (7:7) - innerFunction - app/source-maps-rsc/page.tsx (6:3) - Page - app/source-maps-rsc/page.tsx (2:3)" + " + [project]/apps/web/app/separate-file.ts [app-rsc] (ecmascript) (rsc://React/Server/file:///apps/web/.next/server/chunks/ssr/apps_web_8d1c0a._.js (7:7) + innerFunction + app/source-maps-rsc/page.tsx (10:3) + Page + app/source-maps-rsc/page.tsx (4:5)" `) } else { // TODO the function name is incorrect @@ -98,16 +98,16 @@ describe('non-root-project-monorepo', () => { // TODO webpack runtime code shouldn't be included in stack trace expect(normalizeStackTrace(await getRedboxCallStack(browser))) .toMatchInlineSnapshot(` - " - rsc)/./app/separate-file.ts (rsc://React/Server/file:///apps/web/.next/server/app/source-maps-rsc/page.js - __webpack_require__ - file:///apps/web/.next/server/webpack-runtime.js - require - app/source-maps-rsc/page.tsx (10:3) - innerArrowFunction - app/source-maps-rsc/page.tsx (6:3) - innerFunction - app/source-maps-rsc/page.tsx (2:3)" + " + rsc)/./app/separate-file.ts (rsc://React/Server/file:///apps/web/.next/server/app/source-maps-rsc/page.js + __webpack_require__ + file:///apps/web/.next/server/webpack-runtime.js + require + app/source-maps-rsc/page.tsx (14:3) + innerArrowFunction + app/source-maps-rsc/page.tsx (10:3) + innerFunction + app/source-maps-rsc/page.tsx (4:5)" `) } await browser.close() @@ -128,12 +128,12 @@ describe('non-root-project-monorepo', () => { `) expect(normalizeStackTrace(await getRedboxCallStack(browser))) .toMatchInlineSnapshot(` - "innerArrowFunction - app/source-maps-ssr/page.tsx (11:28) - innerFunction - app/source-maps-ssr/page.tsx (8:3) - Page - app/source-maps-ssr/page.tsx (4:3)" + "innerArrowFunction + app/source-maps-ssr/page.tsx (15:28) + innerFunction + app/source-maps-ssr/page.tsx (12:3) + Page + app/source-maps-ssr/page.tsx (6:5)" `) } else { // TODO the function name should be hidden @@ -147,20 +147,20 @@ describe('non-root-project-monorepo', () => { // TODO webpack runtime code shouldn't be included in stack trace expect(normalizeStackTrace(await getRedboxCallStack(browser))) .toMatchInlineSnapshot(` - "./app/separate-file.ts - file:///apps/web/.next/static/chunks/app/source-maps-ssr/page.js (27:1) - options.factory - file:///apps/web/.next/static/chunks/webpack.js (700:31) - __webpack_require__ - file:///apps/web/.next/static/chunks/webpack.js (37:33) - fn - file:///apps/web/.next/static/chunks/webpack.js (357:21) - require - app/source-maps-ssr/page.tsx (12:3) - innerArrowFunction - app/source-maps-ssr/page.tsx (8:3) - innerFunction - app/source-maps-ssr/page.tsx (4:3)" + "./app/separate-file.ts + file:///apps/web/.next/static/chunks/app/source-maps-ssr/page.js (27:1) + options.factory + file:///apps/web/.next/static/chunks/webpack.js (700:31) + __webpack_require__ + file:///apps/web/.next/static/chunks/webpack.js (37:33) + fn + file:///apps/web/.next/static/chunks/webpack.js (357:21) + require + app/source-maps-ssr/page.tsx (16:3) + innerArrowFunction + app/source-maps-ssr/page.tsx (12:3) + innerFunction + app/source-maps-ssr/page.tsx (6:5)" `) } await browser.close() diff --git a/test/integration/server-side-dev-errors/test/index.test.js b/test/integration/server-side-dev-errors/test/index.test.js index 23e658726f77d..0dd632cf83eb8 100644 --- a/test/integration/server-side-dev-errors/test/index.test.js +++ b/test/integration/server-side-dev-errors/test/index.test.js @@ -66,10 +66,6 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { - expect(stderrOutput).toStartWith( - ' ⨯ ../../test/integration/server-side-dev-errors/pages/gsp.js (6:3) @ getStaticProps' + - '\n ⨯ ../../test/integration/server-side-dev-errors/pages/gsp.js (6:3) @ getStaticProps' - ) expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + '\n at getStaticProps (../../test/integration/server-side-dev-errors/pages/gsp.js:6:2)' + @@ -121,10 +117,6 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { - expect(stderrOutput).toStartWith( - ' ⨯ ../../test/integration/server-side-dev-errors/pages/gssp.js (6:3) @ getServerSideProps' + - '\n ⨯ ../../test/integration/server-side-dev-errors/pages/gssp.js (6:3) @ getServerSideProps' - ) expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + '\n at getServerSideProps (../../test/integration/server-side-dev-errors/pages/gssp.js:6:2)' + @@ -176,10 +168,6 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { - expect(stderrOutput).toStartWith( - ' ⨯ ../../test/integration/server-side-dev-errors/pages/blog/[slug].js (6:3) @ getServerSideProps' + - '\n ⨯ ../../test/integration/server-side-dev-errors/pages/blog/[slug].js (6:3) @ getServerSideProps' - ) expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + '\n at getServerSideProps (../../test/integration/server-side-dev-errors/pages/blog/[slug].js:6:2)' + @@ -231,10 +219,6 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() if (isTurbopack) { - expect(stderrOutput).toStartWith( - ' ⨯ ../../test/integration/server-side-dev-errors/pages/api/hello.js (2:3) @ handler' + - '\n ⨯ ../../test/integration/server-side-dev-errors/pages/api/hello.js (2:3) @ handler' - ) expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + '\n at handler (../../test/integration/server-side-dev-errors/pages/api/hello.js:2:2)' + @@ -287,10 +271,6 @@ describe('server-side dev errors', () => { const stderrOutput = stripAnsi(stderr.slice(stderrIdx)).trim() // FIXME(veil): error repeated if (isTurbopack) { - expect(stderrOutput).toStartWith( - ' ⨯ ../../test/integration/server-side-dev-errors/pages/api/blog/[slug].js (2:3) @ handler' + - '\n ⨯ ../../test/integration/server-side-dev-errors/pages/api/blog/[slug].js (2:3) @ handler' - ) expect(stderrOutput).toContain( ' ⨯ ReferenceError: missingVar is not defined' + '\n at handler (../../test/integration/server-side-dev-errors/pages/api/blog/[slug].js:2:2)' +