From d209facae4b09681516590d3c319fc88d414b041 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Mon, 6 Oct 2025 13:30:20 +0200 Subject: [PATCH 01/10] add new bundler detection --- packages/nextjs/src/config/util.ts | 65 ++++++++ packages/nextjs/test/config/util.test.ts | 179 ++++++++++++++++++++++- 2 files changed, 243 insertions(+), 1 deletion(-) diff --git a/packages/nextjs/src/config/util.ts b/packages/nextjs/src/config/util.ts index de8ad68cac41..9578b752a74e 100644 --- a/packages/nextjs/src/config/util.ts +++ b/packages/nextjs/src/config/util.ts @@ -65,3 +65,68 @@ export function supportsProductionCompileHook(version: string): boolean { return false; } + +/** + * Checks if the current Next.js version uses Turbopack as the default bundler. + * Starting from Next.js 15.6.0-canary.38, turbopack became the default for `next build`. + * + * @param version - Next.js version string to check. + * @returns true if the version uses Turbopack by default + */ +export function isTurbopackDefaultForVersion(version: string): boolean { + if (!version) { + return false; + } + + const { major, minor, prerelease } = parseSemver(version); + + if (major === undefined || minor === undefined) { + return false; + } + + // Next.js 16+ uses turbopack by default + if (major >= 16) { + return true; + } + + // For Next.js 15, only canary versions 15.6.0-canary.38+ use turbopack by default + // Stable 15.x releases still use webpack by default + if (major === 15 && minor >= 6 && prerelease && prerelease.startsWith('canary.')) { + if (minor >= 7) { + return true; + } + const canaryNumber = parseInt(prerelease.split('.')[1] || '0', 10); + if (canaryNumber >= 38) { + return true; + } + } + + return false; +} + +/** + * Determines which bundler is actually being used based on environment variables, + * CLI flags, and Next.js version. + * + * @param nextJsVersion - The Next.js version string + * @returns 'turbopack', 'webpack', or undefined if it cannot be determined + */ +export function detectActiveBundler(nextJsVersion: string | undefined): 'turbopack' | 'webpack' | undefined { + if (process.env.TURBOPACK || process.argv.includes('--turbo')) { + return 'turbopack'; + } + + // Explicit opt-in to webpack via --webpack flag + if (process.argv.includes('--webpack')) { + return 'webpack'; + } + + // Fallback to version-based default behavior + if (nextJsVersion) { + const turbopackIsDefault = isTurbopackDefaultForVersion(nextJsVersion); + return turbopackIsDefault ? 'turbopack' : 'webpack'; + } + + // Unlikely but at this point, we just assume webpack for older behavior + return 'webpack'; +} diff --git a/packages/nextjs/test/config/util.test.ts b/packages/nextjs/test/config/util.test.ts index b31f71705029..c3e9d9c0955d 100644 --- a/packages/nextjs/test/config/util.test.ts +++ b/packages/nextjs/test/config/util.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import * as util from '../../src/config/util'; describe('util', () => { @@ -96,4 +96,181 @@ describe('util', () => { }); }); }); + + describe('isTurbopackDefaultForVersion', () => { + describe('returns true for versions where turbopack is default', () => { + it.each([ + // Next.js 16+ stable versions + ['16.0.0', 'Next.js 16.0.0 stable'], + ['16.0.1', 'Next.js 16.0.1 stable'], + ['16.1.0', 'Next.js 16.1.0 stable'], + ['16.2.5', 'Next.js 16.2.5 stable'], + + // Next.js 16+ pre-release versions + ['16.0.0-rc.1', 'Next.js 16.0.0-rc.1'], + ['16.0.0-canary.1', 'Next.js 16.0.0-canary.1'], + ['16.1.0-beta.2', 'Next.js 16.1.0-beta.2'], + + // Next.js 17+ + ['17.0.0', 'Next.js 17.0.0'], + ['18.0.0', 'Next.js 18.0.0'], + ['20.0.0', 'Next.js 20.0.0'], + + // Next.js 15.6.0-canary.38+ (boundary case) + ['15.6.0-canary.38', 'Next.js 15.6.0-canary.38 (exact threshold)'], + ['15.6.0-canary.39', 'Next.js 15.6.0-canary.39'], + ['15.6.0-canary.40', 'Next.js 15.6.0-canary.40'], + ['15.6.0-canary.100', 'Next.js 15.6.0-canary.100'], + + // Next.js 15.7+ canary versions + ['15.7.0-canary.1', 'Next.js 15.7.0-canary.1'], + ['15.7.0-canary.50', 'Next.js 15.7.0-canary.50'], + ['15.8.0-canary.1', 'Next.js 15.8.0-canary.1'], + ['15.10.0-canary.1', 'Next.js 15.10.0-canary.1'], + ])('returns true for %s (%s)', version => { + expect(util.isTurbopackDefaultForVersion(version)).toBe(true); + }); + }); + + describe('returns false for versions where webpack is still default', () => { + it.each([ + // Next.js 15.6.0-canary.37 and below + ['15.6.0-canary.37', 'Next.js 15.6.0-canary.37 (just below threshold)'], + ['15.6.0-canary.36', 'Next.js 15.6.0-canary.36'], + ['15.6.0-canary.1', 'Next.js 15.6.0-canary.1'], + ['15.6.0-canary.0', 'Next.js 15.6.0-canary.0'], + + // Next.js 15.6.x stable releases (NOT canary) + ['15.6.0', 'Next.js 15.6.0 stable'], + ['15.6.1', 'Next.js 15.6.1 stable'], + ['15.6.2', 'Next.js 15.6.2 stable'], + ['15.6.10', 'Next.js 15.6.10 stable'], + + // Next.js 15.6.x rc releases (NOT canary) + ['15.6.0-rc.1', 'Next.js 15.6.0-rc.1'], + ['15.6.0-rc.2', 'Next.js 15.6.0-rc.2'], + + // Next.js 15.7+ stable releases (NOT canary) + ['15.7.0', 'Next.js 15.7.0 stable'], + ['15.8.0', 'Next.js 15.8.0 stable'], + ['15.10.0', 'Next.js 15.10.0 stable'], + + // Next.js 15.5 and below (all versions) + ['15.5.0', 'Next.js 15.5.0'], + ['15.5.0-canary.100', 'Next.js 15.5.0-canary.100'], + ['15.4.1', 'Next.js 15.4.1'], + ['15.0.0', 'Next.js 15.0.0'], + ['15.0.0-canary.1', 'Next.js 15.0.0-canary.1'], + + // Next.js 14.x and below + ['14.2.0', 'Next.js 14.2.0'], + ['14.0.0', 'Next.js 14.0.0'], + ['14.0.0-canary.50', 'Next.js 14.0.0-canary.50'], + ['13.5.0', 'Next.js 13.5.0'], + ['13.0.0', 'Next.js 13.0.0'], + ['12.0.0', 'Next.js 12.0.0'], + ])('returns false for %s (%s)', version => { + expect(util.isTurbopackDefaultForVersion(version)).toBe(false); + }); + }); + + describe('edge cases', () => { + it.each([ + ['', 'empty string'], + ['invalid', 'invalid version string'], + ['15', 'missing minor and patch'], + ['15.6', 'missing patch'], + ['not.a.version', 'completely invalid'], + ['15.6.0-alpha.1', 'alpha prerelease (not canary)'], + ['15.6.0-beta.1', 'beta prerelease (not canary)'], + ])('returns false for %s (%s)', version => { + expect(util.isTurbopackDefaultForVersion(version)).toBe(false); + }); + }); + + describe('canary number parsing edge cases', () => { + it.each([ + ['15.6.0-canary.', 'canary with no number'], + ['15.6.0-canary.abc', 'canary with non-numeric value'], + ['15.6.0-canary.38.extra', 'canary with extra segments'], + ])('handles malformed canary versions: %s (%s)', version => { + // Should not throw, just return appropriate boolean + expect(() => util.isTurbopackDefaultForVersion(version)).not.toThrow(); + }); + + it('handles canary.38 exactly (boundary)', () => { + expect(util.isTurbopackDefaultForVersion('15.6.0-canary.38')).toBe(true); + }); + + it('handles canary.37 exactly (boundary)', () => { + expect(util.isTurbopackDefaultForVersion('15.6.0-canary.37')).toBe(false); + }); + }); + }); + + describe('detectActiveBundler', () => { + const originalArgv = process.argv; + const originalEnv = process.env; + + beforeEach(() => { + process.argv = [...originalArgv]; + process.env = { ...originalEnv }; + delete process.env.TURBOPACK; + }); + + afterEach(() => { + process.argv = originalArgv; + process.env = originalEnv; + }); + + it('returns turbopack when TURBOPACK env var is set', () => { + process.env.TURBOPACK = '1'; + expect(util.detectActiveBundler('15.5.0')).toBe('turbopack'); + }); + + it('returns webpack when --webpack flag is present', () => { + process.argv.push('--webpack'); + expect(util.detectActiveBundler('16.0.0')).toBe('webpack'); + }); + + it('returns turbopack for Next.js 16+ by default', () => { + expect(util.detectActiveBundler('16.0.0')).toBe('turbopack'); + expect(util.detectActiveBundler('17.0.0')).toBe('turbopack'); + }); + + it('returns turbopack for Next.js 15.6.0-canary.38+', () => { + expect(util.detectActiveBundler('15.6.0-canary.38')).toBe('turbopack'); + expect(util.detectActiveBundler('15.6.0-canary.50')).toBe('turbopack'); + }); + + it('returns webpack for Next.js 15.6.0 stable', () => { + expect(util.detectActiveBundler('15.6.0')).toBe('webpack'); + }); + + it('returns webpack for Next.js 15.5.x and below', () => { + expect(util.detectActiveBundler('15.5.0')).toBe('webpack'); + expect(util.detectActiveBundler('15.0.0')).toBe('webpack'); + expect(util.detectActiveBundler('14.2.0')).toBe('webpack'); + }); + + it('returns webpack when version is undefined', () => { + expect(util.detectActiveBundler(undefined)).toBe('webpack'); + }); + + it('prioritizes TURBOPACK env var over version detection', () => { + process.env.TURBOPACK = '1'; + expect(util.detectActiveBundler('14.0.0')).toBe('turbopack'); + }); + + it('prioritizes --webpack flag over version detection', () => { + process.argv.push('--webpack'); + expect(util.detectActiveBundler('16.0.0')).toBe('webpack'); + }); + + it('prioritizes TURBOPACK env var over --webpack flag', () => { + process.env.TURBOPACK = '1'; + process.argv.push('--webpack'); + expect(util.detectActiveBundler('15.5.0')).toBe('turbopack'); + }); + }); }); From 3e8681e449be3992fc166048a5447a0076bda854 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Mon, 6 Oct 2025 14:00:06 +0200 Subject: [PATCH 02/10] update detection --- .../nextjs/src/config/withSentryConfig.ts | 35 +++++++++---------- .../test/config/withSentryConfig.test.ts | 23 ++---------- 2 files changed, 19 insertions(+), 39 deletions(-) diff --git a/packages/nextjs/src/config/withSentryConfig.ts b/packages/nextjs/src/config/withSentryConfig.ts index ddf761998e50..8285dda46b46 100644 --- a/packages/nextjs/src/config/withSentryConfig.ts +++ b/packages/nextjs/src/config/withSentryConfig.ts @@ -15,7 +15,7 @@ import type { NextConfigObject, SentryBuildOptions, } from './types'; -import { getNextjsVersion, supportsProductionCompileHook } from './util'; +import { detectActiveBundler, getNextjsVersion, supportsProductionCompileHook } from './util'; import { constructWebpackConfigFunction } from './webpack'; let showedExportModeTunnelWarning = false; @@ -258,28 +258,24 @@ function getFinalConfigObject( nextMajor = major; } - const isTurbopack = process.env.TURBOPACK; + const activeBundler = detectActiveBundler(nextJsVersion); + const isTurbopack = activeBundler === 'turbopack'; + const isWebpack = activeBundler === 'webpack'; const isTurbopackSupported = supportsProductionCompileHook(nextJsVersion ?? ''); + // Warn if using turbopack with an unsupported Next.js version if (!isTurbopackSupported && isTurbopack) { - if (process.env.NODE_ENV === 'development') { - // eslint-disable-next-line no-console - console.warn( - `[@sentry/nextjs] WARNING: You are using the Sentry SDK with Turbopack (\`next dev --turbopack\`). The Sentry SDK is compatible with Turbopack on Next.js version 15.4.1 or later. You are currently on ${nextJsVersion}. Please upgrade to a newer Next.js version to use the Sentry SDK with Turbopack.`, - ); - } else if (process.env.NODE_ENV === 'production') { - // eslint-disable-next-line no-console - console.warn( - `[@sentry/nextjs] WARNING: You are using the Sentry SDK with Turbopack (\`next build --turbopack\`). The Sentry SDK is compatible with Turbopack on Next.js version 15.4.1 or later. You are currently on ${nextJsVersion}. Please upgrade to a newer Next.js version to use the Sentry SDK with Turbopack.`, - ); - } + // eslint-disable-next-line no-console + console.warn( + `[@sentry/nextjs] WARNING: You are using the Sentry SDK with Turbopack. The Sentry SDK is compatible with Turbopack on Next.js version 15.4.1 or later. You are currently on ${nextJsVersion}. Please upgrade to a newer Next.js version to use the Sentry SDK with Turbopack.`, + ); } - // webpack case + // Webpack case - warn if trying to use runAfterProductionCompile hook with unsupported Next.js version if ( userSentryOptions.useRunAfterProductionCompileHook && !supportsProductionCompileHook(nextJsVersion ?? '') && - !isTurbopack + isWebpack ) { // eslint-disable-next-line no-console console.warn( @@ -367,10 +363,9 @@ function getFinalConfigObject( ], }, }), - webpack: - isTurbopack || userSentryOptions.disableSentryWebpackConfig - ? incomingUserNextConfigObject.webpack // just return the original webpack config - : constructWebpackConfigFunction({ + ...(isWebpack && !userSentryOptions.disableSentryWebpackConfig + ? { + webpack: constructWebpackConfigFunction({ userNextConfig: incomingUserNextConfigObject, userSentryOptions, releaseName, @@ -378,6 +373,8 @@ function getFinalConfigObject( nextJsVersion, useRunAfterProductionCompileHook: shouldUseRunAfterProductionCompileHook, }), + } + : {}), ...(isTurbopackSupported && isTurbopack ? { turbopack: constructTurbopackConfig({ diff --git a/packages/nextjs/test/config/withSentryConfig.test.ts b/packages/nextjs/test/config/withSentryConfig.test.ts index b437e73dfe75..5ff0349f8c8a 100644 --- a/packages/nextjs/test/config/withSentryConfig.test.ts +++ b/packages/nextjs/test/config/withSentryConfig.test.ts @@ -994,7 +994,7 @@ describe('withSentryConfig', () => { materializeFinalNextConfig(exportedNextConfig); expect(consoleWarnSpy).toHaveBeenCalledWith( - '[@sentry/nextjs] WARNING: You are using the Sentry SDK with Turbopack (`next dev --turbopack`). The Sentry SDK is compatible with Turbopack on Next.js version 15.4.1 or later. You are currently on 15.4.0. Please upgrade to a newer Next.js version to use the Sentry SDK with Turbopack.', + '[@sentry/nextjs] WARNING: You are using the Sentry SDK with Turbopack. The Sentry SDK is compatible with Turbopack on Next.js version 15.4.1 or later. You are currently on 15.4.0. Please upgrade to a newer Next.js version to use the Sentry SDK with Turbopack.', ); consoleWarnSpy.mockRestore(); @@ -1011,7 +1011,7 @@ describe('withSentryConfig', () => { materializeFinalNextConfig(exportedNextConfig); expect(consoleWarnSpy).toHaveBeenCalledWith( - '[@sentry/nextjs] WARNING: You are using the Sentry SDK with Turbopack (`next build --turbopack`). The Sentry SDK is compatible with Turbopack on Next.js version 15.4.1 or later. You are currently on 15.3.9. Please upgrade to a newer Next.js version to use the Sentry SDK with Turbopack.', + '[@sentry/nextjs] WARNING: You are using the Sentry SDK with Turbopack. The Sentry SDK is compatible with Turbopack on Next.js version 15.4.1 or later. You are currently on 15.3.9. Please upgrade to a newer Next.js version to use the Sentry SDK with Turbopack.', ); consoleWarnSpy.mockRestore(); @@ -1115,24 +1115,7 @@ describe('withSentryConfig', () => { materializeFinalNextConfig(exportedNextConfig); expect(consoleWarnSpy).toHaveBeenCalledWith( - '[@sentry/nextjs] WARNING: You are using the Sentry SDK with Turbopack (`next dev --turbopack`). The Sentry SDK is compatible with Turbopack on Next.js version 15.4.1 or later. You are currently on 15.4.0-canary.15. Please upgrade to a newer Next.js version to use the Sentry SDK with Turbopack.', - ); - - consoleWarnSpy.mockRestore(); - }); - - it('does not warn in other environments besides development and production', () => { - process.env.TURBOPACK = '1'; - // @ts-expect-error - NODE_ENV is read-only in types but we need to set it for testing - process.env.NODE_ENV = 'test'; - vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.4.1'); - vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(false); - const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); - - materializeFinalNextConfig(exportedNextConfig); - - expect(consoleWarnSpy).not.toHaveBeenCalledWith( - expect.stringContaining('WARNING: You are using the Sentry SDK with Turbopack'), + '[@sentry/nextjs] WARNING: You are using the Sentry SDK with Turbopack. The Sentry SDK is compatible with Turbopack on Next.js version 15.4.1 or later. You are currently on 15.4.0-canary.15. Please upgrade to a newer Next.js version to use the Sentry SDK with Turbopack.', ); consoleWarnSpy.mockRestore(); From 2b525823075ef1bc3669e1495623baa00489050e Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Mon, 6 Oct 2025 14:34:37 +0200 Subject: [PATCH 03/10] some more tests --- .../test/config/withSentryConfig.test.ts | 276 +++++++++++++++++- 1 file changed, 275 insertions(+), 1 deletion(-) diff --git a/packages/nextjs/test/config/withSentryConfig.test.ts b/packages/nextjs/test/config/withSentryConfig.test.ts index 5ff0349f8c8a..4211d31206d3 100644 --- a/packages/nextjs/test/config/withSentryConfig.test.ts +++ b/packages/nextjs/test/config/withSentryConfig.test.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it, vi } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import * as util from '../../src/config/util'; import { DEFAULT_SERVER_EXTERNAL_PACKAGES } from '../../src/config/withSentryConfig'; import { defaultRuntimePhase, defaultsObject, exportedNextConfig, userNextConfig } from './fixtures'; @@ -269,6 +269,280 @@ describe('withSentryConfig', () => { }); }); + describe('bundler detection with version-based defaults', () => { + const originalTurbopack = process.env.TURBOPACK; + const originalArgv = process.argv; + + beforeEach(() => { + process.argv = [...originalArgv]; + delete process.env.TURBOPACK; + }); + + afterEach(() => { + vi.restoreAllMocks(); + process.env.TURBOPACK = originalTurbopack; + process.argv = originalArgv; + }); + + describe('Next.js 16+ defaults to turbopack', () => { + it('uses turbopack config by default for Next.js 16.0.0', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('16.0.0'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeDefined(); + expect(finalConfig.webpack).toBe(exportedNextConfig.webpack); + }); + + it('uses turbopack config by default for Next.js 17.0.0', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('17.0.0'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeDefined(); + expect(finalConfig.webpack).toBe(exportedNextConfig.webpack); + }); + + it('uses webpack when --webpack flag is present on Next.js 16.0.0', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('16.0.0'); + process.argv.push('--webpack'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeUndefined(); + expect(finalConfig.webpack).toBeInstanceOf(Function); + }); + + it('prioritizes TURBOPACK env var over --webpack flag', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('16.0.0'); + process.env.TURBOPACK = '1'; + process.argv.push('--webpack'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeDefined(); + expect(finalConfig.webpack).toBe(exportedNextConfig.webpack); + }); + }); + + describe('Next.js 15.6.0-canary.38+ defaults to turbopack', () => { + it('uses turbopack config by default for 15.6.0-canary.38', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.6.0-canary.38'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeDefined(); + expect(finalConfig.webpack).toBe(exportedNextConfig.webpack); + }); + + it('uses turbopack config by default for 15.6.0-canary.50', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.6.0-canary.50'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeDefined(); + expect(finalConfig.webpack).toBe(exportedNextConfig.webpack); + }); + + it('uses turbopack config by default for 15.7.0-canary.1', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.7.0-canary.1'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeDefined(); + expect(finalConfig.webpack).toBe(exportedNextConfig.webpack); + }); + + it('uses webpack when --webpack flag is present on 15.6.0-canary.38', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.6.0-canary.38'); + process.argv.push('--webpack'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeUndefined(); + expect(finalConfig.webpack).toBeInstanceOf(Function); + }); + + it('uses webpack when --webpack flag is present on 15.7.0-canary.1', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.7.0-canary.1'); + process.argv.push('--webpack'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeUndefined(); + expect(finalConfig.webpack).toBeInstanceOf(Function); + }); + }); + + describe('Next.js 15.6.0-canary.37 and below defaults to webpack', () => { + it('uses webpack config by default for 15.6.0-canary.37', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.6.0-canary.37'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeUndefined(); + expect(finalConfig.webpack).toBeInstanceOf(Function); + }); + + it('uses webpack config by default for 15.6.0-canary.1', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.6.0-canary.1'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeUndefined(); + expect(finalConfig.webpack).toBeInstanceOf(Function); + }); + + it('uses turbopack when TURBOPACK env var is set on 15.6.0-canary.37', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.6.0-canary.37'); + process.env.TURBOPACK = '1'; + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeDefined(); + expect(finalConfig.webpack).toBe(exportedNextConfig.webpack); + }); + }); + + describe('Next.js 15.6.x stable releases default to webpack', () => { + it('uses webpack config by default for 15.6.0 stable', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.6.0'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeUndefined(); + expect(finalConfig.webpack).toBeInstanceOf(Function); + }); + + it('uses webpack config by default for 15.6.1 stable', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.6.1'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeUndefined(); + expect(finalConfig.webpack).toBeInstanceOf(Function); + }); + + it('uses webpack config by default for 15.7.0 stable', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.7.0'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeUndefined(); + expect(finalConfig.webpack).toBeInstanceOf(Function); + }); + + it('uses turbopack when explicitly requested via env var on 15.6.0 stable', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.6.0'); + process.env.TURBOPACK = '1'; + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeDefined(); + expect(finalConfig.webpack).toBe(exportedNextConfig.webpack); + }); + }); + + describe('older Next.js versions default to webpack', () => { + it.each([['15.5.0'], ['15.0.0'], ['14.2.0'], ['13.5.0']])( + 'uses webpack config by default for Next.js %s', + version => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue(version); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeUndefined(); + expect(finalConfig.webpack).toBeInstanceOf(Function); + }, + ); + + it.each([['15.5.0-canary.100'], ['15.0.0-canary.1'], ['14.2.0-canary.50']])( + 'uses webpack config by default for Next.js %s canary', + version => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue(version); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeUndefined(); + expect(finalConfig.webpack).toBeInstanceOf(Function); + }, + ); + }); + + describe('warnings are shown for unsupported turbopack usage', () => { + let consoleWarnSpy: ReturnType; + + beforeEach(() => { + consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); + }); + + it('warns when using turbopack on unsupported version', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.0.0'); + vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(false); + process.env.TURBOPACK = '1'; + + materializeFinalNextConfig(exportedNextConfig); + + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining('WARNING: You are using the Sentry SDK with Turbopack'), + ); + expect(consoleWarnSpy).toHaveBeenCalledWith(expect.stringContaining('15.0.0')); + }); + + it('does not warn when using turbopack on supported version', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('16.0.0'); + vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(true); + process.env.TURBOPACK = '1'; + + materializeFinalNextConfig(exportedNextConfig); + + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); + + it('does not warn when using webpack', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.0.0'); + vi.spyOn(util, 'supportsProductionCompileHook').mockReturnValue(false); + + materializeFinalNextConfig(exportedNextConfig); + + expect(consoleWarnSpy).not.toHaveBeenCalled(); + }); + }); + + describe('edge cases', () => { + it('defaults to webpack when Next.js version cannot be determined', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue(undefined); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeUndefined(); + expect(finalConfig.webpack).toBeInstanceOf(Function); + }); + + it('uses turbopack when TURBOPACK env var is set even when version is undefined', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue(undefined); + process.env.TURBOPACK = '1'; + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + // Note: turbopack config won't be added when version is undefined because + // isTurbopackSupported will be false, but webpack config should still be skipped + expect(finalConfig.webpack).toBe(exportedNextConfig.webpack); + // Turbopack config is only added when both isTurbopack AND isTurbopackSupported are true + expect(finalConfig.turbopack).toBeUndefined(); + }); + + it('handles malformed version strings gracefully', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('not.a.version'); + + const finalConfig = materializeFinalNextConfig(exportedNextConfig); + + expect(finalConfig.turbopack).toBeUndefined(); + expect(finalConfig.webpack).toBeInstanceOf(Function); + }); + }); + }); + describe('turbopack sourcemap configuration', () => { const originalTurbopack = process.env.TURBOPACK; From 5fa6b29b758dbb4b960b4684648bf380c9e6daa5 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Mon, 6 Oct 2025 14:40:55 +0200 Subject: [PATCH 04/10] update app dir test --- .../e2e-tests/test-applications/nextjs-app-dir/package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json index 2ac1965e180a..fbb1fa6ceee1 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json @@ -4,12 +4,13 @@ "private": true, "scripts": { "build": "next build > .tmp_build_stdout 2> .tmp_build_stderr || (cat .tmp_build_stdout && cat .tmp_build_stderr && exit 1)", + "build:webpack": "next build --webpack > .tmp_build_stdout 2> .tmp_build_stderr || (cat .tmp_build_stdout && cat .tmp_build_stderr && exit 1)", "clean": "npx rimraf node_modules pnpm-lock.yaml", "test:prod": "TEST_ENV=production playwright test", "test:dev": "TEST_ENV=development playwright test", "test:build": "pnpm install && pnpm build", "test:test-build": "pnpm ts-node --script-mode assert-build.ts", - "test:build-canary": "pnpm install && pnpm add next@canary && pnpm add react@beta && pnpm add react-dom@beta && pnpm build", + "test:build-canary": "pnpm install && pnpm add next@canary && pnpm add react@latest && pnpm add react-dom@latest && pnpm build:webpack", "test:build-latest": "pnpm install && pnpm add next@latest && pnpm build", "test:build-13": "pnpm install && pnpm add next@13.5.11 && pnpm build", "test:assert": "pnpm test:test-build && pnpm test:prod && pnpm test:dev" @@ -43,7 +44,7 @@ "optionalVariants": [ { "build-command": "pnpm test:build-canary", - "label": "nextjs-app-dir (canary)" + "label": "nextjs-app-dir (canary, webpack opt-in)" }, { "build-command": "pnpm test:build-latest", From 1bc818769413bdf16ce98c8332843e599b68f04f Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Mon, 6 Oct 2025 14:41:47 +0200 Subject: [PATCH 05/10] update pages dir canary tests --- .../test-applications/nextjs-pages-dir/package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json index 0ef1d9bbcac7..604e597b92c2 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json @@ -4,12 +4,13 @@ "private": true, "scripts": { "build": "next build > .tmp_build_stdout 2> .tmp_build_stderr || (cat .tmp_build_stdout && cat .tmp_build_stderr && exit 1)", + "build:webpack": "next build --webpack > .tmp_build_stdout 2> .tmp_build_stderr || (cat .tmp_build_stdout && cat .tmp_build_stderr && exit 1)", "clean": "npx rimraf node_modules pnpm-lock.yaml", "test:prod": "TEST_ENV=production playwright test", "test:dev": "TEST_ENV=development playwright test", "test:build": "pnpm install && pnpm build", "test:test-build": "pnpm ts-node --script-mode assert-build.ts", - "test:build-canary": "pnpm install && pnpm add next@canary && pnpm add react@beta && pnpm add react-dom@beta && pnpm build", + "test:build-canary": "pnpm install && pnpm add next@canary && pnpm add react@latest && pnpm add react-dom@latest && pnpm build:webpack", "test:build-latest": "pnpm install && pnpm add next@latest && pnpm add react@latest && pnpm add react-dom@latest && pnpm build", "test:build-13": "pnpm install && pnpm add next@13.5.11 && pnpm build", "test:assert": "pnpm test:test-build && pnpm test:prod && pnpm test:dev" @@ -43,7 +44,7 @@ "optionalVariants": [ { "build-command": "pnpm test:build-canary", - "label": "nextjs-pages-dir (canary)" + "label": "nextjs-pages-dir (canary, webpack opt-in)" }, { "build-command": "pnpm test:build-latest", From 2aa387750d89fbbbe3f79e4e4ea2995f31e9a2ab Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Mon, 6 Oct 2025 15:37:46 +0200 Subject: [PATCH 06/10] add start commands for dev mode --- .../test-applications/nextjs-app-dir/package.json | 4 +++- .../nextjs-app-dir/playwright.config.mjs | 10 +++++++++- .../test-applications/nextjs-pages-dir/package.json | 4 +++- .../nextjs-pages-dir/playwright.config.mjs | 10 +++++++++- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json index fbb1fa6ceee1..b5d2a9c143ff 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json @@ -8,6 +8,7 @@ "clean": "npx rimraf node_modules pnpm-lock.yaml", "test:prod": "TEST_ENV=production playwright test", "test:dev": "TEST_ENV=development playwright test", + "test:dev-webpack": "TEST_ENV=development-webpack playwright test", "test:build": "pnpm install && pnpm build", "test:test-build": "pnpm ts-node --script-mode assert-build.ts", "test:build-canary": "pnpm install && pnpm add next@canary && pnpm add react@latest && pnpm add react-dom@latest && pnpm build:webpack", @@ -44,7 +45,8 @@ "optionalVariants": [ { "build-command": "pnpm test:build-canary", - "label": "nextjs-app-dir (canary, webpack opt-in)" + "label": "nextjs-app-dir (canary, webpack opt-in)", + "assert-command": "pnpm test:prod && pnpm test:dev-webpack" }, { "build-command": "pnpm test:build-latest", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/playwright.config.mjs index c675d003853a..494df5bc5432 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/playwright.config.mjs +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/playwright.config.mjs @@ -5,8 +5,16 @@ if (!testEnv) { throw new Error('No test env defined'); } +const getStartCommand = () => { + if (testEnv === 'development-webpack') { + return 'pnpm next dev -p 3030 --webpack'; + } + + return testEnv === 'development' ? 'pnpm next dev -p 3030' : 'pnpm next start -p 3030'; +}; + const config = getPlaywrightConfig({ - startCommand: testEnv === 'development' ? 'pnpm next dev -p 3030' : 'pnpm next start -p 3030', + startCommand: getStartCommand(), port: 3030, }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json index 604e597b92c2..c6ad1885aecb 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json @@ -8,6 +8,7 @@ "clean": "npx rimraf node_modules pnpm-lock.yaml", "test:prod": "TEST_ENV=production playwright test", "test:dev": "TEST_ENV=development playwright test", + "test:dev-webpack": "TEST_ENV=development-webpack playwright test", "test:build": "pnpm install && pnpm build", "test:test-build": "pnpm ts-node --script-mode assert-build.ts", "test:build-canary": "pnpm install && pnpm add next@canary && pnpm add react@latest && pnpm add react-dom@latest && pnpm build:webpack", @@ -44,7 +45,8 @@ "optionalVariants": [ { "build-command": "pnpm test:build-canary", - "label": "nextjs-pages-dir (canary, webpack opt-in)" + "label": "nextjs-pages-dir (canary, webpack opt-in)", + "assert-command": "pnpm test:prod && pnpm test:dev-webpack" }, { "build-command": "pnpm test:build-latest", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/playwright.config.mjs index c675d003853a..494df5bc5432 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/playwright.config.mjs +++ b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/playwright.config.mjs @@ -5,8 +5,16 @@ if (!testEnv) { throw new Error('No test env defined'); } +const getStartCommand = () => { + if (testEnv === 'development-webpack') { + return 'pnpm next dev -p 3030 --webpack'; + } + + return testEnv === 'development' ? 'pnpm next dev -p 3030' : 'pnpm next start -p 3030'; +}; + const config = getPlaywrightConfig({ - startCommand: testEnv === 'development' ? 'pnpm next dev -p 3030' : 'pnpm next start -p 3030', + startCommand: getStartCommand(), port: 3030, }); From 1d46b0e32989150e730ac72e75fda1e8e9ca6327 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Mon, 6 Oct 2025 16:16:29 +0200 Subject: [PATCH 07/10] use canary 40 --- packages/nextjs/src/config/util.ts | 4 +-- packages/nextjs/test/config/util.test.ts | 26 +++++++++---------- .../test/config/withSentryConfig.test.ts | 10 +++---- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/nextjs/src/config/util.ts b/packages/nextjs/src/config/util.ts index 9578b752a74e..40eb65e4e1e9 100644 --- a/packages/nextjs/src/config/util.ts +++ b/packages/nextjs/src/config/util.ts @@ -89,14 +89,14 @@ export function isTurbopackDefaultForVersion(version: string): boolean { return true; } - // For Next.js 15, only canary versions 15.6.0-canary.38+ use turbopack by default + // For Next.js 15, only canary versions 15.6.0-canary.40+ use turbopack by default // Stable 15.x releases still use webpack by default if (major === 15 && minor >= 6 && prerelease && prerelease.startsWith('canary.')) { if (minor >= 7) { return true; } const canaryNumber = parseInt(prerelease.split('.')[1] || '0', 10); - if (canaryNumber >= 38) { + if (canaryNumber >= 40) { return true; } } diff --git a/packages/nextjs/test/config/util.test.ts b/packages/nextjs/test/config/util.test.ts index c3e9d9c0955d..2dcff9889364 100644 --- a/packages/nextjs/test/config/util.test.ts +++ b/packages/nextjs/test/config/util.test.ts @@ -116,10 +116,10 @@ describe('util', () => { ['18.0.0', 'Next.js 18.0.0'], ['20.0.0', 'Next.js 20.0.0'], - // Next.js 15.6.0-canary.38+ (boundary case) - ['15.6.0-canary.38', 'Next.js 15.6.0-canary.38 (exact threshold)'], - ['15.6.0-canary.39', 'Next.js 15.6.0-canary.39'], - ['15.6.0-canary.40', 'Next.js 15.6.0-canary.40'], + // Next.js 15.6.0-canary.40+ (boundary case) + ['15.6.0-canary.40', 'Next.js 15.6.0-canary.40 (exact threshold)'], + ['15.6.0-canary.41', 'Next.js 15.6.0-canary.41'], + ['15.6.0-canary.42', 'Next.js 15.6.0-canary.42'], ['15.6.0-canary.100', 'Next.js 15.6.0-canary.100'], // Next.js 15.7+ canary versions @@ -134,10 +134,10 @@ describe('util', () => { describe('returns false for versions where webpack is still default', () => { it.each([ - // Next.js 15.6.0-canary.37 and below - ['15.6.0-canary.37', 'Next.js 15.6.0-canary.37 (just below threshold)'], + // Next.js 15.6.0-canary.39 and below + ['15.6.0-canary.39', 'Next.js 15.6.0-canary.39 (just below threshold)'], ['15.6.0-canary.36', 'Next.js 15.6.0-canary.36'], - ['15.6.0-canary.1', 'Next.js 15.6.0-canary.1'], + ['15.6.0-canary.38', 'Next.js 15.6.0-canary.38'], ['15.6.0-canary.0', 'Next.js 15.6.0-canary.0'], // Next.js 15.6.x stable releases (NOT canary) @@ -198,12 +198,12 @@ describe('util', () => { expect(() => util.isTurbopackDefaultForVersion(version)).not.toThrow(); }); - it('handles canary.38 exactly (boundary)', () => { - expect(util.isTurbopackDefaultForVersion('15.6.0-canary.38')).toBe(true); + it('handles canary.40 exactly (boundary)', () => { + expect(util.isTurbopackDefaultForVersion('15.6.0-canary.40')).toBe(true); }); - it('handles canary.37 exactly (boundary)', () => { - expect(util.isTurbopackDefaultForVersion('15.6.0-canary.37')).toBe(false); + it('handles canary.39 exactly (boundary)', () => { + expect(util.isTurbopackDefaultForVersion('15.6.0-canary.39')).toBe(false); }); }); }); @@ -238,8 +238,8 @@ describe('util', () => { expect(util.detectActiveBundler('17.0.0')).toBe('turbopack'); }); - it('returns turbopack for Next.js 15.6.0-canary.38+', () => { - expect(util.detectActiveBundler('15.6.0-canary.38')).toBe('turbopack'); + it('returns turbopack for Next.js 15.6.0-canary.40+', () => { + expect(util.detectActiveBundler('15.6.0-canary.40')).toBe('turbopack'); expect(util.detectActiveBundler('15.6.0-canary.50')).toBe('turbopack'); }); diff --git a/packages/nextjs/test/config/withSentryConfig.test.ts b/packages/nextjs/test/config/withSentryConfig.test.ts index 4211d31206d3..f1f46c6fc6f2 100644 --- a/packages/nextjs/test/config/withSentryConfig.test.ts +++ b/packages/nextjs/test/config/withSentryConfig.test.ts @@ -325,9 +325,9 @@ describe('withSentryConfig', () => { }); }); - describe('Next.js 15.6.0-canary.38+ defaults to turbopack', () => { - it('uses turbopack config by default for 15.6.0-canary.38', () => { - vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.6.0-canary.38'); + describe('Next.js 15.6.0-canary.40+ defaults to turbopack', () => { + it('uses turbopack config by default for 15.6.0-canary.40', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.6.0-canary.40'); const finalConfig = materializeFinalNextConfig(exportedNextConfig); @@ -353,8 +353,8 @@ describe('withSentryConfig', () => { expect(finalConfig.webpack).toBe(exportedNextConfig.webpack); }); - it('uses webpack when --webpack flag is present on 15.6.0-canary.38', () => { - vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.6.0-canary.38'); + it('uses webpack when --webpack flag is present on 15.6.0-canary.40', () => { + vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.6.0-canary.40'); process.argv.push('--webpack'); const finalConfig = materializeFinalNextConfig(exportedNextConfig); From a0df026eb9b17186e6056556c68098a0cb60ccd5 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Thu, 9 Oct 2025 09:31:31 +0200 Subject: [PATCH 08/10] skip webpack dev tests --- .../nextjs-app-dir/tests/client-errors.test.ts | 2 +- .../nextjs-app-dir/tests/devErrorSymbolification.test.ts | 2 +- .../nextjs-app-dir/tests/transactions.test.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts index 580368f4b9a1..a5a747f56456 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts @@ -6,7 +6,7 @@ const packageJson = require('../package.json'); test('Sends a client-side exception to Sentry', async ({ page }) => { const nextjsVersion = packageJson.dependencies.next; const nextjsMajor = Number(nextjsVersion.split('.')[0]); - const isDevMode = process.env.TEST_ENV === 'development'; + const isDevMode = process.env.TEST_ENV.includes('development'); await page.goto('/'); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/devErrorSymbolification.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/devErrorSymbolification.test.ts index ea9fd112778f..7a186bb7c3e3 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/devErrorSymbolification.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/devErrorSymbolification.test.ts @@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; test('should have symbolicated dev errors', async ({ page }) => { - test.skip(process.env.TEST_ENV !== 'development', 'should be skipped for non-dev mode'); + test.skip(!process.env.TEST_ENV.includes('development'), 'should be skipped for non-dev mode'); await page.goto('/'); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts index 3ad9e43f06db..ed0b2bf18384 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts @@ -6,7 +6,7 @@ const packageJson = require('../package.json'); test('Sends a pageload transaction', async ({ page }) => { const nextjsVersion = packageJson.dependencies.next; const nextjsMajor = Number(nextjsVersion.split('.')[0]); - const isDevMode = process.env.TEST_ENV === 'development'; + const isDevMode = process.env.TEST_ENV.includes('development'); const pageloadTransactionEventPromise = waitForTransaction('nextjs-app-dir', transactionEvent => { return transactionEvent?.contexts?.trace?.op === 'pageload' && transactionEvent?.transaction === '/'; @@ -79,7 +79,7 @@ test('Should send a wrapped server action as a child of a nextjs transaction', a const nextjsVersion = packageJson.dependencies.next; const nextjsMajor = Number(nextjsVersion.split('.')[0]); test.skip(!isNaN(nextjsMajor) && nextjsMajor < 14, 'only applies to nextjs apps >= version 14'); - test.skip(process.env.TEST_ENV === 'development', 'this magically only works in production'); + test.skip(process.env.TEST_ENV.includes('development'), 'this magically only works in production'); const nextjsPostTransactionPromise = waitForTransaction('nextjs-app-dir', async transactionEvent => { return ( From d05629f8a07e8c385639be308b4e5b55ff3e2e5c Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Thu, 9 Oct 2025 10:51:54 +0200 Subject: [PATCH 09/10] test conditions for development-webpack --- .../nextjs-app-dir/tests/client-errors.test.ts | 2 +- .../nextjs-app-dir/tests/devErrorSymbolification.test.ts | 3 ++- .../nextjs-app-dir/tests/transactions.test.ts | 5 +++-- .../nextjs-pages-dir/tests/devErrorSymbolification.test.ts | 2 +- .../nextjs-pages-dir/tests/transactions.test.ts | 4 ++-- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts index a5a747f56456..0044271568df 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts @@ -6,7 +6,7 @@ const packageJson = require('../package.json'); test('Sends a client-side exception to Sentry', async ({ page }) => { const nextjsVersion = packageJson.dependencies.next; const nextjsMajor = Number(nextjsVersion.split('.')[0]); - const isDevMode = process.env.TEST_ENV.includes('development'); + const isDevMode = !!process.env.TEST_ENV && process.env.TEST_ENV.includes('development'); await page.goto('/'); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/devErrorSymbolification.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/devErrorSymbolification.test.ts index 7a186bb7c3e3..1f48cbe3f1f5 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/devErrorSymbolification.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/devErrorSymbolification.test.ts @@ -2,7 +2,8 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; test('should have symbolicated dev errors', async ({ page }) => { - test.skip(!process.env.TEST_ENV.includes('development'), 'should be skipped for non-dev mode'); + const isDevMode = !!process.env.TEST_ENV && process.env.TEST_ENV.includes('development'); + test.skip(!isDevMode, 'should be skipped for non-dev mode'); await page.goto('/'); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts index ed0b2bf18384..29161d3d9275 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts @@ -6,7 +6,7 @@ const packageJson = require('../package.json'); test('Sends a pageload transaction', async ({ page }) => { const nextjsVersion = packageJson.dependencies.next; const nextjsMajor = Number(nextjsVersion.split('.')[0]); - const isDevMode = process.env.TEST_ENV.includes('development'); + const isDevMode = !!process.env.TEST_ENV && process.env.TEST_ENV.includes('development'); const pageloadTransactionEventPromise = waitForTransaction('nextjs-app-dir', transactionEvent => { return transactionEvent?.contexts?.trace?.op === 'pageload' && transactionEvent?.transaction === '/'; @@ -78,8 +78,9 @@ test('Should send a transaction for instrumented server actions', async ({ page test('Should send a wrapped server action as a child of a nextjs transaction', async ({ page }) => { const nextjsVersion = packageJson.dependencies.next; const nextjsMajor = Number(nextjsVersion.split('.')[0]); + const isDevMode = !!process.env.TEST_ENV && process.env.TEST_ENV.includes('development'); test.skip(!isNaN(nextjsMajor) && nextjsMajor < 14, 'only applies to nextjs apps >= version 14'); - test.skip(process.env.TEST_ENV.includes('development'), 'this magically only works in production'); + test.skip(isDevMode, 'this magically only works in production'); const nextjsPostTransactionPromise = waitForTransaction('nextjs-app-dir', async transactionEvent => { return ( diff --git a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/devErrorSymbolification.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/devErrorSymbolification.test.ts index c846fab3464c..010c49ae3aa9 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/devErrorSymbolification.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/devErrorSymbolification.test.ts @@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test'; import { waitForError } from '@sentry-internal/test-utils'; test('should have symbolicated dev errors', async ({ page }) => { - test.skip(process.env.TEST_ENV !== 'development', 'should be skipped for non-dev mode'); + test.skip(!process.env.TEST_ENV?.includes('development'), 'should be skipped for non-dev mode'); await page.goto('/'); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/transactions.test.ts index 918297898de7..6099052a07d1 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/transactions.test.ts @@ -1,12 +1,12 @@ import { expect, test } from '@playwright/test'; -import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; +import { waitForTransaction } from '@sentry-internal/test-utils'; const packageJson = require('../package.json'); test('Sends a pageload transaction', async ({ page }) => { const nextjsVersion = packageJson.dependencies.next; const nextjsMajor = Number(nextjsVersion.split('.')[0]); - const isDevMode = process.env.TEST_ENV === 'development'; + const isDevMode = process.env.TEST_ENV?.includes('development'); const pageloadTransactionEventPromise = waitForTransaction('nextjs-pages-dir', transactionEvent => { return transactionEvent?.contexts?.trace?.op === 'pageload' && transactionEvent?.transaction === '/'; From 9ee2db2fe4c24d73d021a99e361f6113470c647b Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Thu, 9 Oct 2025 10:53:23 +0200 Subject: [PATCH 10/10] only test prod for webpack --- .../e2e-tests/test-applications/nextjs-app-dir/package.json | 2 +- .../e2e-tests/test-applications/nextjs-pages-dir/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json index b5d2a9c143ff..15c337c33c3a 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/package.json @@ -46,7 +46,7 @@ { "build-command": "pnpm test:build-canary", "label": "nextjs-app-dir (canary, webpack opt-in)", - "assert-command": "pnpm test:prod && pnpm test:dev-webpack" + "assert-command": "pnpm test:prod" }, { "build-command": "pnpm test:build-latest", diff --git a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json index c6ad1885aecb..42c321a2f93a 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json +++ b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/package.json @@ -46,7 +46,7 @@ { "build-command": "pnpm test:build-canary", "label": "nextjs-pages-dir (canary, webpack opt-in)", - "assert-command": "pnpm test:prod && pnpm test:dev-webpack" + "assert-command": "pnpm test:prod" }, { "build-command": "pnpm test:build-latest",