Skip to content

Commit 91f447c

Browse files
author
Luca Forstner
authored
ref(nextjs): Remove NFT build time exclusions (#6846)
1 parent 1249496 commit 91f447c

File tree

3 files changed

+27
-116
lines changed

3 files changed

+27
-116
lines changed

packages/nextjs/src/config/webpack.ts

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
/* eslint-disable complexity */
22
/* eslint-disable max-lines */
33
import { getSentryRelease } from '@sentry/node';
4-
import {
5-
arrayify,
6-
dropUndefinedKeys,
7-
escapeStringForRegex,
8-
GLOBAL_OBJ,
9-
logger,
10-
stringMatchesSomePattern,
11-
} from '@sentry/utils';
4+
import { arrayify, dropUndefinedKeys, escapeStringForRegex, logger, stringMatchesSomePattern } from '@sentry/utils';
125
import { default as SentryWebpackPlugin } from '@sentry/webpack-plugin';
136
import * as chalk from 'chalk';
147
import * as fs from 'fs';
@@ -27,7 +20,6 @@ import type {
2720
WebpackConfigObjectWithModuleRules,
2821
WebpackEntryProperty,
2922
WebpackModuleRule,
30-
WebpackPluginInstance,
3123
} from './types';
3224

3325
export { SentryWebpackPlugin };
@@ -36,14 +28,6 @@ export { SentryWebpackPlugin };
3628
// TODO: merge default SentryWebpackPlugin include with their SentryWebpackPlugin include
3729
// TODO: drop merged keys from override check? `includeDefaults` option?
3830

39-
// In order to make sure that build-time code isn't getting bundled in runtime bundles (specifically, in the serverless
40-
// functions into which nextjs converts a user's routes), we exclude this module (and all of its dependencies) from the
41-
// nft file manifests nextjs generates. (See the code dealing with the `TraceEntryPointsPlugin` below.) So that we don't
42-
// crash people, we therefore need to make sure nothing tries to either require this file or import from it. Setting
43-
// this env variable allows us to test whether or not that's happened.
44-
const _global = GLOBAL_OBJ as typeof GLOBAL_OBJ & { _sentryWebpackModuleLoaded?: true };
45-
_global._sentryWebpackModuleLoaded = true;
46-
4731
/**
4832
* Construct the function which will be used as the nextjs config's `webpack` value.
4933
*
@@ -159,31 +143,6 @@ export function constructWebpackConfigFunction(
159143
],
160144
});
161145
}
162-
163-
// Prevent `@vercel/nft` (which nextjs uses to determine which files are needed when packaging up a lambda) from
164-
// including any of our build-time code or dependencies. (Otherwise it'll include files like this one and even the
165-
// entirety of rollup and sucrase.) Since this file is the root of that dependency tree, it's enough to just
166-
// exclude it and the rest will be excluded as well.
167-
const nftPlugin = newConfig.plugins?.find((plugin: WebpackPluginInstance) => {
168-
const proto = Object.getPrototypeOf(plugin) as WebpackPluginInstance;
169-
return proto.constructor.name === 'TraceEntryPointsPlugin';
170-
}) as WebpackPluginInstance & { excludeFiles: string[] };
171-
if (nftPlugin) {
172-
if (Array.isArray(nftPlugin.excludeFiles)) {
173-
nftPlugin.excludeFiles.push(__filename);
174-
} else {
175-
__DEBUG_BUILD__ &&
176-
logger.warn(
177-
'Unable to exclude Sentry build-time helpers from nft files. `TraceEntryPointsPlugin.excludeFiles` is not ' +
178-
'an array. This is a bug; please report this to Sentry: https://github.com/getsentry/sentry-javascript/issues/.',
179-
);
180-
}
181-
} else {
182-
__DEBUG_BUILD__ &&
183-
logger.warn(
184-
'Unable to exclude Sentry build-time helpers from nft files. Could not find `TraceEntryPointsPlugin`.',
185-
);
186-
}
187146
}
188147

189148
// The SDK uses syntax (ES6 and ES6+ features like object spread) which isn't supported by older browsers. For users

packages/nextjs/src/config/withSentryConfig.ts

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_BUILD } from 'next/constants';
2-
31
import type {
42
ExportedNextConfig,
53
NextConfigFunction,
@@ -8,6 +6,7 @@ import type {
86
SentryWebpackPluginOptions,
97
UserSentryOptions,
108
} from './types';
9+
import { constructWebpackConfigFunction } from './webpack';
1110

1211
/**
1312
* Add Sentry options to the config to be exported from the user's `next.config.js` file.
@@ -22,49 +21,44 @@ export function withSentryConfig(
2221
userSentryWebpackPluginOptions: Partial<SentryWebpackPluginOptions> = {},
2322
sentryOptions?: UserSentryOptions,
2423
): NextConfigFunction | NextConfigObject {
25-
return function (phase: string, defaults: { defaultConfig: NextConfigObject }): NextConfigObject {
26-
const userNextConfigObject =
27-
typeof exportedUserNextConfig === 'function' ? exportedUserNextConfig(phase, defaults) : exportedUserNextConfig;
28-
// Inserts additional `sentry` options into the existing config, allows for backwards compatability
29-
// in case nothing is passed into the optional `sentryOptions` argument
30-
userNextConfigObject.sentry = { ...userNextConfigObject.sentry, ...sentryOptions };
31-
return getFinalConfigObject(phase, userNextConfigObject, userSentryWebpackPluginOptions);
32-
};
24+
if (typeof exportedUserNextConfig === 'function') {
25+
return function (this: unknown, ...webpackConfigFunctionArgs: unknown[]): NextConfigObject {
26+
const userNextConfigObject: NextConfigObjectWithSentry = exportedUserNextConfig.apply(
27+
this,
28+
webpackConfigFunctionArgs,
29+
);
30+
31+
const userSentryOptions = { ...userNextConfigObject.sentry, ...sentryOptions };
32+
return getFinalConfigObject(userNextConfigObject, userSentryOptions, userSentryWebpackPluginOptions);
33+
};
34+
} else {
35+
const userSentryOptions = { ...exportedUserNextConfig.sentry, ...sentryOptions };
36+
return getFinalConfigObject(exportedUserNextConfig, userSentryOptions, userSentryWebpackPluginOptions);
37+
}
3338
}
3439

3540
// Modify the materialized object form of the user's next config by deleting the `sentry` property and wrapping the
3641
// `webpack` property
3742
function getFinalConfigObject(
38-
phase: string,
3943
incomingUserNextConfigObject: NextConfigObjectWithSentry,
44+
userSentryOptions: UserSentryOptions,
4045
userSentryWebpackPluginOptions: Partial<SentryWebpackPluginOptions>,
4146
): NextConfigObject {
42-
// Next 12.2.3+ warns about non-canonical properties on `userNextConfig`, so grab and then remove the `sentry`
43-
// property there. Where we actually need it is in the webpack config function we're going to create, so pass it
44-
// to `constructWebpackConfigFunction` so that it can live in the returned function's closure.
45-
const { sentry: userSentryOptions } = incomingUserNextConfigObject;
47+
// Next 12.2.3+ warns about non-canonical properties on `userNextConfig`.
4648
delete incomingUserNextConfigObject.sentry;
47-
// Remind TS that there's now no `sentry` property
48-
const userNextConfigObject = incomingUserNextConfigObject as NextConfigObject;
4949

5050
if (userSentryOptions?.tunnelRoute) {
51-
setUpTunnelRewriteRules(userNextConfigObject, userSentryOptions.tunnelRoute);
51+
setUpTunnelRewriteRules(incomingUserNextConfigObject, userSentryOptions.tunnelRoute);
5252
}
5353

54-
// In order to prevent all of our build-time code from being bundled in people's route-handling serverless functions,
55-
// we exclude `webpack.ts` and all of its dependencies from nextjs's `@vercel/nft` filetracing. We therefore need to
56-
// make sure that we only require it at build time or in development mode.
57-
if (phase === PHASE_PRODUCTION_BUILD || phase === PHASE_DEVELOPMENT_SERVER) {
58-
// eslint-disable-next-line @typescript-eslint/no-var-requires
59-
const { constructWebpackConfigFunction } = require('./webpack');
60-
return {
61-
...userNextConfigObject,
62-
webpack: constructWebpackConfigFunction(userNextConfigObject, userSentryWebpackPluginOptions, userSentryOptions),
63-
};
64-
}
65-
66-
// At runtime, we just return the user's config untouched.
67-
return userNextConfigObject;
54+
return {
55+
...incomingUserNextConfigObject,
56+
webpack: constructWebpackConfigFunction(
57+
incomingUserNextConfigObject,
58+
userSentryWebpackPluginOptions,
59+
userSentryOptions,
60+
),
61+
};
6862
}
6963

7064
/**

packages/nextjs/test/config/withSentryConfig.test.ts

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -59,46 +59,4 @@ describe('withSentryConfig', () => {
5959
// directly
6060
expect('sentry' in finalConfig).toBe(false);
6161
});
62-
63-
describe('conditional use of `constructWebpackConfigFunction`', () => {
64-
// Note: In these tests, it would be nice to be able to spy on `constructWebpackConfigFunction` to see whether or
65-
// not it's called, but that sets up a catch-22: If you import or require the module to spy on the function, it gets
66-
// cached and the `require` call we care about (inside of `withSentryConfig`) doesn't actually run the module code.
67-
// Alternatively, if we call `jest.resetModules()` after setting up the spy, then the module code *is* run a second
68-
// time, but the spy belongs to the first instance of the module and therefore never registers a call. Thus we have
69-
// to test whether or not the file is required instead.
70-
71-
it('imports from `webpack.ts` if build phase is "phase-production-build"', () => {
72-
jest.isolateModules(() => {
73-
// In case this is still set from elsewhere, reset it
74-
delete (global as any)._sentryWebpackModuleLoaded;
75-
76-
materializeFinalNextConfig(exportedNextConfig, undefined, 'phase-production-build');
77-
78-
expect((global as any)._sentryWebpackModuleLoaded).toBe(true);
79-
});
80-
});
81-
82-
it('imports from `webpack.ts` if build phase is "phase-development-server"', () => {
83-
jest.isolateModules(() => {
84-
// In case this is still set from elsewhere, reset it
85-
delete (global as any)._sentryWebpackModuleLoaded;
86-
87-
materializeFinalNextConfig(exportedNextConfig, undefined, 'phase-production-build');
88-
89-
expect((global as any)._sentryWebpackModuleLoaded).toBe(true);
90-
});
91-
});
92-
93-
it('Doesn\'t import from `webpack.ts` if build phase is "phase-production-server"', () => {
94-
jest.isolateModules(() => {
95-
// In case this is still set from elsewhere, reset it
96-
delete (global as any)._sentryWebpackModuleLoaded;
97-
98-
materializeFinalNextConfig(exportedNextConfig, undefined, 'phase-production-server');
99-
100-
expect((global as any)._sentryWebpackModuleLoaded).toBeUndefined();
101-
});
102-
});
103-
});
10462
});

0 commit comments

Comments
 (0)