Skip to content

Commit 2b8237c

Browse files
authored
ref(nextjs): Make build-phase check more robust (#5857)
This makes a few changes to the `isBuild` utility function in the nextjs SDK in order to make its operation less brittle. Currently, it relies on being called at the top level of `index.server.ts`, which ensures that it runs early enough in the build process to be executed in the main thread, before operation has been split out into child processes. (This is important because child processes don't conveniently have the word 'build' in their invocations the way the main thread (invoked by `next build`) does, removing one of `isBuild`'s clues about what phase it's in.) It then sets an environment variable as a clue to future calls to itself, so that, child processes or not, it knows the correct phase. This means, however, that if it stops being used at `index.server.ts`'s top level, and doesn't get called until after the process split, it won't have any way to know whether it's in the build phase or not. This makes two changes to guard against those possibilities: - To ensure that it's always called as early as possible in the build, it is now run independently of any use of its return value, instead storing that value in a constant, which is then used anywhere it's needed. - To provide a backup indicator of the current phase, it now also checks the next-provided `NEXT_PHASE` environment variable. (The reason it doesn't simply rely on this variable to begin with is that it's only set partway through the build process.) As a result of these changes, two other changes are included here: - The export of `isBuild` is deprecated in favor of the computed constant. (Since they're equivalent, no reason to keep exporting them both.) - The local constant `isVercel` has been renamed `IS_VERCEL`, to match `IS_BUILD`.
1 parent ff1229b commit 2b8237c

File tree

2 files changed

+36
-16
lines changed

2 files changed

+36
-16
lines changed

packages/nextjs/src/index.server.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,14 @@ export { ErrorBoundary, showReportDialog, withErrorBoundary } from '@sentry/reac
2222
type GlobalWithDistDir = typeof global & { __rewriteFramesDistDir__: string };
2323
const domain = domainModule as typeof domainModule & { active: (domainModule.Domain & Carrier) | null };
2424

25-
const isVercel = !!process.env.VERCEL;
25+
// Exporting this constant means we can compute it without the linter complaining, even if we stop directly using it in
26+
// this file. It's important that it be computed as early as possible, because one of its indicators is seeing 'build'
27+
// (as in the CLI command `next build`) in `process.argv`. Later on in the build process, everything's been spun out
28+
// into child threads and `argv` turns into ['node', 'path/to/childProcess.js'], so the original indicator is lost. We
29+
// thus want to compute it as soon as the SDK is loaded for the first time, which is normally when the user imports
30+
// `withSentryConfig` into `next.config.js`.
31+
export const IS_BUILD = isBuild();
32+
const IS_VERCEL = !!process.env.VERCEL;
2633

2734
/** Inits the Sentry NextJS SDK on node. */
2835
export function init(options: NextjsOptions): void {
@@ -63,7 +70,7 @@ export function init(options: NextjsOptions): void {
6370

6471
configureScope(scope => {
6572
scope.setTag('runtime', 'node');
66-
if (isVercel) {
73+
if (IS_VERCEL) {
6774
scope.setTag('vercel', true);
6875
}
6976

@@ -124,9 +131,16 @@ function addServerIntegrations(options: NextjsOptions): void {
124131
options.integrations = integrations;
125132
}
126133

134+
// TODO (v8): Remove this
135+
/**
136+
* @deprecated Use the constant `IS_BUILD` instead.
137+
*/
138+
const deprecatedIsBuild = (): boolean => isBuild();
139+
// eslint-disable-next-line deprecation/deprecation
140+
export { deprecatedIsBuild as isBuild };
141+
127142
export type { SentryWebpackPluginOptions } from './config/types';
128143
export { withSentryConfig } from './config';
129-
export { isBuild } from './utils/isBuild';
130144
export {
131145
withSentryGetServerSideProps,
132146
withSentryGetStaticProps,
@@ -142,7 +156,7 @@ export {
142156
// deployments, because the current method of doing the wrapping a) crashes Next 12 apps deployed to Vercel and
143157
// b) doesn't work on those apps anyway. We also don't do it during build, because there's no server running in that
144158
// phase.)
145-
if (!isBuild() && !isVercel) {
159+
if (!IS_BUILD && !IS_VERCEL) {
146160
// Dynamically require the file because even importing from it causes Next 12 to crash on Vercel.
147161
// In environments where the JS file doesn't exist, such as testing, import the TS file.
148162
try {

packages/nextjs/src/utils/isBuild.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,24 @@
22
* Decide if the currently running process is part of the build phase or happening at runtime.
33
*/
44
export function isBuild(): boolean {
5-
// During build, the main process is invoked by
6-
// `node next build`
7-
// and child processes are invoked as
8-
// `node <path>/node_modules/.../jest-worker/processChild.js`.
9-
// The former is (obviously) easy to recognize, but the latter could happen at runtime as well. Fortunately, the main
10-
// process hits this file before any of the child processes do, so we're able to set an env variable which the child
11-
// processes can then check. During runtime, the main process is invoked as
12-
// `node next start`
13-
// or
14-
// `node /var/runtime/index.js`,
15-
// so we never drop into the `if` in the first place.
16-
if (process.argv.includes('build') || process.env.SENTRY_BUILD_PHASE) {
5+
if (
6+
// During build, the main process is invoked by
7+
// `node next build`
8+
// and child processes are invoked as
9+
// `node <path>/node_modules/.../jest-worker/processChild.js`.
10+
// The former is (obviously) easy to recognize, but the latter could happen at runtime as well. Fortunately, the main
11+
// process hits this file before any of the child processes do, so we're able to set an env variable which the child
12+
// processes can then check. During runtime, the main process is invoked as
13+
// `node next start`
14+
// or
15+
// `node /var/runtime/index.js`,
16+
// so we never drop into the `if` in the first place.
17+
process.argv.includes('build') ||
18+
process.env.SENTRY_BUILD_PHASE ||
19+
// This is set by next, but not until partway through the build process, which is why we need the above checks. That
20+
// said, in case this function isn't called until we're in a child process, it can serve as a good backup.
21+
process.env.NEXT_PHASE === 'phase-production-build'
22+
) {
1723
process.env.SENTRY_BUILD_PHASE = 'true';
1824
return true;
1925
}

0 commit comments

Comments
 (0)