Skip to content

Commit 4fb043e

Browse files
authored
fix(nextjs): Inject init calls via loader instead of via entrypoints (#8368)
1 parent b84c23f commit 4fb043e

File tree

4 files changed

+18
-193
lines changed

4 files changed

+18
-193
lines changed

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ jobs:
424424
name: Nextjs (Node ${{ matrix.node }}) Tests
425425
needs: [job_get_metadata, job_build]
426426
if: needs.job_get_metadata.outputs.changed_nextjs == 'true' || github.event_name != 'pull_request'
427-
timeout-minutes: 15
427+
timeout-minutes: 25
428428
runs-on: ubuntu-20.04
429429
strategy:
430430
fail-fast: false

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -201,21 +201,21 @@ export default function wrappingLoader(
201201
} else {
202202
templateCode = templateCode.replace(/__COMPONENT_TYPE__/g, 'Unknown');
203203
}
204-
205-
// We check whether `this.resourcePath` is absolute because there is no contract by webpack that says it is absolute,
206-
// 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.
207-
if (sentryConfigFilePath && path.isAbsolute(this.resourcePath)) {
208-
const sentryConfigImportPath = path
209-
.relative(path.dirname(this.resourcePath), sentryConfigFilePath) // Absolute paths do not work with Windows: https://github.com/getsentry/sentry-javascript/issues/8133
210-
.replace(/\\/g, '/');
211-
templateCode = `import "${sentryConfigImportPath}";\n`.concat(templateCode);
212-
}
213204
} else if (wrappingTargetKind === 'middleware') {
214205
templateCode = middlewareWrapperTemplateCode;
215206
} else {
216207
throw new Error(`Invariant: Could not get template code of unknown kind "${wrappingTargetKind}"`);
217208
}
218209

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+
219219
// Replace the import path of the wrapping target in the template with a path that the `wrapUserCode` function will understand.
220220
templateCode = templateCode.replace(/__SENTRY_WRAPPING_TARGET_FILE__/g, WRAPPING_TARGET_MODULE_NAME);
221221

packages/nextjs/src/config/webpack.ts

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable complexity */
22
/* eslint-disable max-lines */
33
import { getSentryRelease } from '@sentry/node';
4-
import { arrayify, dropUndefinedKeys, escapeStringForRegex, logger, stringMatchesSomePattern } from '@sentry/utils';
4+
import { arrayify, dropUndefinedKeys, escapeStringForRegex, logger } from '@sentry/utils';
55
import { default as SentryWebpackPlugin } from '@sentry/webpack-plugin';
66
import * as chalk from 'chalk';
77
import * as fs from 'fs';
@@ -441,7 +441,7 @@ async function addSentryToEntryProperty(
441441

442442
// inject into all entry points which might contain user's code
443443
for (const entryPointName in newEntryProperty) {
444-
if (shouldAddSentryToEntryPoint(entryPointName, runtime, userSentryOptions.excludeServerRoutes ?? [])) {
444+
if (shouldAddSentryToEntryPoint(entryPointName, runtime)) {
445445
addFilesToExistingEntryPoint(newEntryProperty, entryPointName, filesToInject);
446446
} else {
447447
if (
@@ -589,39 +589,13 @@ function checkWebpackPluginOverrides(
589589
* @param excludeServerRoutes A list of excluded serverside entrypoints provided by the user
590590
* @returns `true` if sentry code should be injected, and `false` otherwise
591591
*/
592-
function shouldAddSentryToEntryPoint(
593-
entryPointName: string,
594-
runtime: 'node' | 'browser' | 'edge',
595-
excludeServerRoutes: Array<string | RegExp>,
596-
): boolean {
597-
// On the server side, by default we inject the `Sentry.init()` code into every page (with a few exceptions).
598-
if (runtime === 'node') {
599-
// User-specified pages to skip. (Note: For ease of use, `excludeServerRoutes` is specified in terms of routes,
600-
// which don't have the `pages` prefix.)
601-
const entryPointRoute = entryPointName.replace(/^pages/, '');
602-
if (stringMatchesSomePattern(entryPointRoute, excludeServerRoutes, true)) {
603-
return false;
604-
}
605-
606-
// This expression will implicitly include `pages/_app` which is called for all serverside routes and pages
607-
// regardless whether or not the user has a`_app` file.
608-
return entryPointName.startsWith('pages/');
609-
} else if (runtime === 'browser') {
610-
return (
611-
// entrypoint for `/pages` pages - this is included on all clientside pages
612-
// It's important that we inject the SDK into this file and not into 'main' because in 'main'
613-
// some important Next.js code (like the setup code for getCongig()) is located and some users
614-
// may need this code inside their Sentry configs
615-
entryPointName === 'pages/_app' ||
592+
function shouldAddSentryToEntryPoint(entryPointName: string, runtime: 'node' | 'browser' | 'edge'): boolean {
593+
return (
594+
runtime === 'browser' &&
595+
(entryPointName === 'pages/_app' ||
616596
// entrypoint for `/app` pages
617-
entryPointName === 'main-app'
618-
);
619-
} else {
620-
// User-specified pages to skip. (Note: For ease of use, `excludeServerRoutes` is specified in terms of routes,
621-
// which don't have the `pages` prefix.)
622-
const entryPointRoute = entryPointName.replace(/^pages/, '');
623-
return !stringMatchesSomePattern(entryPointRoute, excludeServerRoutes, true);
624-
}
597+
entryPointName === 'main-app')
598+
);
625599
}
626600

627601
/**

packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts

Lines changed: 0 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@ import {
77
CLIENT_SDK_CONFIG_FILE,
88
clientBuildContext,
99
clientWebpackConfig,
10-
EDGE_SDK_CONFIG_FILE,
11-
edgeBuildContext,
1210
exportedNextConfig,
13-
SERVER_SDK_CONFIG_FILE,
1411
serverBuildContext,
1512
serverWebpackConfig,
1613
userNextConfig,
@@ -88,143 +85,22 @@ describe('constructWebpackConfigFunction()', () => {
8885
});
8986

9087
describe('webpack `entry` property config', () => {
91-
const serverConfigFilePath = `./${SERVER_SDK_CONFIG_FILE}`;
9288
const clientConfigFilePath = `./${CLIENT_SDK_CONFIG_FILE}`;
93-
const edgeConfigFilePath = `./${EDGE_SDK_CONFIG_FILE}`;
94-
95-
it('handles various entrypoint shapes', async () => {
96-
const finalWebpackConfig = await materializeFinalWebpackConfig({
97-
exportedNextConfig,
98-
incomingWebpackConfig: serverWebpackConfig,
99-
incomingWebpackBuildContext: serverBuildContext,
100-
});
101-
102-
expect(finalWebpackConfig.entry).toEqual(
103-
expect.objectContaining({
104-
// original entrypoint value is a string
105-
// (was 'private-next-pages/_error.js')
106-
'pages/_error': [serverConfigFilePath, 'private-next-pages/_error.js'],
107-
108-
// original entrypoint value is a string array
109-
// (was ['./node_modules/smellOVision/index.js', 'private-next-pages/sniffTour.js'])
110-
'pages/sniffTour': [
111-
serverConfigFilePath,
112-
'./node_modules/smellOVision/index.js',
113-
'private-next-pages/sniffTour.js',
114-
],
115-
116-
// original entrypoint value is an object containing a string `import` value
117-
// (was { import: 'private-next-pages/api/simulator/dogStats/[name].js' })
118-
'pages/api/simulator/dogStats/[name]': {
119-
import: [serverConfigFilePath, 'private-next-pages/api/simulator/dogStats/[name].js'],
120-
},
121-
122-
// original entrypoint value is an object containing a string array `import` value
123-
// (was { import: ['./node_modules/dogPoints/converter.js', 'private-next-pages/simulator/leaderboard.js'] })
124-
'pages/simulator/leaderboard': {
125-
import: [
126-
serverConfigFilePath,
127-
'./node_modules/dogPoints/converter.js',
128-
'private-next-pages/simulator/leaderboard.js',
129-
],
130-
},
131-
132-
// original entrypoint value is an object containg properties besides `import`
133-
// (was { import: 'private-next-pages/api/tricks/[trickName].js', dependOn: 'treats', })
134-
'pages/api/tricks/[trickName]': {
135-
import: [serverConfigFilePath, 'private-next-pages/api/tricks/[trickName].js'],
136-
dependOn: 'treats', // untouched
137-
},
138-
}),
139-
);
140-
});
14189

14290
it('injects user config file into `_app` in server bundle and in the client bundle', async () => {
143-
const finalServerWebpackConfig = await materializeFinalWebpackConfig({
144-
exportedNextConfig,
145-
incomingWebpackConfig: serverWebpackConfig,
146-
incomingWebpackBuildContext: serverBuildContext,
147-
});
14891
const finalClientWebpackConfig = await materializeFinalWebpackConfig({
14992
exportedNextConfig,
15093
incomingWebpackConfig: clientWebpackConfig,
15194
incomingWebpackBuildContext: clientBuildContext,
15295
});
15396

154-
expect(finalServerWebpackConfig.entry).toEqual(
155-
expect.objectContaining({
156-
'pages/_app': expect.arrayContaining([serverConfigFilePath]),
157-
}),
158-
);
15997
expect(finalClientWebpackConfig.entry).toEqual(
16098
expect.objectContaining({
16199
'pages/_app': expect.arrayContaining([clientConfigFilePath]),
162100
}),
163101
);
164102
});
165103

166-
it('injects user config file into `_error` in server bundle but not client bundle', async () => {
167-
const finalServerWebpackConfig = await materializeFinalWebpackConfig({
168-
exportedNextConfig,
169-
incomingWebpackConfig: serverWebpackConfig,
170-
incomingWebpackBuildContext: serverBuildContext,
171-
});
172-
const finalClientWebpackConfig = await materializeFinalWebpackConfig({
173-
exportedNextConfig,
174-
incomingWebpackConfig: clientWebpackConfig,
175-
incomingWebpackBuildContext: clientBuildContext,
176-
});
177-
178-
expect(finalServerWebpackConfig.entry).toEqual(
179-
expect.objectContaining({
180-
'pages/_error': expect.arrayContaining([serverConfigFilePath]),
181-
}),
182-
);
183-
expect(finalClientWebpackConfig.entry).toEqual(
184-
expect.objectContaining({
185-
'pages/_error': expect.not.arrayContaining([clientConfigFilePath]),
186-
}),
187-
);
188-
});
189-
190-
it('injects user config file into both API routes and non-API routes', async () => {
191-
const finalWebpackConfig = await materializeFinalWebpackConfig({
192-
exportedNextConfig,
193-
incomingWebpackConfig: serverWebpackConfig,
194-
incomingWebpackBuildContext: serverBuildContext,
195-
});
196-
197-
expect(finalWebpackConfig.entry).toEqual(
198-
expect.objectContaining({
199-
'pages/api/simulator/dogStats/[name]': {
200-
import: expect.arrayContaining([serverConfigFilePath]),
201-
},
202-
203-
'pages/api/tricks/[trickName]': expect.objectContaining({
204-
import: expect.arrayContaining([serverConfigFilePath]),
205-
}),
206-
207-
'pages/simulator/leaderboard': {
208-
import: expect.arrayContaining([serverConfigFilePath]),
209-
},
210-
}),
211-
);
212-
});
213-
214-
it('injects user config file into API middleware', async () => {
215-
const finalWebpackConfig = await materializeFinalWebpackConfig({
216-
exportedNextConfig,
217-
incomingWebpackConfig: serverWebpackConfig,
218-
incomingWebpackBuildContext: edgeBuildContext,
219-
});
220-
221-
expect(finalWebpackConfig.entry).toEqual(
222-
expect.objectContaining({
223-
middleware: [edgeConfigFilePath, 'private-next-pages/middleware.js'],
224-
}),
225-
);
226-
});
227-
228104
it('does not inject anything into non-_app pages during client build', async () => {
229105
const finalWebpackConfig = await materializeFinalWebpackConfig({
230106
exportedNextConfig,
@@ -244,30 +120,5 @@ describe('constructWebpackConfigFunction()', () => {
244120
simulatorBundle: './src/simulator/index.ts',
245121
});
246122
});
247-
248-
it('does not inject into routes included in `excludeServerRoutes`', async () => {
249-
const nextConfigWithExcludedRoutes = {
250-
...exportedNextConfig,
251-
sentry: {
252-
excludeServerRoutes: [/simulator/],
253-
},
254-
};
255-
const finalWebpackConfig = await materializeFinalWebpackConfig({
256-
exportedNextConfig: nextConfigWithExcludedRoutes,
257-
incomingWebpackConfig: serverWebpackConfig,
258-
incomingWebpackBuildContext: serverBuildContext,
259-
});
260-
261-
expect(finalWebpackConfig.entry).toEqual(
262-
expect.objectContaining({
263-
'pages/simulator/leaderboard': {
264-
import: expect.not.arrayContaining([serverConfigFilePath]),
265-
},
266-
'pages/api/simulator/dogStats/[name]': {
267-
import: expect.not.arrayContaining([serverConfigFilePath]),
268-
},
269-
}),
270-
);
271-
});
272123
});
273124
});

0 commit comments

Comments
 (0)