Skip to content

Commit 2f3355e

Browse files
authored
ref(nextjs): Move config code to SDK and fix next version bug (#3418)
1 parent bd0dda5 commit 2f3355e

File tree

5 files changed

+113
-4
lines changed

5 files changed

+113
-4
lines changed

packages/nextjs/package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"engines": {
1010
"node": ">=6"
1111
},
12+
"main": "./dist/index.server.js",
1213
"module": "./esm/index.server.js",
1314
"browser": "./esm/index.client.js",
1415
"types": "./esm/index.client.d.ts",
@@ -21,18 +22,22 @@
2122
"@sentry/next-plugin-sentry": "6.2.5",
2223
"@sentry/node": "6.2.5",
2324
"@sentry/react": "6.2.5",
25+
"@sentry/utils": "6.2.5",
2426
"@sentry/webpack-plugin": "1.15.0"
2527
},
2628
"devDependencies": {
2729
"@sentry/types": "6.2.5",
30+
"@types/webpack": "^5.28.0",
2831
"eslint": "7.20.0",
2932
"rimraf": "3.0.2"
3033
},
3134
"scripts": {
32-
"build": "run-p build:esm",
35+
"build": "run-p build:esm build:es5",
3336
"build:esm": "tsc -p tsconfig.esm.json",
34-
"build:watch": "run-p build:watch:esm",
37+
"build:es5": "tsc -p tsconfig.build.json",
38+
"build:watch": "run-p build:watch:esm build:watch:es5",
3539
"build:watch:esm": "tsc -p tsconfig.esm.json -w --preserveWatchOutput",
40+
"build:watch:es5": "tsc -p tsconfig.build.json -w --preserveWatchOutput",
3641
"clean": "rimraf dist esm coverage *.js *.js.map *.d.ts",
3742
"link:yarn": "yarn link",
3843
"lint": "run-s lint:prettier lint:eslint",

packages/nextjs/src/index.server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,5 @@ export function init(options: NextjsOptions): void {
2525
scope.setTag('runtime', 'node');
2626
});
2727
}
28+
29+
export { withSentryConfig } from './utils/config';

packages/nextjs/src/utils/config.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
// import { version as nextVersion } from './node_modules/next/package.json';
3+
import { getSentryRelease } from '@sentry/node';
4+
import { logger } from '@sentry/utils';
5+
import defaultWebpackPlugin, { SentryCliPluginOptions } from '@sentry/webpack-plugin';
6+
import * as SentryWebpackPlugin from '@sentry/webpack-plugin';
7+
import * as fs from 'fs';
8+
9+
/**
10+
* Next requires that plugins be tagged with the same version number as the currently-running `next.js` package, so
11+
* modify our `package.json` to trick it into thinking we comply. Run before the plugin is loaded at server startup.
12+
*/
13+
export function syncPluginVersionWithNextVersion(): void {
14+
// TODO Once we get at least to TS 2.9, we can use `"resolveJsonModule": true` in our `compilerOptions` and we'll be
15+
// able to do:
16+
// import { version as nextVersion } from './node_modules/next/package.json';
17+
18+
// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access
19+
const nextVersion = (require('../../../../next/package.json') as any).version;
20+
if (!nextVersion) {
21+
logger.error('[next-plugin-sentry] Cannot read next.js version. Plug-in will not work.');
22+
return;
23+
}
24+
25+
const pluginPackageDotJsonPath = `../../../next-plugin-sentry/package.json`;
26+
// eslint-disable-next-line @typescript-eslint/no-var-requires
27+
const pluginPackageDotJson = require(pluginPackageDotJsonPath); // see TODO above
28+
if (!pluginPackageDotJson) {
29+
logger.error(`[next-plugin-sentry] Cannot read ${pluginPackageDotJsonPath}. Plug-in will not work.`);
30+
return;
31+
}
32+
33+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
34+
(pluginPackageDotJson as any).version = nextVersion;
35+
// interestingly, the `require` calls above seem to resolve from a different starting point than `fs` does here, which
36+
// is why we can't just use `pluginPackageDotJsonPath` again
37+
fs.writeFileSync('./node_modules/@sentry/next-plugin-sentry/package.json', JSON.stringify(pluginPackageDotJson));
38+
}
39+
40+
type WebpackConfig = { devtool: string; plugins: Array<{ [key: string]: any }> };
41+
type NextConfigExports = {
42+
experimental?: { plugins: boolean };
43+
plugins?: string[];
44+
productionBrowserSourceMaps?: boolean;
45+
webpack?: (config: WebpackConfig, { dev }: { dev: boolean }) => WebpackConfig;
46+
};
47+
48+
export function withSentryConfig(
49+
providedExports: NextConfigExports = {},
50+
providedWebpackPluginOptions: Partial<SentryCliPluginOptions> = {},
51+
): NextConfigExports {
52+
const defaultWebpackPluginOptions = {
53+
release: getSentryRelease(),
54+
url: process.env.SENTRY_URL,
55+
org: process.env.SENTRY_ORG,
56+
project: process.env.SENTRY_PROJECT,
57+
authToken: process.env.SENTRY_AUTH_TOKEN,
58+
configFile: 'sentry.properties',
59+
stripPrefix: ['webpack://_N_E/'],
60+
urlPrefix: `~/_next`,
61+
include: '.next/',
62+
ignore: ['node_modules', 'webpack.config.js'],
63+
};
64+
const webpackPluginOptionOverrides = Object.keys(defaultWebpackPluginOptions)
65+
.concat('dryrun')
66+
.map(key => key in Object.keys(providedWebpackPluginOptions));
67+
if (webpackPluginOptionOverrides.length > 0) {
68+
logger.warn(
69+
'[next-plugin-sentry] You are overriding the following automatically-set SentryWebpackPlugin config options:\n' +
70+
`\t${webpackPluginOptionOverrides.toString()},\n` +
71+
"which has the possibility of breaking source map upload and application. This is only a good idea if you know what you're doing.",
72+
);
73+
}
74+
75+
return {
76+
experimental: { plugins: true },
77+
plugins: [...(providedExports.plugins || []), '@sentry/next-plugin-sentry'],
78+
productionBrowserSourceMaps: true,
79+
webpack: (config, { dev }) => {
80+
if (!dev) {
81+
// Ensure quality source maps in production. (Source maps aren't uploaded in dev, and besides, Next doesn't let
82+
// you change this is dev even if you want to - see
83+
// https://github.com/vercel/next.js/blob/master/errors/improper-devtool.md.)
84+
config.devtool = 'source-map';
85+
}
86+
config.plugins.push(
87+
// TODO it's not clear how to do this better, but there *must* be a better way
88+
new ((SentryWebpackPlugin as unknown) as typeof defaultWebpackPlugin)({
89+
dryRun: dev,
90+
...defaultWebpackPluginOptions,
91+
...providedWebpackPluginOptions,
92+
}),
93+
);
94+
return config;
95+
},
96+
};
97+
}
98+
99+
syncPluginVersionWithNextVersion();

packages/node/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export {
4040

4141
export { NodeBackend, NodeOptions } from './backend';
4242
export { NodeClient } from './client';
43-
export { defaultIntegrations, init, lastEventId, flush, close } from './sdk';
43+
export { defaultIntegrations, init, lastEventId, flush, close, getSentryRelease } from './sdk';
4444
export { SDK_NAME } from './version';
4545

4646
import { Integrations as CoreIntegrations } from '@sentry/core';

packages/node/src/sdk.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ export async function close(timeout?: number): Promise<boolean> {
151151
/**
152152
* A function that returns a Sentry release string dynamically from env variables
153153
*/
154-
function getSentryRelease(): string | undefined {
154+
export function getSentryRelease(): string | undefined {
155155
// Always read first as Sentry takes this as precedence
156156
if (process.env.SENTRY_RELEASE) {
157157
return process.env.SENTRY_RELEASE;
@@ -170,6 +170,9 @@ function getSentryRelease(): string | undefined {
170170
process.env.COMMIT_REF ||
171171
// Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables
172172
process.env.VERCEL_GIT_COMMIT_SHA ||
173+
process.env.VERCEL_GITHUB_COMMIT_SHA ||
174+
process.env.VERCEL_GITLAB_COMMIT_SHA ||
175+
process.env.VERCEL_BITBUCKET_COMMIT_SHA ||
173176
// Zeit (now known as Vercel)
174177
process.env.ZEIT_GITHUB_COMMIT_SHA ||
175178
process.env.ZEIT_GITLAB_COMMIT_SHA ||

0 commit comments

Comments
 (0)