Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/nextjs/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@ export function init(options: BrowserOptions): Client | undefined {

if (!DEBUG_BUILD && options.debug) {
consoleSandbox(() => {
// TODO(v12): Remove the `webpack.treeshake` mention together with that option
// eslint-disable-next-line no-console
console.warn(
'[@sentry/nextjs] You have enabled `debug: true`, but Sentry debug logging was removed from your bundle (likely via `withSentryConfig({ disableLogger: true })` / `webpack.treeshake.removeDebugLogging: true`). Set that option to `false` to see Sentry debug output.',
'[@sentry/nextjs] You have enabled `debug: true`, but Sentry debug logging was removed from your bundle (likely via `bundleSizeOptimizations.excludeDebugStatements: true`, `withSentryConfig({ disableLogger: true })` or `webpack.treeshake.removeDebugLogging: true`). Set that option to `false` to see Sentry debug output.',
);
});
}
Expand Down
6 changes: 5 additions & 1 deletion packages/nextjs/src/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export type NextConfigObject = {
outputFileTracingIncludes?: Record<string, string[]>; // next >= v15.0.0
turbopack?: TurbopackOptions;
compiler?: {
define?: Record<string, string | boolean>;
runAfterProductionCompile?: (context: { distDir: string; projectDir: string }) => Promise<void> | void;
};
};
Expand Down Expand Up @@ -110,6 +111,9 @@ export type SentryBuildWebpackOptions = {

/**
* Tree-shaking options to help reduce the size of the Sentry SDK bundle.
*
* Prefer the top-level `bundleSizeOptimizations` option, which works for both webpack and Turbopack builds.
* If both are set, `bundleSizeOptimizations` takes precedence.
*/
treeshake?: {
/**
Expand Down Expand Up @@ -146,7 +150,7 @@ export type SentryBuildWebpackOptions = {
* This has no effect if you did not add `replayIntegration`.
*/
excludeReplayCompressionWorker?: boolean;
};
}; // TODO: Deprecate this option and its nested options in favor of `bundleSizeOptimizations` closer to v12

/**
* Options to be passed directly to the Sentry Webpack Plugin (`@sentry/webpack-plugin`) that ships with the Sentry SDK.
Expand Down
41 changes: 25 additions & 16 deletions packages/nextjs/src/config/webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,7 @@ export function constructWebpackConfigFunction({
}
}

if (userSentryOptions.webpack?.treeshake) {
setupTreeshakingFromConfig(userSentryOptions, newConfig, buildContext);
}
setupTreeshakingFromConfig(userSentryOptions, newConfig, buildContext);

// We inject a map of dependencies that the nextjs app has, as we cannot reliably extract them at runtime, sadly
newConfig.plugins = newConfig.plugins || [];
Expand Down Expand Up @@ -912,38 +910,49 @@ function addEdgeRuntimePolyfills(newConfig: WebpackConfigObjectWithModuleRules,
}

/**
* Sets up the tree-shaking flags based on the user's configuration.
* Returns the tree-shaking flags to define, based on the user's configuration.
* https://docs.sentry.io/platforms/javascript/guides/nextjs/configuration/tree-shaking/
*/
function setupTreeshakingFromConfig(
userSentryOptions: SentryBuildOptions,
newConfig: WebpackConfigObjectWithModuleRules,
buildContext: BuildContext,
): void {
export function getTreeshakeDefines(userSentryOptions: SentryBuildOptions): Record<string, boolean> {
const defines: Record<string, boolean> = {};
const bundleSizeOptimizations = userSentryOptions.bundleSizeOptimizations;
const treeshake = userSentryOptions.webpack?.treeshake;

newConfig.plugins = newConfig.plugins || [];

if (userSentryOptions.webpack?.treeshake?.removeDebugLogging) {
if (bundleSizeOptimizations?.excludeDebugStatements ?? treeshake?.removeDebugLogging) {
defines.__SENTRY_DEBUG__ = false;
}

if (userSentryOptions.webpack?.treeshake?.removeTracing) {
if (bundleSizeOptimizations?.excludeTracing ?? treeshake?.removeTracing) {
defines.__SENTRY_TRACING__ = false;
}

if (userSentryOptions.webpack?.treeshake?.excludeReplayIframe) {
if (bundleSizeOptimizations?.excludeReplayIframe ?? treeshake?.excludeReplayIframe) {
defines.__RRWEB_EXCLUDE_IFRAME__ = true;
}

if (userSentryOptions.webpack?.treeshake?.excludeReplayShadowDOM) {
if (bundleSizeOptimizations?.excludeReplayShadowDom ?? treeshake?.excludeReplayShadowDOM) {
defines.__RRWEB_EXCLUDE_SHADOW_DOM__ = true;
}

if (userSentryOptions.webpack?.treeshake?.excludeReplayCompressionWorker) {
if (bundleSizeOptimizations?.excludeReplayWorker ?? treeshake?.excludeReplayCompressionWorker) {
defines.__SENTRY_EXCLUDE_REPLAY_WORKER__ = true;
}

return defines;
}

/**
* Sets up the tree-shaking flags based on the user's configuration.
*/
function setupTreeshakingFromConfig(
userSentryOptions: SentryBuildOptions,
newConfig: WebpackConfigObjectWithModuleRules,
buildContext: BuildContext,
): void {
const defines = getTreeshakeDefines(userSentryOptions);

newConfig.plugins = newConfig.plugins || [];

// Only add DefinePlugin if there are actual defines to set
if (Object.keys(defines).length > 0) {
newConfig.plugins.push(new buildContext.webpack.DefinePlugin(defines));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
getWebpackPatch,
maybeConstructTurbopackConfig,
maybeEnableTurbopackSourcemaps,
maybeSetTurbopackTreeshakeDefines,
maybeSetUpRunAfterProductionCompileHook,
maybeWarnAboutUnsupportedRunAfterProductionCompileHook,
maybeWarnAboutUnsupportedTurbopack,
Expand Down Expand Up @@ -84,6 +85,7 @@ export function getFinalConfigObject(
});

maybeEnableTurbopackSourcemaps(incomingUserNextConfigObject, userSentryOptions, bundlerInfo);
maybeSetTurbopackTreeshakeDefines(incomingUserNextConfigObject, userSentryOptions, bundlerInfo);

const useDiagnosticsChannelInjection = userSentryOptions._experimental?.useDiagnosticsChannelInjection ?? false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { RouteManifest } from '../manifest/types';
import { constructTurbopackConfig } from '../turbopack';
import type { NextConfigObject, SentryBuildOptions, TurbopackOptions } from '../types';
import { detectActiveBundler, supportsProductionCompileHook } from '../util';
import { constructWebpackConfigFunction } from '../webpack';
import { constructWebpackConfigFunction, getTreeshakeDefines } from '../webpack';
import { DEFAULT_SERVER_EXTERNAL_PACKAGES } from './constants';
import type { VercelCronsConfigResult } from './getFinalConfigObjectUtils';

Expand Down Expand Up @@ -301,6 +301,35 @@ export function getWebpackPatch({
};
}

/**
* Applies the `bundleSizeOptimizations` flags to Turbopack builds through `compiler.define`.
*
* Note: this mutates `incomingUserNextConfigObject`.
*/
export function maybeSetTurbopackTreeshakeDefines(
incomingUserNextConfigObject: NextConfigObject,
userSentryOptions: SentryBuildOptions,
bundlerInfo: BundlerInfo,
): void {
if (!bundlerInfo.isTurbopackSupported || !bundlerInfo.isTurbopack) {
return;
}

const defines = getTreeshakeDefines(userSentryOptions);
if (Object.keys(defines).length === 0) {
return;
}

incomingUserNextConfigObject.compiler = {
...incomingUserNextConfigObject.compiler,
define: {
// Must stay booleans: Turbopack injects string values as string literals, so `'false'` would be truthy.
...defines,
...incomingUserNextConfigObject.compiler?.define,
},
};
}

/**
* Returns the patch for adding turbopack config (if enabled and supported).
*/
Expand Down
3 changes: 2 additions & 1 deletion packages/nextjs/src/edge/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,10 @@ export function init(options: VercelEdgeOptions = {}): void {
}

if (!DEBUG_BUILD && options.debug) {
// TODO(v12): Remove the `webpack.treeshake` mention together with that option
// eslint-disable-next-line no-console
console.warn(
'[@sentry/nextjs] You have enabled `debug: true`, but Sentry debug logging was removed from your bundle (likely via `withSentryConfig({ disableLogger: true })` / `webpack.treeshake.removeDebugLogging: true`). Set that option to `false` to see Sentry debug output.',
'[@sentry/nextjs] You have enabled `debug: true`, but Sentry debug logging was removed from your bundle (likely via `bundleSizeOptimizations.excludeDebugStatements: true`, `withSentryConfig({ disableLogger: true })` or `webpack.treeshake.removeDebugLogging: true`). Set that option to `false` to see Sentry debug output.',
);
}

Expand Down
3 changes: 2 additions & 1 deletion packages/nextjs/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,10 @@ export function init(options: NodeOptions): NodeClient | undefined {
}

if (!DEBUG_BUILD && options.debug) {
// TODO(v12): Remove the `webpack.treeshake` mention together with that option
// eslint-disable-next-line no-console
console.warn(
'[@sentry/nextjs] You have enabled `debug: true`, but Sentry debug logging was removed from your bundle (likely via `withSentryConfig({ disableLogger: true })` / `webpack.treeshake.removeDebugLogging: true`). Set that option to `false` to see Sentry debug output.',
'[@sentry/nextjs] You have enabled `debug: true`, but Sentry debug logging was removed from your bundle (likely via `bundleSizeOptimizations.excludeDebugStatements: true`, `withSentryConfig({ disableLogger: true })` or `webpack.treeshake.removeDebugLogging: true`). Set that option to `false` to see Sentry debug output.',
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,41 @@ describe('constructWebpackConfigFunction()', () => {
expect(treeshakePlugin.definitions.__SENTRY_EXCLUDE_REPLAY_WORKER__).toBe(true);
});

it('adds flags from the top-level bundleSizeOptimizations option', async () => {
vi.spyOn(core, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
_name: 'sentry-webpack-plugin',
}),
}));

const finalWebpackConfig = await materializeFinalWebpackConfig({
exportedNextConfig,
incomingWebpackConfig: serverWebpackConfig,
incomingWebpackBuildContext: serverBuildContext,
sentryBuildTimeOptions: {
bundleSizeOptimizations: {
excludeDebugStatements: true,
excludeTracing: true,
excludeReplayIframe: true,
excludeReplayShadowDom: true,
excludeReplayWorker: true,
},
},
});

const definePlugin = finalWebpackConfig.plugins?.find(
plugin => plugin.constructor.name === 'DefinePlugin' && plugin.definitions?.__SENTRY_TRACING__ !== undefined,
) as any;

expect(definePlugin.definitions).toEqual({
__SENTRY_DEBUG__: false,
__SENTRY_TRACING__: false,
__RRWEB_EXCLUDE_IFRAME__: true,
__RRWEB_EXCLUDE_SHADOW_DOM__: true,
__SENTRY_EXCLUDE_REPLAY_WORKER__: true,
});
});

it('does not add flags when treeshake options are false', async () => {
vi.spyOn(core, 'loadModule').mockImplementation(() => ({
sentryWebpackPlugin: () => ({
Expand Down
61 changes: 60 additions & 1 deletion packages/nextjs/test/config/withSentryConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ describe('withSentryConfig', () => {
disableLogger: true,
};

materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions);
materializeFinalNextConfig({ ...exportedNextConfig }, undefined, sentryOptions);

expect(consoleWarnSpy).toHaveBeenCalledWith(
expect.stringContaining('Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)'),
Expand Down Expand Up @@ -623,6 +623,65 @@ describe('withSentryConfig', () => {
});
});

describe('turbopack treeshake configuration', () => {
const originalTurbopack = process.env.TURBOPACK;

afterEach(() => {
vi.restoreAllMocks();
process.env.TURBOPACK = originalTurbopack;
});

it('sets treeshake flags as boolean defines and keeps user defines', () => {
process.env.TURBOPACK = '1';
vi.spyOn(util, 'getNextjsVersion').mockReturnValue('16.0.0');

const finalConfig = materializeFinalNextConfig(
{ ...exportedNextConfig, compiler: { define: { __SENTRY_DEBUG__: true, USER_FLAG: 'x' } } },
undefined,
{ bundleSizeOptimizations: { excludeTracing: true, excludeDebugStatements: true } },
);

expect(finalConfig.compiler?.define).toEqual({
__SENTRY_TRACING__: false,
__SENTRY_DEBUG__: true,
USER_FLAG: 'x',
});
});

it('supports the webpack.treeshake option', () => {
process.env.TURBOPACK = '1';
vi.spyOn(util, 'getNextjsVersion').mockReturnValue('16.0.0');

const finalConfig = materializeFinalNextConfig({ ...exportedNextConfig }, undefined, {
webpack: { treeshake: { removeTracing: true } },
});

expect(finalConfig.compiler?.define).toEqual({ __SENTRY_TRACING__: false });
});

it('prefers bundleSizeOptimizations over the webpack.treeshake option', () => {
process.env.TURBOPACK = '1';
vi.spyOn(util, 'getNextjsVersion').mockReturnValue('16.0.0');

const finalConfig = materializeFinalNextConfig({ ...exportedNextConfig }, undefined, {
bundleSizeOptimizations: { excludeTracing: false },
webpack: { treeshake: { removeTracing: true } },
});

expect(finalConfig.compiler?.define).toBeUndefined();
});

it('does not set defines on webpack builds', () => {
delete process.env.TURBOPACK;

const finalConfig = materializeFinalNextConfig({ ...exportedNextConfig }, undefined, {
bundleSizeOptimizations: { excludeTracing: true },
});

expect(finalConfig.compiler?.define).toBeUndefined();
});
});

describe('turbopack sourcemap configuration', () => {
const originalTurbopack = process.env.TURBOPACK;

Expand Down
Loading