Skip to content

Commit dd6273e

Browse files
authored
fix(nextjs): Execute sentry config independently of autoInstrumentServerFunctions and autoInstrumentAppDirectory (#8781)
1 parent f83b906 commit dd6273e

File tree

4 files changed

+93
-41
lines changed

4 files changed

+93
-41
lines changed

packages/nextjs/rollup.npm.config.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ export default [
2424
...makeNPMConfigVariants(
2525
makeBaseNPMConfig({
2626
entrypoints: [
27-
'src/config/templates/pageWrapperTemplate.ts',
2827
'src/config/templates/apiWrapperTemplate.ts',
2928
'src/config/templates/middlewareWrapperTemplate.ts',
30-
'src/config/templates/serverComponentWrapperTemplate.ts',
29+
'src/config/templates/pageWrapperTemplate.ts',
3130
'src/config/templates/requestAsyncStorageShim.ts',
31+
'src/config/templates/sentryInitWrapperTemplate.ts',
32+
'src/config/templates/serverComponentWrapperTemplate.ts',
3233
],
3334

3435
packageSpecificConfig: {
@@ -47,6 +48,7 @@ export default [
4748
external: [
4849
'@sentry/nextjs',
4950
'next/dist/client/components/request-async-storage',
51+
'__SENTRY_CONFIG_IMPORT_PATH__',
5052
'__SENTRY_WRAPPING_TARGET_FILE__',
5153
'__SENTRY_NEXTJS_REQUEST_ASYNC_STORAGE_SHIM__',
5254
],

packages/nextjs/src/config/loaders/wrappingLoader.ts

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ const requestAsyncStorageShimPath = path.resolve(__dirname, '..', 'templates', '
3030
const requestAsyncStorageModuleExists = moduleExists(NEXTJS_REQUEST_ASYNC_STORAGE_MODULE_PATH);
3131
let showedMissingAsyncStorageModuleWarning = false;
3232

33+
const sentryInitWrapperTemplatePath = path.resolve(__dirname, '..', 'templates', 'sentryInitWrapperTemplate.js');
34+
const sentryInitWrapperTemplateCode = fs.readFileSync(sentryInitWrapperTemplatePath, { encoding: 'utf8' });
35+
3336
const serverComponentWrapperTemplatePath = path.resolve(
3437
__dirname,
3538
'..',
@@ -43,7 +46,7 @@ type LoaderOptions = {
4346
appDir: string;
4447
pageExtensionRegex: string;
4548
excludeServerRoutes: Array<RegExp | string>;
46-
wrappingTargetKind: 'page' | 'api-route' | 'middleware' | 'server-component';
49+
wrappingTargetKind: 'page' | 'api-route' | 'middleware' | 'server-component' | 'sentry-init';
4750
sentryConfigFilePath?: string;
4851
vercelCronsConfig?: VercelCronsConfig;
4952
};
@@ -83,7 +86,23 @@ export default function wrappingLoader(
8386

8487
let templateCode: string;
8588

86-
if (wrappingTargetKind === 'page' || wrappingTargetKind === 'api-route') {
89+
if (wrappingTargetKind === 'sentry-init') {
90+
templateCode = sentryInitWrapperTemplateCode;
91+
92+
// Absolute paths to the sentry config do not work with Windows: https://github.com/getsentry/sentry-javascript/issues/8133
93+
// Se we need check whether `this.resourcePath` is absolute because there is no contract by webpack that says it is absolute.
94+
// Examples where `this.resourcePath` could possibly be non-absolute are virtual modules.
95+
if (sentryConfigFilePath && path.isAbsolute(this.resourcePath)) {
96+
const sentryConfigImportPath = path
97+
.relative(path.dirname(this.resourcePath), sentryConfigFilePath)
98+
.replace(/\\/g, '/');
99+
templateCode = templateCode.replace(/__SENTRY_CONFIG_IMPORT_PATH__/g, sentryConfigImportPath);
100+
} else {
101+
// Bail without doing any wrapping
102+
this.callback(null, userCode, userModuleSourceMap);
103+
return;
104+
}
105+
} else if (wrappingTargetKind === 'page' || wrappingTargetKind === 'api-route') {
87106
// Get the parameterized route name from this page's filepath
88107
const parameterizedPagesRoute = path.posix
89108
.normalize(
@@ -207,15 +226,6 @@ export default function wrappingLoader(
207226
throw new Error(`Invariant: Could not get template code of unknown kind "${wrappingTargetKind}"`);
208227
}
209228

210-
// We check whether `this.resourcePath` is absolute because there is no contract by webpack that says it is absolute,
211-
// however we can only create relative paths to the sentry config from absolute paths.Examples where this could possibly be non - absolute are virtual modules.
212-
if (sentryConfigFilePath && path.isAbsolute(this.resourcePath)) {
213-
const sentryConfigImportPath = path
214-
.relative(path.dirname(this.resourcePath), sentryConfigFilePath) // Absolute paths do not work with Windows: https://github.com/getsentry/sentry-javascript/issues/8133
215-
.replace(/\\/g, '/');
216-
templateCode = `import "${sentryConfigImportPath}";\n`.concat(templateCode);
217-
}
218-
219229
// Replace the import path of the wrapping target in the template with a path that the `wrapUserCode` function will understand.
220230
templateCode = templateCode.replace(/__SENTRY_WRAPPING_TARGET_FILE__/g, WRAPPING_TARGET_MODULE_NAME);
221231

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// @ts-ignore This will be replaced with the user's sentry config gile
2+
// eslint-disable-next-line import/no-unresolved
3+
import '__SENTRY_CONFIG_IMPORT_PATH__';
4+
5+
// @ts-ignore This is the file we're wrapping
6+
// eslint-disable-next-line import/no-unresolved
7+
export * from '__SENTRY_WRAPPING_TARGET_FILE__';
8+
9+
// @ts-ignore This is the file we're wrapping
10+
// eslint-disable-next-line import/no-unresolved
11+
export { default } from '__SENTRY_WRAPPING_TARGET_FILE__';

packages/nextjs/src/config/webpack.ts

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -140,19 +140,45 @@ export function constructWebpackConfigFunction(
140140
return path.normalize(absoluteResourcePath);
141141
};
142142

143+
const isPageResource = (resourcePath: string): boolean => {
144+
const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath);
145+
return (
146+
normalizedAbsoluteResourcePath.startsWith(pagesDirPath + path.sep) &&
147+
!normalizedAbsoluteResourcePath.startsWith(apiRoutesPath + path.sep) &&
148+
dotPrefixedPageExtensions.some(ext => normalizedAbsoluteResourcePath.endsWith(ext))
149+
);
150+
};
151+
152+
const isApiRouteResource = (resourcePath: string): boolean => {
153+
const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath);
154+
return (
155+
normalizedAbsoluteResourcePath.startsWith(apiRoutesPath + path.sep) &&
156+
dotPrefixedPageExtensions.some(ext => normalizedAbsoluteResourcePath.endsWith(ext))
157+
);
158+
};
159+
160+
const isMiddlewareResource = (resourcePath: string): boolean => {
161+
const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath);
162+
return normalizedAbsoluteResourcePath === middlewareJsPath || normalizedAbsoluteResourcePath === middlewareTsPath;
163+
};
164+
165+
const isServerComponentResource = (resourcePath: string): boolean => {
166+
const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath);
167+
168+
// ".js, .jsx, or .tsx file extensions can be used for Pages"
169+
// https://beta.nextjs.org/docs/routing/pages-and-layouts#pages:~:text=.js%2C%20.jsx%2C%20or%20.tsx%20file%20extensions%20can%20be%20used%20for%20Pages.
170+
return (
171+
normalizedAbsoluteResourcePath.startsWith(appDirPath + path.sep) &&
172+
!!normalizedAbsoluteResourcePath.match(/[\\/](page|layout|loading|head|not-found)\.(js|jsx|tsx)$/)
173+
);
174+
};
175+
143176
if (isServer && userSentryOptions.autoInstrumentServerFunctions !== false) {
144177
// 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.
145178

146179
// Wrap pages
147180
newConfig.module.rules.unshift({
148-
test: resourcePath => {
149-
const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath);
150-
return (
151-
normalizedAbsoluteResourcePath.startsWith(pagesDirPath + path.sep) &&
152-
!normalizedAbsoluteResourcePath.startsWith(apiRoutesPath + path.sep) &&
153-
dotPrefixedPageExtensions.some(ext => normalizedAbsoluteResourcePath.endsWith(ext))
154-
);
155-
},
181+
test: isPageResource,
156182
use: [
157183
{
158184
loader: path.resolve(__dirname, 'loaders', 'wrappingLoader.js'),
@@ -190,13 +216,7 @@ export function constructWebpackConfigFunction(
190216

191217
// Wrap api routes
192218
newConfig.module.rules.unshift({
193-
test: resourcePath => {
194-
const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath);
195-
return (
196-
normalizedAbsoluteResourcePath.startsWith(apiRoutesPath + path.sep) &&
197-
dotPrefixedPageExtensions.some(ext => normalizedAbsoluteResourcePath.endsWith(ext))
198-
);
199-
},
219+
test: isApiRouteResource,
200220
use: [
201221
{
202222
loader: path.resolve(__dirname, 'loaders', 'wrappingLoader.js'),
@@ -211,12 +231,7 @@ export function constructWebpackConfigFunction(
211231

212232
// Wrap middleware
213233
newConfig.module.rules.unshift({
214-
test: resourcePath => {
215-
const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath);
216-
return (
217-
normalizedAbsoluteResourcePath === middlewareJsPath || normalizedAbsoluteResourcePath === middlewareTsPath
218-
);
219-
},
234+
test: isMiddlewareResource,
220235
use: [
221236
{
222237
loader: path.resolve(__dirname, 'loaders', 'wrappingLoader.js'),
@@ -232,22 +247,36 @@ export function constructWebpackConfigFunction(
232247
if (isServer && userSentryOptions.autoInstrumentAppDirectory !== false) {
233248
// Wrap page server components
234249
newConfig.module.rules.unshift({
235-
test: resourcePath => {
236-
const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath);
250+
test: isServerComponentResource,
251+
use: [
252+
{
253+
loader: path.resolve(__dirname, 'loaders', 'wrappingLoader.js'),
254+
options: {
255+
...staticWrappingLoaderOptions,
256+
wrappingTargetKind: 'server-component',
257+
},
258+
},
259+
],
260+
});
261+
}
237262

238-
// ".js, .jsx, or .tsx file extensions can be used for Pages"
239-
// https://beta.nextjs.org/docs/routing/pages-and-layouts#pages:~:text=.js%2C%20.jsx%2C%20or%20.tsx%20file%20extensions%20can%20be%20used%20for%20Pages.
263+
if (isServer) {
264+
// Import the Sentry config in every user file
265+
newConfig.module.rules.unshift({
266+
test: resourcePath => {
240267
return (
241-
normalizedAbsoluteResourcePath.startsWith(appDirPath + path.sep) &&
242-
!!normalizedAbsoluteResourcePath.match(/[\\/](page|layout|loading|head|not-found)\.(js|jsx|tsx)$/)
268+
isPageResource(resourcePath) ||
269+
isApiRouteResource(resourcePath) ||
270+
isMiddlewareResource(resourcePath) ||
271+
isServerComponentResource(resourcePath)
243272
);
244273
},
245274
use: [
246275
{
247276
loader: path.resolve(__dirname, 'loaders', 'wrappingLoader.js'),
248277
options: {
249278
...staticWrappingLoaderOptions,
250-
wrappingTargetKind: 'server-component',
279+
wrappingTargetKind: 'sentry-init',
251280
},
252281
},
253282
],

0 commit comments

Comments
 (0)