|
| 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(); |
0 commit comments