Skip to content

Commit dc9fe45

Browse files
committed
fix: handle removal of outputTracingFileRoot from required-server-files
1 parent c22d123 commit dc9fe45

File tree

1 file changed

+34
-6
lines changed

1 file changed

+34
-6
lines changed

src/build/plugin-context.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { existsSync, readFileSync } from 'node:fs'
22
import { readFile } from 'node:fs/promises'
33
import { createRequire } from 'node:module'
4-
import { join, relative, resolve } from 'node:path'
5-
import { join as posixJoin, relative as posixRelative } from 'node:path/posix'
4+
import { join, relative, resolve, sep } from 'node:path'
5+
import { join as posixJoin, relative as posixRelative, sep as posixSep } from 'node:path/posix'
66
import { fileURLToPath } from 'node:url'
77

88
import type {
@@ -20,6 +20,8 @@ import type {
2020
} from 'next-with-cache-handler-v2/dist/build/index.js'
2121
import { satisfies } from 'semver'
2222

23+
const toPosixPath = (path: string) => path.split(sep).join(posixSep)
24+
2325
const MODULE_DIR = fileURLToPath(new URL('.', import.meta.url))
2426
const PLUGIN_DIR = join(MODULE_DIR, '../..')
2527
const DEFAULT_PUBLISH_DIR = '.next'
@@ -88,12 +90,38 @@ export class PluginContext {
8890
* The root directory for output file tracing. Paths inside standalone directory preserve paths of project, relative to this directory.
8991
*/
9092
get outputFileTracingRoot(): string {
91-
return (
93+
// Up until https://github.com/vercel/next.js/pull/86812 we had direct access to computed value of it with following
94+
const outputFileTracingRootFromRequiredServerFiles =
9295
this.requiredServerFiles.config.outputFileTracingRoot ??
9396
// fallback for older Next.js versions that don't have outputFileTracingRoot in the config, but had it in config.experimental
94-
this.requiredServerFiles.config.experimental.outputFileTracingRoot ??
95-
process.cwd()
96-
)
97+
this.requiredServerFiles.config.experimental.outputFileTracingRoot
98+
if (outputFileTracingRootFromRequiredServerFiles) {
99+
return outputFileTracingRootFromRequiredServerFiles
100+
}
101+
102+
if (!this.relativeAppDir.includes('..')) {
103+
// For newer Next.js versions this is not written to the output directly anymore, but we can use appDir and relativeAppDir to compute it.
104+
// This assumes that relative app dir will never contain '..' segments. Some monorepos support workspaces outside of the monorepo root (verified with pnpm)
105+
// However Next.js itself have some limits on it:
106+
// - turbopack by default would throw "Module not found: Can't resolve '<name_of_package_outside_of_root>'"
107+
// forcing user to manually set `outputFileTracingRoot` in next.config which will impact `appDir` and `relativeAppDir` preserving the lack of '..' in `relativeAppDir`
108+
// - webpack case depends on wether dependency is marked as external or not:
109+
// - if it's marked as external then standalone while working locally, it would never work when someone tries to deploy it (and not just on Netlify, but also in fully self-hosted scenarios)
110+
// because parts of application would be outside of "standalone" directory
111+
// - if it's not marked as external it will be included in next.js produced chunks
112+
113+
const depth = toPosixPath(this.relativeAppDir).split(posixSep).length
114+
const computedOutputFileTracingRoot = resolve(
115+
this.requiredServerFiles.appDir,
116+
...Array.from<string>({ length: depth }).fill('..'),
117+
)
118+
return computedOutputFileTracingRoot
119+
}
120+
121+
// if relativeAppDir contains '..', we can't actually figure out the outputFileTracingRoot
122+
// so best fallback is to just cwd() which won't work in wild edge cases, but there is no way of getting anything better
123+
// if it's not correct it will cause build failures later when assembling a server handler function
124+
return process.cwd()
97125
}
98126

99127
/**

0 commit comments

Comments
 (0)