Skip to content

ref(nextjs): Move config code to SDK and fix next version bug #3418

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions packages/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"engines": {
"node": ">=6"
},
"main": "./dist/index.server.js",
"module": "./esm/index.server.js",
"browser": "./esm/index.client.js",
"types": "./esm/index.client.d.ts",
Expand All @@ -21,18 +22,22 @@
"@sentry/next-plugin-sentry": "6.2.5",
"@sentry/node": "6.2.5",
"@sentry/react": "6.2.5",
"@sentry/utils": "6.2.5",
"@sentry/webpack-plugin": "1.15.0"
},
"devDependencies": {
"@sentry/types": "6.2.5",
"@types/webpack": "^5.28.0",
"eslint": "7.20.0",
"rimraf": "3.0.2"
},
"scripts": {
"build": "run-p build:esm",
"build": "run-p build:esm build:es5",
"build:esm": "tsc -p tsconfig.esm.json",
"build:watch": "run-p build:watch:esm",
"build:es5": "tsc -p tsconfig.build.json",
"build:watch": "run-p build:watch:esm build:watch:es5",
"build:watch:esm": "tsc -p tsconfig.esm.json -w --preserveWatchOutput",
"build:watch:es5": "tsc -p tsconfig.build.json -w --preserveWatchOutput",
"clean": "rimraf dist esm coverage *.js *.js.map *.d.ts",
"link:yarn": "yarn link",
"lint": "run-s lint:prettier lint:eslint",
Expand Down
2 changes: 2 additions & 0 deletions packages/nextjs/src/index.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,5 @@ export function init(options: NextjsOptions): void {
scope.setTag('runtime', 'node');
});
}

export { withSentryConfig } from './utils/config';
99 changes: 99 additions & 0 deletions packages/nextjs/src/utils/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// import { version as nextVersion } from './node_modules/next/package.json';
import { getSentryRelease } from '@sentry/node';
import { logger } from '@sentry/utils';
import defaultWebpackPlugin, { SentryCliPluginOptions } from '@sentry/webpack-plugin';
import * as SentryWebpackPlugin from '@sentry/webpack-plugin';
import * as fs from 'fs';

/**
* Next requires that plugins be tagged with the same version number as the currently-running `next.js` package, so
* modify our `package.json` to trick it into thinking we comply. Run before the plugin is loaded at server startup.
*/
export function syncPluginVersionWithNextVersion(): void {
// TODO Once we get at least to TS 2.9, we can use `"resolveJsonModule": true` in our `compilerOptions` and we'll be
// able to do:
// import { version as nextVersion } from './node_modules/next/package.json';

// eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-member-access
const nextVersion = (require('../../../../next/package.json') as any).version;
if (!nextVersion) {
logger.error('[next-plugin-sentry] Cannot read next.js version. Plug-in will not work.');
return;
}

const pluginPackageDotJsonPath = `../../../next-plugin-sentry/package.json`;
// eslint-disable-next-line @typescript-eslint/no-var-requires
const pluginPackageDotJson = require(pluginPackageDotJsonPath); // see TODO above
if (!pluginPackageDotJson) {
logger.error(`[next-plugin-sentry] Cannot read ${pluginPackageDotJsonPath}. Plug-in will not work.`);
return;
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
(pluginPackageDotJson as any).version = nextVersion;
// interestingly, the `require` calls above seem to resolve from a different starting point than `fs` does here, which
// is why we can't just use `pluginPackageDotJsonPath` again
fs.writeFileSync('./node_modules/@sentry/next-plugin-sentry/package.json', JSON.stringify(pluginPackageDotJson));
}

type WebpackConfig = { devtool: string; plugins: Array<{ [key: string]: any }> };
type NextConfigExports = {
experimental?: { plugins: boolean };
plugins?: string[];
productionBrowserSourceMaps?: boolean;
webpack?: (config: WebpackConfig, { dev }: { dev: boolean }) => WebpackConfig;
};

export function withSentryConfig(
providedExports: NextConfigExports = {},
providedWebpackPluginOptions: Partial<SentryCliPluginOptions> = {},
): NextConfigExports {
const defaultWebpackPluginOptions = {
release: getSentryRelease(),
url: process.env.SENTRY_URL,
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
authToken: process.env.SENTRY_AUTH_TOKEN,
configFile: 'sentry.properties',
stripPrefix: ['webpack://_N_E/'],
urlPrefix: `~/_next`,
include: '.next/',
ignore: ['node_modules', 'webpack.config.js'],
};
const webpackPluginOptionOverrides = Object.keys(defaultWebpackPluginOptions)
.concat('dryrun')
.map(key => key in Object.keys(providedWebpackPluginOptions));
if (webpackPluginOptionOverrides.length > 0) {
logger.warn(
'[next-plugin-sentry] You are overriding the following automatically-set SentryWebpackPlugin config options:\n' +
`\t${webpackPluginOptionOverrides.toString()},\n` +
"which has the possibility of breaking source map upload and application. This is only a good idea if you know what you're doing.",
);
}

return {
experimental: { plugins: true },
plugins: [...(providedExports.plugins || []), '@sentry/next-plugin-sentry'],
productionBrowserSourceMaps: true,
webpack: (config, { dev }) => {
if (!dev) {
// Ensure quality source maps in production. (Source maps aren't uploaded in dev, and besides, Next doesn't let
// you change this is dev even if you want to - see
// https://github.com/vercel/next.js/blob/master/errors/improper-devtool.md.)
config.devtool = 'source-map';
}
config.plugins.push(
// TODO it's not clear how to do this better, but there *must* be a better way
new ((SentryWebpackPlugin as unknown) as typeof defaultWebpackPlugin)({
dryRun: dev,
...defaultWebpackPluginOptions,
...providedWebpackPluginOptions,
}),
);
return config;
},
};
}

syncPluginVersionWithNextVersion();
2 changes: 1 addition & 1 deletion packages/node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export {

export { NodeBackend, NodeOptions } from './backend';
export { NodeClient } from './client';
export { defaultIntegrations, init, lastEventId, flush, close } from './sdk';
export { defaultIntegrations, init, lastEventId, flush, close, getSentryRelease } from './sdk';
export { SDK_NAME } from './version';

import { Integrations as CoreIntegrations } from '@sentry/core';
Expand Down
5 changes: 4 additions & 1 deletion packages/node/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export async function close(timeout?: number): Promise<boolean> {
/**
* A function that returns a Sentry release string dynamically from env variables
*/
function getSentryRelease(): string | undefined {
export function getSentryRelease(): string | undefined {
// Always read first as Sentry takes this as precedence
if (process.env.SENTRY_RELEASE) {
return process.env.SENTRY_RELEASE;
Expand All @@ -170,6 +170,9 @@ function getSentryRelease(): string | undefined {
process.env.COMMIT_REF ||
// Vercel - https://vercel.com/docs/v2/build-step#system-environment-variables
process.env.VERCEL_GIT_COMMIT_SHA ||
process.env.VERCEL_GITHUB_COMMIT_SHA ||
process.env.VERCEL_GITLAB_COMMIT_SHA ||
process.env.VERCEL_BITBUCKET_COMMIT_SHA ||
// Zeit (now known as Vercel)
process.env.ZEIT_GITHUB_COMMIT_SHA ||
process.env.ZEIT_GITLAB_COMMIT_SHA ||
Expand Down