Skip to content

Commit d34fd36

Browse files
authored
feat(nextjs): Add browser SDK to app directory browser bundle (#6812)
1 parent e357c90 commit d34fd36

21 files changed

+174
-57
lines changed

packages/nextjs/src/config/webpack.ts

+9-14
Original file line numberDiff line numberDiff line change
@@ -500,12 +500,12 @@ function shouldAddSentryToEntryPoint(
500500
excludeServerRoutes: Array<string | RegExp> = [],
501501
isDev: boolean,
502502
): boolean {
503-
if (entryPointName === 'middleware') {
504-
return true;
505-
}
506-
507503
// On the server side, by default we inject the `Sentry.init()` code into every page (with a few exceptions).
508504
if (isServer) {
505+
if (entryPointName === 'middleware') {
506+
return true;
507+
}
508+
509509
const entryPointRoute = entryPointName.replace(/^pages/, '');
510510

511511
// User-specified pages to skip. (Note: For ease of use, `excludeServerRoutes` is specified in terms of routes,
@@ -527,23 +527,18 @@ function shouldAddSentryToEntryPoint(
527527
// versions.)
528528
entryPointRoute === '/_app' ||
529529
entryPointRoute === '/_document' ||
530-
// Newer versions of nextjs are starting to introduce things outside the `pages/` folder (middleware, an `app/`
531-
// directory, etc), but until those features are stable and we know how we want to support them, the safest bet is
532-
// not to inject anywhere but inside `pages/`.
533530
!entryPointName.startsWith('pages/')
534531
) {
535532
return false;
536533
}
537534

538535
// We want to inject Sentry into all other pages
539536
return true;
540-
}
541-
542-
// On the client side, we only want to inject into `_app`, because that guarantees there'll be only one copy of the
543-
// SDK in the eventual bundle. Since `_app` is the (effectively) the root component for every nextjs app, inclusing
544-
// Sentry there means it will be available for every front end page.
545-
else {
546-
return entryPointName === 'pages/_app';
537+
} else {
538+
return (
539+
entryPointName === 'pages/_app' || // entrypoint for `/pages` pages
540+
entryPointName === 'main-app' // entrypoint for `/app` pages
541+
);
547542
}
548543
}
549544

packages/nextjs/test/integration/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,6 @@ yarn-error.log*
3434

3535
# vercel
3636
.vercel
37+
38+
# Generated by Next.js 13
39+
.vscode
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function ({ children }: { children: React.ReactNode }) {
2+
return <>{children}</>;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use client';
2+
3+
export default function () {
4+
return <p>I am a client component!</p>;
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function ({ children }: { children: React.ReactNode }) {
2+
return <>{children}</>;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default async function () {
2+
return <p>I am a server component!</p>;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
const { withSentryConfig } = require('@sentry/nextjs');
2+
3+
// NOTE: This will be used by integration tests to distinguish between Webpack 4 and Webpack 5
4+
const moduleExports = {
5+
webpack5: %RUN_WEBPACK_5%,
6+
eslint: {
7+
ignoreDuringBuilds: true,
8+
},
9+
pageExtensions: ['jsx', 'js', 'tsx', 'ts', 'page.tsx'],
10+
sentry: {
11+
// Suppress the warning message from `handleSourcemapHidingOptionWarning` in `src/config/webpack.ts`
12+
// TODO (v8): This can come out in v8, because this option will get a default value
13+
hideSourceMaps: false,
14+
excludeServerRoutes: [
15+
'/api/excludedEndpoints/excludedWithString',
16+
/\/api\/excludedEndpoints\/excludedWithRegExp/,
17+
],
18+
},
19+
};
20+
21+
const SentryWebpackPluginOptions = {
22+
dryRun: true,
23+
silent: true,
24+
};
25+
26+
module.exports = withSentryConfig(moduleExports, SentryWebpackPluginOptions);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const { withSentryConfig } = require('@sentry/nextjs');
2+
3+
// NOTE: This will be used by integration tests to distinguish between Webpack 4 and Webpack 5
4+
const moduleExports = {
5+
webpack5: %RUN_WEBPACK_5%,
6+
eslint: {
7+
ignoreDuringBuilds: true,
8+
},
9+
experimental: {
10+
appDir: Number(process.env.NODE_MAJOR) >= 16, // experimental.appDir requires Node v16.8.0 or later.
11+
},
12+
pageExtensions: ['jsx', 'js', 'tsx', 'ts', 'page.tsx'],
13+
sentry: {
14+
// Suppress the warning message from `handleSourcemapHidingOptionWarning` in `src/config/webpack.ts`
15+
// TODO (v8): This can come out in v8, because this option will get a default value
16+
hideSourceMaps: false,
17+
excludeServerRoutes: [
18+
'/api/excludedEndpoints/excludedWithString',
19+
/\/api\/excludedEndpoints\/excludedWithRegExp/,
20+
],
21+
},
22+
};
23+
24+
const SentryWebpackPluginOptions = {
25+
dryRun: true,
26+
silent: true,
27+
};
28+
29+
module.exports = withSentryConfig(moduleExports, SentryWebpackPluginOptions);
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { withSentry } from '@sentry/nextjs';
21
import { NextApiRequest, NextApiResponse } from 'next';
32

43
const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void> => {
54
res.status(500).json({ statusCode: 500, message: 'Something went wrong' });
65
};
76

8-
export default withSentry(handler);
7+
export default handler;
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { withSentry } from '@sentry/nextjs';
21
import { NextApiRequest, NextApiResponse } from 'next';
32

43
const handler = async (_req: NextApiRequest, _res: NextApiResponse): Promise<void> => {
54
throw new Error('API Error');
65
};
76

8-
export default withSentry(handler);
7+
export default handler;

packages/nextjs/test/integration/pages/api/http/index.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { withSentry } from '@sentry/nextjs';
21
import { get } from 'http';
32
import { NextApiRequest, NextApiResponse } from 'next';
43

@@ -9,4 +8,4 @@ const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void
98
res.status(200).json({});
109
};
1110

12-
export default withSentry(handler);
11+
export default handler;

packages/nextjs/test/integration/pages/api/users/index.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { withSentry } from '@sentry/nextjs';
21
import { NextApiRequest, NextApiResponse } from 'next';
32

43
import { sampleUserData } from '../../../utils/sample-data';
@@ -15,4 +14,4 @@ const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void
1514
}
1615
};
1716

18-
export default withSentry(handler);
17+
export default handler;
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { withSentry } from '@sentry/nextjs';
21
import { NextApiRequest, NextApiResponse } from 'next';
32

43
const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void> => {
54
res.status(200).json({});
65
};
76

8-
export default withSentry(handler);
7+
export default handler;
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { withSentry } from '@sentry/nextjs';
21
import { NextApiRequest, NextApiResponse } from 'next';
32

43
const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void> => {
54
res.status(200).json({});
65
};
76

8-
export default withSentry(handler);
7+
export default handler;
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { withSentry } from '@sentry/nextjs';
21
import { NextApiRequest, NextApiResponse } from 'next';
32

43
const handler = async (_req: NextApiRequest, res: NextApiResponse): Promise<void> => {
54
res.status(200).json({});
65
};
76

8-
export default withSentry(handler);
7+
export default handler;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import * as Sentry from '@sentry/nextjs';
2+
3+
Sentry.init({
4+
dsn: 'https://[email protected]/1337',
5+
tracesSampleRate: 1,
6+
debug: process.env.SDK_DEBUG,
7+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const { expectRequestCount, isTransactionRequest, expectTransaction } = require('../utils/client');
2+
3+
module.exports = async ({ page, url, requests }) => {
4+
if (Number(process.env.NEXTJS_VERSION) < 13 || Number(process.env.NODE_MAJOR) < 16) {
5+
// Next.js versions < 13 don't support the app directory and the app dir requires Node v16.8.0 or later.
6+
return;
7+
}
8+
9+
const requestPromise = page.waitForRequest(isTransactionRequest);
10+
await page.goto(`${url}/clientcomponent`);
11+
await requestPromise;
12+
13+
expectTransaction(requests.transactions[0], {
14+
contexts: {
15+
trace: {
16+
op: 'pageload',
17+
},
18+
},
19+
transaction: '/clientcomponent',
20+
});
21+
22+
await expectRequestCount(requests, { transactions: 1 });
23+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const { expectRequestCount, isTransactionRequest, expectTransaction } = require('../utils/client');
2+
3+
module.exports = async ({ page, url, requests }) => {
4+
if (Number(process.env.NEXTJS_VERSION) < 13 || Number(process.env.NODE_MAJOR) < 16) {
5+
// Next.js versions < 13 don't support the app directory and the app dir requires Node v16.8.0 or later.
6+
return;
7+
}
8+
9+
const requestPromise = page.waitForRequest(isTransactionRequest);
10+
await page.goto(`${url}/servercomponent`);
11+
await requestPromise;
12+
13+
expectTransaction(requests.transactions[0], {
14+
contexts: {
15+
trace: {
16+
op: 'pageload',
17+
},
18+
},
19+
transaction: '/servercomponent',
20+
});
21+
22+
await expectRequestCount(requests, { transactions: 1 });
23+
};
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
1-
const { waitForAll } = require('../utils/common');
2-
const { expectRequestCount, isSessionRequest, expectSession } = require('../utils/client');
1+
const { expectRequestCount, isSessionRequest, expectSession, extractEnvelopeFromRequest } = require('../utils/client');
32

43
module.exports = async ({ page, url, requests }) => {
5-
await waitForAll([
6-
page.goto(`${url}/crashed`),
7-
page.waitForRequest(isSessionRequest),
8-
page.waitForRequest(isSessionRequest),
9-
]);
10-
11-
expectSession(requests.sessions[0], {
12-
init: true,
13-
status: 'ok',
14-
errors: 0,
4+
const sessionRequestPromise1 = page.waitForRequest(request => {
5+
if (isSessionRequest(request)) {
6+
const { item } = extractEnvelopeFromRequest(request);
7+
return item.init === true && item.status === 'ok' && item.errors === 0;
8+
} else {
9+
return false;
10+
}
1511
});
1612

17-
expectSession(requests.sessions[1], {
18-
init: false,
19-
status: 'crashed',
20-
errors: 1,
13+
const sessionRequestPromise2 = page.waitForRequest(request => {
14+
if (isSessionRequest(request)) {
15+
const { item } = extractEnvelopeFromRequest(request);
16+
return item.init === false && item.status === 'crashed' && item.errors === 1;
17+
} else {
18+
return false;
19+
}
2120
});
2221

22+
await page.goto(`${url}/crashed`);
23+
await sessionRequestPromise1;
24+
await sessionRequestPromise2;
25+
2326
await expectRequestCount(requests, { sessions: 2 });
2427
};

packages/nextjs/test/integration/tsconfig.json

+9-12
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,7 @@
66
"forceConsistentCasingInFileNames": true,
77
"isolatedModules": true,
88
"jsx": "preserve",
9-
"lib": [
10-
"dom",
11-
"es2017"
12-
],
9+
"lib": ["dom", "es2017"],
1310
"module": "esnext",
1411
"moduleResolution": "node",
1512
"noEmit": true,
@@ -20,13 +17,13 @@
2017
"skipLibCheck": true,
2118
"strict": true,
2219
"target": "esnext",
23-
"incremental": true
20+
"incremental": true,
21+
"plugins": [
22+
{
23+
"name": "next"
24+
}
25+
]
2426
},
25-
"exclude": [
26-
"node_modules"
27-
],
28-
"include": [
29-
"**/*.ts",
30-
"**/*.tsx"
31-
]
27+
"exclude": ["node_modules"],
28+
"include": ["**/*.ts", "**/*.tsx", ".next/types/**/*.ts"]
3229
}

packages/nextjs/test/run-integration-tests.sh

+5-1
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,12 @@ for NEXTJS_VERSION in 10 11 12 13; do
107107
# next 10 defaults to webpack 4 and next 11 defaults to webpack 5, but each can use either based on settings
108108
if [ "$NEXTJS_VERSION" -eq "10" ]; then
109109
sed "s/%RUN_WEBPACK_5%/$RUN_WEBPACK_5/g" <next10.config.template >next.config.js
110-
else
110+
elif [ "$NEXTJS_VERSION" -eq "11" ]; then
111111
sed "s/%RUN_WEBPACK_5%/$RUN_WEBPACK_5/g" <next11.config.template >next.config.js
112+
elif [ "$NEXTJS_VERSION" -eq "12" ]; then
113+
sed "s/%RUN_WEBPACK_5%/$RUN_WEBPACK_5/g" <next12.config.template >next.config.js
114+
elif [ "$NEXTJS_VERSION" -eq "13" ]; then
115+
sed "s/%RUN_WEBPACK_5%/$RUN_WEBPACK_5/g" <next13.config.template >next.config.js
112116
fi
113117

114118
echo "[nextjs@$NEXTJS_VERSION | webpack@$WEBPACK_VERSION] Building..."

0 commit comments

Comments
 (0)