diff --git a/crates/napi/src/next_api/project.rs b/crates/napi/src/next_api/project.rs index 2f92a63c7673c..709632ea6c0e0 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, @@ -1015,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, @@ -1084,6 +1086,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 +1123,50 @@ 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 (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: source_file.to_string(), + file, + original_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 a5f5d5513deb0..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() } @@ -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/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..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 */ @@ -207,7 +208,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..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 @@ -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: { @@ -56,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 @@ -231,10 +235,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? @@ -252,6 +253,12 @@ async function nativeTraceSource( return undefined } +function relativeToCwd(file: string): string { + const relPath = path.relative(process.cwd(), url.fileURLToPath(file)) + // TODO(sokra) include a ./ here to make it a relative path + return relPath +} + async function createOriginalStackFrame( project: Project, frame: TurbopackStackFrame @@ -288,7 +295,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) { 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..9cf7e5c218293 --- /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' + +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..f0ec54bb6fd3d --- /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' + +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..d2aa3c4fec7ca --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-rsc/page.tsx @@ -0,0 +1,15 @@ +export default async function Page({ searchParams }) { + // We don't want the build to fail in production + if (process.env.NODE_ENV === 'development') { + 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-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..c2ff962855524 --- /dev/null +++ b/test/e2e/app-dir/non-root-project-monorepo/apps/web/app/source-maps-ssr/page.tsx @@ -0,0 +1,17 @@ +'use client' + +export default function Page() { + // We don't want the build to fail in production + if (process.env.NODE_ENV === 'development') { + 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/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..eb77983ea7c97 --- /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 (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 (10:3) + Page + app/source-maps-rsc/page.tsx (4:5)" + `) + } 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 (14:3) + innerArrowFunction + app/source-maps-rsc/page.tsx (10:3) + innerFunction + app/source-maps-rsc/page.tsx (4:5)" + `) + } + 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 (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 + 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 (16:3) + innerArrowFunction + app/source-maps-ssr/page.tsx (12:3) + innerFunction + app/source-maps-ssr/page.tsx (6:5)" + `) + } + 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/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 { 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() 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) { 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(); 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())) }