11import { copyFileSync , mkdirSync , statSync } from 'node:fs' ;
22import { copyFile , mkdir , readFile } from 'node:fs/promises' ;
3- import { dirname , isAbsolute , join } from 'node:path' ;
3+ import { dirname , isAbsolute , join , resolve } from 'node:path' ;
44import type { NextConfig } from 'next' ;
55import semver from 'semver' ;
66import { getNextBuilder } from './builder.js' ;
@@ -16,7 +16,6 @@ const VERCEL_WORLD_SERVER_EXTERNAL_PACKAGES = [
1616 VERCEL_WORLD_PACKAGE ,
1717 ...VERCEL_WORLD_DEPENDENCY_PACKAGES ,
1818] ;
19-
2019const useWorkflowPattern = / ^ \s * ( [ ' " ] ) u s e w o r k f l o w \1; ? \s * $ / m;
2120const useStepPattern = / ^ \s * ( [ ' " ] ) u s e s t e p \1; ? \s * $ / m;
2221const workflowSerdeImportPattern = / f r o m \s + ( [ ' " ] ) @ w o r k f l o w \/ s e r d e \1/ ;
@@ -232,6 +231,74 @@ function fileExists(path: string): boolean {
232231 }
233232}
234233
234+ function findRootFile ( names : string [ ] , workingDir : string ) : string | undefined {
235+ let current = resolve ( workingDir ) ;
236+
237+ while ( true ) {
238+ for ( const name of names ) {
239+ const file = join ( current , name ) ;
240+ if ( fileExists ( file ) ) {
241+ return file ;
242+ }
243+ }
244+
245+ const parent = dirname ( current ) ;
246+ if ( parent === current ) {
247+ return undefined ;
248+ }
249+ current = parent ;
250+ }
251+ }
252+
253+ function findNextRootFile ( workingDir : string ) : string | undefined {
254+ return (
255+ findRootFile ( [ 'pnpm-workspace.yaml' ] , workingDir ) ??
256+ findRootFile (
257+ [
258+ 'pnpm-lock.yaml' ,
259+ 'package-lock.json' ,
260+ 'yarn.lock' ,
261+ 'bun.lock' ,
262+ 'bun.lockb' ,
263+ ] ,
264+ workingDir
265+ )
266+ ) ;
267+ }
268+
269+ function resolveNextProjectRoot (
270+ nextConfig : NextConfig ,
271+ workingDir : string
272+ ) : string {
273+ const configuredRoot =
274+ nextConfig . outputFileTracingRoot ?? nextConfig . turbopack ?. root ;
275+
276+ if ( configuredRoot ) {
277+ return isAbsolute ( configuredRoot )
278+ ? configuredRoot
279+ : resolve ( workingDir , configuredRoot ) ;
280+ }
281+
282+ let rootFile = findNextRootFile ( workingDir ) ;
283+ if ( ! rootFile ) {
284+ return workingDir ;
285+ }
286+
287+ while ( true ) {
288+ const currentDir = dirname ( rootFile ) ;
289+ const parentDir = dirname ( currentDir ) ;
290+ if ( parentDir === currentDir ) {
291+ return currentDir ;
292+ }
293+
294+ const parentRootFile = findNextRootFile ( parentDir ) ;
295+ if ( ! parentRootFile ) {
296+ return currentDir ;
297+ }
298+ rootFile = parentRootFile ;
299+ }
300+ }
301+
235302function getWorkflowManifestCopyPaths ( {
236303 projectDir,
237304 distDir,
@@ -442,7 +509,9 @@ export function withWorkflow(
442509 nextConfig . turbopack . rules = { } ;
443510 }
444511 const existingRules = nextConfig . turbopack . rules as any ;
445- const nextVersion = resolveNextVersion ( process . cwd ( ) ) ;
512+ const workingDir = process . cwd ( ) ;
513+ const nextVersion = resolveNextVersion ( workingDir ) ;
514+ const projectRoot = resolveNextProjectRoot ( nextConfig , workingDir ) ;
446515 const supportsTurboCondition = semver . gte ( nextVersion , 'v16.0.0' ) ;
447516
448517 const shouldWatch = process . env . NODE_ENV === 'development' ;
@@ -474,9 +543,9 @@ export function withWorkflow(
474543 'jsx' ,
475544 'js' ,
476545 ] ,
477- projectRoot : nextConfig . outputFileTracingRoot ,
478- moduleSpecifierRoot : process . cwd ( ) ,
479- workingDir : process . cwd ( ) ,
546+ projectRoot,
547+ moduleSpecifierRoot : workingDir ,
548+ workingDir,
480549 distDir,
481550 diagnosticsDir : `${ distDir } /diagnostics` ,
482551 buildTarget : 'next' ,
0 commit comments