Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 1.8k
ref(nextjs): Inject init code in _app and API routes#3786
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -4,7 +4,6 @@ import * as SentryWebpackPlugin from '@sentry/webpack-plugin'; | ||
| import { | ||
| BuildContext, | ||
| EntryPointObject, | ||
| EntryPointValue, | ||
| EntryPropertyObject, | ||
| NextConfigObject, | ||
| @@ -13,16 +12,15 @@ import { | ||
| WebpackConfigObject, | ||
| WebpackEntryProperty, | ||
| } from './types'; | ||
| import { SERVER_SDK_INIT_PATH, storeServerConfigFileLocation } from './utils'; | ||
| export { SentryWebpackPlugin }; | ||
| // TODO: merge default SentryWebpackPlugin ignore with their SentryWebpackPlugin ignore or ignoreFile | ||
| // TODO: merge default SentryWebpackPlugin include with their SentryWebpackPlugin include | ||
| // TODO: drop merged keys from override check? `includeDefaults` option? | ||
| const CLIENT_SDK_CONFIG_FILE = './sentry.client.config.js'; | ||
| const SERVER_SDK_CONFIG_FILE = './sentry.server.config.js'; | ||
| export const CLIENT_SDK_CONFIG_FILE = './sentry.client.config.js'; | ||
| export const SERVER_SDK_CONFIG_FILE = './sentry.server.config.js'; | ||
| const defaultSentryWebpackPluginOptions = dropUndefinedKeys({ | ||
| url: process.env.SENTRY_URL, | ||
| @@ -58,12 +56,6 @@ export function constructWebpackConfigFunction( | ||
| const newWebpackFunction = (incomingConfig: WebpackConfigObject, buildContext: BuildContext): WebpackConfigObject => { | ||
| let newConfig = { ...incomingConfig }; | ||
| // if we're building server code, store the webpack output path as an env variable, so we know where to look for the | ||
| // webpack-processed version of `sentry.server.config.js` when we need it | ||
| if (newConfig.target === 'node') { | ||
| storeServerConfigFileLocation(newConfig); | ||
| } | ||
| // if user has custom webpack config (which always takes the form of a function), run it so we have actual values to | ||
| // work with | ||
| if ('webpack' in userNextConfig && typeof userNextConfig.webpack === 'function') { | ||
| @@ -140,39 +132,11 @@ async function addSentryToEntryProperty( | ||
| const newEntryProperty = | ||
| typeof currentEntryProperty === 'function' ? await currentEntryProperty() : { ...currentEntryProperty }; | ||
| // Add a new element to the `entry` array, we force webpack to create a bundle out of the user's | ||
| // `sentry.server.config.js` file and output it to `SERVER_INIT_LOCATION`. (See | ||
| // https://webpack.js.org/guides/code-splitting/#entry-points.) We do this so that the user's config file is run | ||
| // through babel (and any other processors through which next runs the rest of the user-provided code - pages, API | ||
| // routes, etc.). Specifically, we need any ESM-style `import` code to get transpiled into ES5, so that we can call | ||
| // `require()` on the resulting file when we're instrumenting the sesrver. (We can't use a dynamic import there | ||
| // because that then forces the user into a particular TS config.) | ||
| // On the server, create a separate bundle, as there's no one entry point depended on by all the others | ||
| if (buildContext.isServer) { | ||
| // slice off the final `.js` since webpack is going to add it back in for us, and we don't want to end up with | ||
| // `.js.js` as the extension | ||
| newEntryProperty[SERVER_SDK_INIT_PATH.slice(0, -3)] = SERVER_SDK_CONFIG_FILE; | ||
| } | ||
| // On the client, it's sufficient to inject it into the `main` JS code, which is included in every browser page. | ||
| else { | ||
| addFileToExistingEntryPoint(newEntryProperty, 'main', CLIENT_SDK_CONFIG_FILE); | ||
| // To work around a bug in nextjs, we need to ensure that the `main.js` entry is empty (otherwise it'll choose that | ||
| // over `main` and we'll lose the change we just made). In case some other library has put something into it, copy | ||
| // its contents over before emptying it out. See | ||
| // https://github.com/getsentry/sentry-javascript/pull/3696#issuecomment-863363803.) | ||
| const mainjsValue = newEntryProperty['main.js']; | ||
| if (Array.isArray(mainjsValue) && mainjsValue.length > 0) { | ||
| const mainValue = newEntryProperty.main; | ||
| // copy the `main.js` entries over | ||
| newEntryProperty.main = Array.isArray(mainValue) | ||
| ? [...mainjsValue, ...mainValue] | ||
| : { ...(mainValue as EntryPointObject), import: [...mainjsValue, ...(mainValue as EntryPointObject).import] }; | ||
| // nuke the entries | ||
| newEntryProperty['main.js'] = []; | ||
| const userConfigFile = buildContext.isServer ? SERVER_SDK_CONFIG_FILE : CLIENT_SDK_CONFIG_FILE; | ||
| for (const entryPointName in newEntryProperty) { | ||
| if (entryPointName === 'pages/_app' || entryPointName.includes('pages/api')) { | ||
| addFileToExistingEntryPoint(newEntryProperty, entryPointName, userConfigFile); | ||
| } | ||
| } | ||
| @@ -195,22 +159,20 @@ function addFileToExistingEntryPoint( | ||
| const currentEntryPoint = entryProperty[entryPointName]; | ||
| let newEntryPoint: EntryPointValue; | ||
| // We inject the user's client config file after the existing code so that the config file has access to | ||
| // `publicRuntimeConfig`. See https://github.com/getsentry/sentry-javascript/issues/3485 | ||
| if (typeof currentEntryPoint === 'string') { | ||
| newEntryPoint = [currentEntryPoint, filepath]; | ||
| newEntryPoint = [filepath, currentEntryPoint]; | ||
iker-barriocanal marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } else if (Array.isArray(currentEntryPoint)) { | ||
| newEntryPoint = [...currentEntryPoint, filepath]; | ||
| newEntryPoint = [filepath, ...currentEntryPoint]; | ||
lobsterkatie marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } | ||
| // descriptor object (webpack 5+) | ||
| else if (typeof currentEntryPoint === 'object' && 'import' in currentEntryPoint) { | ||
| const currentImportValue = currentEntryPoint.import; | ||
| let newImportValue: string | string[]; | ||
| let newImportValue; | ||
lobsterkatie marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| if (typeof currentImportValue === 'string') { | ||
| newImportValue = [currentImportValue, filepath]; | ||
| newImportValue = [filepath, currentImportValue]; | ||
| } else { | ||
| newImportValue = [...currentImportValue, filepath]; | ||
| newImportValue = [filepath, ...currentImportValue]; | ||
| } | ||
| newEntryPoint = { | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -5,7 +5,6 @@ import { fill, isString, logger, stripUrlQueryAndFragment } from '@sentry/utils' | ||
| import * as domain from 'domain'; | ||
| import * as http from 'http'; | ||
| import { default as createNextServer } from 'next'; | ||
| import * as path from 'path'; | ||
| import * as querystring from 'querystring'; | ||
| import * as url from 'url'; | ||
| @@ -111,18 +110,6 @@ function makeWrappedHandlerGetter(origHandlerGetter: HandlerGetter): WrappedHand | ||
| // Otherwise, it's just a pass-through to the original method. | ||
| const wrappedHandlerGetter = async function(this: NextServer): Promise<ReqHandler> { | ||
| if (!sdkSetupComplete) { | ||
| try { | ||
| // `SENTRY_SERVER_INIT_PATH` is set at build time, and points to a webpack-processed version of the user's | ||
| // `sentry.server.config.js`. Requiring it starts the SDK. | ||
| require(path.resolve(process.env.SENTRY_SERVER_INIT_PATH as string)); | ||
lobsterkatie marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| } catch (err) { | ||
| // Log the error but don't bail - we still want the wrapping to happen, in case the user is doing something weird | ||
| // and manually calling `Sentry.init()` somewhere else. We log to console instead of using logger from utils | ||
| // because Sentry is not initialized. | ||
| // eslint-disable-next-line no-console | ||
| console.error(`[Sentry] Could not initialize SDK. Received error:\n${err}`); | ||
| } | ||
| // stash this in the closure so that `makeWrappedReqHandler` can use it | ||
| liveServer = this.server; | ||
| const serverPrototype = Object.getPrototypeOf(liveServer); | ||
Uh oh!
There was an error while loading. Please reload this page.