Skip to content
Merged
10 changes: 5 additions & 5 deletions packages/nextjs/src/config/getBuildPluginOptions.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -205,7 +205,7 @@ function createReleaseConfig(
vcsRemote: sentryBuildOptions.release?.vcsRemote,
setCommits: sentryBuildOptions.release?.setCommits,
deploy: sentryBuildOptions.release?.deploy,
...sentryBuildOptions.unstable_sentryWebpackPluginOptions?.release,
...sentryBuildOptions.webpack?.unstable_sentryWebpackPluginOptions?.release,
};
}

Expand DownExpand Up@@ -272,8 +272,8 @@ export function getBuildPluginOptions({
reactComponentAnnotation: buildTool.startsWith('after-production-compile')
? undefined
: {
...sentryBuildOptions.reactComponentAnnotation,
...sentryBuildOptions.unstable_sentryWebpackPluginOptions?.reactComponentAnnotation,
...sentryBuildOptions.webpack?.reactComponentAnnotation,
...sentryBuildOptions.webpack?.unstable_sentryWebpackPluginOptions?.reactComponentAnnotation,
},
silent: sentryBuildOptions.silent,
url: sentryBuildOptions.sentryUrl,
Expand All@@ -283,7 +283,7 @@ export function getBuildPluginOptions({
assets: sentryBuildOptions.sourcemaps?.assets ?? sourcemapUploadAssets,
ignore: sentryBuildOptions.sourcemaps?.ignore ?? sourcemapUploadIgnore,
filesToDeleteAfterUpload,
...sentryBuildOptions.unstable_sentryWebpackPluginOptions?.sourcemaps,
...sentryBuildOptions.webpack?.unstable_sentryWebpackPluginOptions?.sourcemaps,
},
release: createReleaseConfig(releaseName, sentryBuildOptions),
bundleSizeOptimizations: {
Expand All@@ -295,6 +295,6 @@ export function getBuildPluginOptions({
metaFramework: 'nextjs',
},
},
...sentryBuildOptions.unstable_sentryWebpackPluginOptions,
...sentryBuildOptions.webpack?.unstable_sentryWebpackPluginOptions,
};
}
106 changes: 106 additions & 0 deletions packages/nextjs/src/config/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -57,6 +57,91 @@ export type NextConfigObject = {
};
};

export type SentryBuildWebpackOptions = {
/**
* Automatically instrument Next.js data fetching methods and Next.js API routes with error and performance monitoring.
* Defaults to `true`.
*/
autoInstrumentServerFunctions?: boolean;

/**
* Automatically instrument Next.js middleware with error and performance monitoring. Defaults to `true`.
*/
autoInstrumentMiddleware?: boolean;

/**
* Automatically instrument components in the `app` directory with error monitoring. Defaults to `true`.
*/
autoInstrumentAppDirectory?: boolean;

/**
* Automatically create cron monitors in Sentry for your Vercel Cron Jobs if configured via `vercel.json`.
*
* Defaults to `false`.
*/
automaticVercelMonitors?: boolean;

/**
* Exclude certain serverside API routes or pages from being instrumented with Sentry during build-time. This option
* takes an array of strings or regular expressions. This options also affects pages in the `app` directory.
*
* NOTE: Pages should be specified as routes (`/animals` or `/api/animals/[animalType]/habitat`), not filepaths
* (`pages/animals/index.js` or `.\src\pages\api\animals\[animalType]\habitat.tsx`), and strings must be be a full,
* exact match.
*
* Notice: If you build Next.js with turbopack, the Sentry SDK will no longer apply build-time instrumentation and
* purely rely on Next.js telemetry features, meaning that this option will effectively no-op.
*/
excludeServerRoutes?: Array<RegExp | string>;

/**
* Disables automatic injection of Sentry's Webpack configuration.
*
* By default, the Sentry Next.js SDK injects its own Webpack configuration to enable features such as
* source map upload and automatic instrumentation. Set this option to `true` if you want to prevent
* the SDK from modifying your Webpack config (for example, if you want to handle Sentry integration manually
* or if you are on an older version of Next.js while using Turbopack).
*/
disableSentryConfig?: boolean;

/**
* Tree-shaking options to help reduce the size of the Sentry SDK bundle.
*/
treeshake?: {
/**
* Removes Sentry SDK logger statements from the bundle. Note that this doesn't affect Sentry Logs.
*/
removeDebugLogging?: boolean;
};

/**
* Options to be passed directly to the Sentry Webpack Plugin (`@sentry/webpack-plugin`) that ships with the Sentry SDK.
* You can use this option to override any options the SDK passes to the Webpack plugin.
*
* Please note that this option is unstable and may change in a breaking way in any release.
*/
unstable_sentryWebpackPluginOptions?: SentryWebpackPluginOptions;

/**
* Options related to react component name annotations.
* Disabled by default, unless a value is set for this option.
* When enabled, your app's DOM will automatically be annotated during build-time with their respective component names.
* This will unlock the capability to search for Replays in Sentry by component name, as well as see component names in breadcrumbs and performance monitoring.
* Please note that this feature is not currently supported by the esbuild bundler plugins, and will only annotate React components
*/
reactComponentAnnotation?: {
/**
* Whether the component name annotate plugin should be enabled or not.
*/
enabled?: boolean;

/**
* A list of strings representing the names of components to ignore. The plugin will not apply `data-sentry` annotations on the DOM element for these components.
*/
ignoredComponents?: string[];
};
};

export type SentryBuildOptions = {
/**
* The slug of the Sentry organization associated with the app.
Expand DownExpand Up@@ -363,6 +448,8 @@ export type SentryBuildOptions = {
* When enabled, your app's DOM will automatically be annotated during build-time with their respective component names.
* This will unlock the capability to search for Replays in Sentry by component name, as well as see component names in breadcrumbs and performance monitoring.
* Please note that this feature is not currently supported by the esbuild bundler plugins, and will only annotate React components
*
* @deprecated Use `webpack.reactComponentAnnotation` instead.
*/
reactComponentAnnotation?: {
/**
Expand All@@ -381,6 +468,7 @@ export type SentryBuildOptions = {
* You can use this option to override any options the SDK passes to the webpack plugin.
*
* Please note that this option is unstable and may change in a breaking way in any release.
* @deprecated Use `webpack.unstable_sentryWebpackPluginOptions` instead.
*/
unstable_sentryWebpackPluginOptions?: SentryWebpackPluginOptions;

Expand All@@ -391,6 +479,8 @@ export type SentryBuildOptions = {
* Disabling this option will leave you without readable stacktraces for dependencies and Next.js-internal code.
*
* Defaults to `false`.
*
* This option applies to both webpack and turbopack builds.
*/
// Enabling this option may upload a lot of source maps and since the sourcemap upload endpoint in Sentry is super
// slow we don't enable it by default so that we don't opaquely increase build times for users.
Expand All@@ -400,16 +490,19 @@ export type SentryBuildOptions = {
/**
* Automatically instrument Next.js data fetching methods and Next.js API routes with error and performance monitoring.
* Defaults to `true`.
* @deprecated Use `webpack.autoInstrumentServerFunctions` instead.
*/
autoInstrumentServerFunctions?: boolean;

/**
* Automatically instrument Next.js middleware with error and performance monitoring. Defaults to `true`.
* @deprecated Use `webpack.autoInstrumentMiddleware` instead.
*/
autoInstrumentMiddleware?: boolean;

/**
* Automatically instrument components in the `app` directory with error monitoring. Defaults to `true`.
* @deprecated Use `webpack.autoInstrumentAppDirectory` instead.
*/
autoInstrumentAppDirectory?: boolean;

Expand All@@ -423,6 +516,8 @@ export type SentryBuildOptions = {
*
* Notice: If you build Next.js with turbopack, the Sentry SDK will no longer apply build-time instrumentation and
* purely rely on Next.js telemetry features, meaning that this option will effectively no-op.
*
* @deprecated Use `webpack.excludeServerRoutes` instead.
*/
excludeServerRoutes?: Array<RegExp | string>;

Expand All@@ -439,13 +534,17 @@ export type SentryBuildOptions = {

/**
* Tree shakes Sentry SDK logger statements from the bundle.
*
* @deprecated Use `webpack.treeshake.removeDebugLogging` instead.
*/
disableLogger?: boolean;

/**
* Automatically create cron monitors in Sentry for your Vercel Cron Jobs if configured via `vercel.json`.
*
* Defaults to `false`.
*
* @deprecated Use `webpack.automaticVercelMonitors` instead.
*/
automaticVercelMonitors?: boolean;

Expand DownExpand Up@@ -497,6 +596,8 @@ export type SentryBuildOptions = {
* the SDK from modifying your Webpack config (for example, if you want to handle Sentry integration manually
* or if you are on an older version of Next.js while using Turbopack).
*
* @deprecated Use `webpack.disableSentryConfig` instead.
*
* @default false
*/
disableSentryWebpackConfig?: boolean;
Expand All@@ -519,6 +620,11 @@ export type SentryBuildOptions = {
_experimental?: Partial<{
thirdPartyOriginStackFrames?: boolean;
}>;

/**
* Options related to webpack builds, has no effect if you are using Turbopack.
*/
webpack?: SentryBuildWebpackOptions;
};

export type NextConfigFunction = (
Expand Down
12 changes: 6 additions & 6 deletions packages/nextjs/src/config/webpack.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -145,7 +145,7 @@ export function constructWebpackConfigFunction({
appDir: appDirPath,
pagesDir: pagesDirPath,
pageExtensionRegex,
excludeServerRoutes: userSentryOptions.excludeServerRoutes,
excludeServerRoutes: userSentryOptions.webpack?.excludeServerRoutes,
nextjsRequestAsyncStorageModulePath: getRequestAsyncStorageModuleLocation(
projectDir,
rawNewConfig.resolve?.modules,
Expand DownExpand Up@@ -220,7 +220,7 @@ export function constructWebpackConfigFunction({
);
};

if (isServer && userSentryOptions.autoInstrumentServerFunctions !== false) {
if (isServer && userSentryOptions.webpack?.autoInstrumentServerFunctions !== false) {
// It is very important that we insert our loaders at the beginning of the array because we expect any sort of transformations/transpilations (e.g. TS -> JS) to already have happened.

// Wrap pages
Expand All@@ -239,7 +239,7 @@ export function constructWebpackConfigFunction({

let vercelCronsConfig: VercelCronsConfig = undefined;
try {
if (process.env.VERCEL && userSentryOptions.automaticVercelMonitors) {
if (process.env.VERCEL && userSentryOptions.webpack?.automaticVercelMonitors) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
vercelCronsConfig = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'vercel.json'), 'utf8')).crons;
if (vercelCronsConfig) {
Expand DownExpand Up@@ -277,7 +277,7 @@ export function constructWebpackConfigFunction({

// Wrap middleware
const canWrapStandaloneMiddleware = userNextConfig.output !== 'standalone' || !major || major < 16;
if ((userSentryOptions.autoInstrumentMiddleware ?? true) && canWrapStandaloneMiddleware) {
if ((userSentryOptions.webpack?.autoInstrumentMiddleware ?? true) && canWrapStandaloneMiddleware) {
newConfig.module.rules.unshift({
test: isMiddlewareResource,
use: [
Expand All@@ -293,7 +293,7 @@ export function constructWebpackConfigFunction({
}
}

if (isServer && userSentryOptions.autoInstrumentAppDirectory !== false) {
if (isServer && userSentryOptions.webpack?.autoInstrumentAppDirectory !== false) {
// Wrap server components
newConfig.module.rules.unshift({
test: isServerComponentResource,
Expand DownExpand Up@@ -431,7 +431,7 @@ export function constructWebpackConfigFunction({
}
}

if (userSentryOptions.disableLogger) {
if (userSentryOptions.webpack?.treeshake?.removeDebugLogging) {
newConfig.plugins = newConfig.plugins || [];
newConfig.plugins.push(
new buildContext.webpack.DefinePlugin({
Expand Down
92 changes: 91 additions & 1 deletion packages/nextjs/src/config/withSentryConfig.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -98,12 +98,102 @@ function generateRandomTunnelRoute(): string {
return `/${randomString}`;
}

/**
* Migrates deprecated top-level webpack options to the new `webpack.*` path for backward compatibility.
* The new path takes precedence over deprecated options. This mutates the userSentryOptions object.
*/
function migrateDeprecatedWebpackOptions(userSentryOptions: SentryBuildOptions): void {
// Initialize webpack options if not present
userSentryOptions.webpack = userSentryOptions.webpack || {};

const webpack = userSentryOptions.webpack;

const withDeprecatedFallback = <T>(
newValue: T | undefined,
deprecatedValue: T | undefined,
message: string,
): T | undefined => {
if (deprecatedValue !== undefined) {
// eslint-disable-next-line no-console
console.warn(message);
}

return newValue ?? deprecatedValue;
};

const deprecatedMessage = (deprecatedPath: string, newPath: string): string =>
`[@sentry/nextjs] DEPRECATION WARNING: ${deprecatedPath} is deprecated and will be removed in a future version. Use ${newPath} instead.`;

/* eslint-disable deprecation/deprecation */
// Migrate each deprecated option to the new path, but only if the new path isn't already set
webpack.autoInstrumentServerFunctions = withDeprecatedFallback(
webpack.autoInstrumentServerFunctions,
userSentryOptions.autoInstrumentServerFunctions,
deprecatedMessage('autoInstrumentServerFunctions', 'webpack.autoInstrumentServerFunctions'),
);

webpack.autoInstrumentMiddleware = withDeprecatedFallback(
webpack.autoInstrumentMiddleware,
userSentryOptions.autoInstrumentMiddleware,
deprecatedMessage('autoInstrumentMiddleware', 'webpack.autoInstrumentMiddleware'),
);

webpack.autoInstrumentAppDirectory = withDeprecatedFallback(
webpack.autoInstrumentAppDirectory,
userSentryOptions.autoInstrumentAppDirectory,
deprecatedMessage('autoInstrumentAppDirectory', 'webpack.autoInstrumentAppDirectory'),
);

webpack.excludeServerRoutes = withDeprecatedFallback(
webpack.excludeServerRoutes,
userSentryOptions.excludeServerRoutes,
deprecatedMessage('excludeServerRoutes', 'webpack.excludeServerRoutes'),
);

webpack.unstable_sentryWebpackPluginOptions = withDeprecatedFallback(
webpack.unstable_sentryWebpackPluginOptions,
userSentryOptions.unstable_sentryWebpackPluginOptions,
deprecatedMessage('unstable_sentryWebpackPluginOptions', 'webpack.unstable_sentryWebpackPluginOptions'),
);

webpack.disableSentryConfig = withDeprecatedFallback(
webpack.disableSentryConfig,
userSentryOptions.disableSentryWebpackConfig,
deprecatedMessage('disableSentryWebpackConfig', 'webpack.disableSentryConfig'),
);

// Handle treeshake.removeDebugLogging specially since it's nested
if (userSentryOptions.disableLogger !== undefined) {
webpack.treeshake = webpack.treeshake || {};
webpack.treeshake.removeDebugLogging = withDeprecatedFallback(
webpack.treeshake.removeDebugLogging,
userSentryOptions.disableLogger,
deprecatedMessage('disableLogger', 'webpack.treeshake.removeDebugLogging'),
);
}

webpack.automaticVercelMonitors = withDeprecatedFallback(
webpack.automaticVercelMonitors,
userSentryOptions.automaticVercelMonitors,
deprecatedMessage('automaticVercelMonitors', 'webpack.automaticVercelMonitors'),
);

webpack.reactComponentAnnotation = withDeprecatedFallback(
webpack.reactComponentAnnotation,
userSentryOptions.reactComponentAnnotation,
deprecatedMessage('reactComponentAnnotation', 'webpack.reactComponentAnnotation'),
);
}

// Modify the materialized object form of the user's next config by deleting the `sentry` property and wrapping the
// `webpack` property
function getFinalConfigObject(
incomingUserNextConfigObject: NextConfigObject,
userSentryOptions: SentryBuildOptions,
): NextConfigObject {
// Migrate deprecated webpack options to new webpack path for backward compatibility
migrateDeprecatedWebpackOptions(userSentryOptions);

// Only determine a release name if release creation is not explicitly disabled
// This prevents injection of Git commit hashes that break build determinism
const shouldCreateRelease = userSentryOptions.release?.create !== false;
Expand DownExpand Up@@ -363,7 +453,7 @@ function getFinalConfigObject(
],
},
}),
...(isWebpack && !userSentryOptions.disableSentryWebpackConfig
...(isWebpack && !userSentryOptions.webpack?.disableSentryConfig
? {
webpack: constructWebpackConfigFunction({
userNextConfig: incomingUserNextConfigObject,
Expand Down
Loading