Skip to content
Open
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
10 changes: 5 additions & 5 deletions packages/nextjs/src/config/getBuildPluginOptions.ts
Original file line number Diff line number Diff 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 Down Expand 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 number Diff line number Diff 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 shakes Sentry SDK logger statements from the bundle.
*/
treeshake?: {
/**
* Tree shakes Sentry SDK logger statements from the bundle. Note that this doesn't affect Sentry Logs.
*/
debugLogs?: 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 Down Expand 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.debugLogs` 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 Down Expand 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 number Diff line number Diff 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 Down Expand 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 Down Expand 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 Down Expand Up @@ -431,7 +431,7 @@ export function constructWebpackConfigFunction({
}
}

if (userSentryOptions.disableLogger) {
if (userSentryOptions.webpack?.treeshake?.debugLogs) {
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 number Diff line number Diff 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.debugLogs specially since it's nested
if (userSentryOptions.disableLogger !== undefined) {
webpack.treeshake = webpack.treeshake || {};
webpack.treeshake.debugLogs = withDeprecatedFallback(
webpack.treeshake.debugLogs,
userSentryOptions.disableLogger,
deprecatedMessage('disableLogger', 'webpack.treeshake.debugLogs'),
);
}

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 Down Expand Up @@ -363,7 +453,7 @@ function getFinalConfigObject(
],
},
}),
...(isWebpack && !userSentryOptions.disableSentryWebpackConfig
...(isWebpack && !userSentryOptions.webpack?.disableSentryConfig
? {
webpack: constructWebpackConfigFunction({
userNextConfig: incomingUserNextConfigObject,
Expand Down
Loading