From d421a8e51c3239bb28b0f3077375cf788fc73aa0 Mon Sep 17 00:00:00 2001 From: Daniel Griesser Date: Fri, 30 Apr 2021 08:54:43 +0200 Subject: [PATCH 1/6] fix: Add tslib dependecy --- packages/nextjs/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 9fc343256d01..c4512675479d 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -22,7 +22,8 @@ "@sentry/node": "6.3.4", "@sentry/react": "6.3.4", "@sentry/utils": "6.3.4", - "@sentry/webpack-plugin": "1.15.0" + "@sentry/webpack-plugin": "1.15.0", + "tslib": "^1.9.3" }, "devDependencies": { "@sentry/types": "6.3.4", From 42992946935ec27cc7da1bd5bbff9f57a9b77d34 Mon Sep 17 00:00:00 2001 From: Daniel Griesser Date: Fri, 30 Apr 2021 09:21:09 +0200 Subject: [PATCH 2/6] fix: Add safeguard --- packages/nextjs/src/utils/config.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/nextjs/src/utils/config.ts b/packages/nextjs/src/utils/config.ts index 7446a4829c11..7bf142c72016 100644 --- a/packages/nextjs/src/utils/config.ts +++ b/packages/nextjs/src/utils/config.ts @@ -30,6 +30,14 @@ type EntryPointObject = { import: string | Array }; const _injectFile = (entryProperty: EntryPropertyObject, injectionPoint: string, injectee: string): void => { // can be a string, array of strings, or object whose `import` property is one of those two let injectedInto = entryProperty[injectionPoint]; + + // Sometimes especially for older next.js versions it happens we don't have an entry point + if (injectedInto === undefined) { + // eslint-disable-next-line no-console + console.error(`[Sentry] Can't inject ${injectee}, no entrypoint is defined.`); + return; + } + // whatever the format, add in the sentry file injectedInto = typeof injectedInto === 'string' From 914bbdafa7e6a8efe5645df3980d9f406e09314f Mon Sep 17 00:00:00 2001 From: Daniel Griesser Date: Fri, 30 Apr 2021 09:49:55 +0200 Subject: [PATCH 3/6] fix: Inject client after entry in frontend --- packages/nextjs/src/utils/config.ts | 52 +++++++++++++++++------------ 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/packages/nextjs/src/utils/config.ts b/packages/nextjs/src/utils/config.ts index 7bf142c72016..85fb3ecf4d80 100644 --- a/packages/nextjs/src/utils/config.ts +++ b/packages/nextjs/src/utils/config.ts @@ -38,32 +38,42 @@ const _injectFile = (entryProperty: EntryPropertyObject, injectionPoint: string, return; } - // whatever the format, add in the sentry file - injectedInto = - typeof injectedInto === 'string' - ? // string case - [injectee, injectedInto] - : // not a string, must be an array or object - Array.isArray(injectedInto) - ? // array case - [injectee, ...injectedInto] - : // object case - { - ...injectedInto, - import: - typeof injectedInto.import === 'string' - ? // string case for inner property - [injectee, injectedInto.import] - : // array case for inner property - [injectee, ...injectedInto.import], - }; + // In case we inject our client config, we need to add it after the frontend code + // otherwise the runtime config isn't loaded. See: https://github.com/getsentry/sentry-javascript/issues/3485 + const isClient = injectee === './sentry.client.config.js'; + + // Object -> Add it + if (injectedInto != null && typeof injectedInto == 'object' && !Array.isArray(injectedInto)) { + let importVal: string | string[] | EntryPointObject; + if (typeof injectedInto.import === 'string') { + importVal = isClient ? [injectedInto.import, injectee] : [injectee, injectedInto.import]; + } else { + // If it's not a string, the inner value is an array + importVal = isClient ? [...injectedInto.import, injectee] : [injectee, ...injectedInto.import]; + } + + injectedInto = { + ...injectedInto, + import: importVal, + }; + } + + // Array -> Add it + if (Array.isArray(injectedInto)) { + injectedInto = isClient ? [...injectedInto, injectee] : [injectee, ...injectedInto]; + } + + // String -> We need to make it an array + if (typeof injectedInto === 'string') { + injectedInto = isClient ? [injectedInto, injectee] : [injectee, injectedInto]; + } + entryProperty[injectionPoint] = injectedInto; }; const injectSentry = async (origEntryProperty: EntryProperty, isServer: boolean): Promise => { // Out of the box, nextjs uses the `() => Promise)` flavor of EntryProperty, where the returned - // object has string arrays for values. But because we don't know whether someone else has come along before us and - // changed that, we need to check a few things along the way. + // object has string arrays for values. // The `entry` entry in a webpack config can be a string, array of strings, object, or function. By default, nextjs // sets it to an async function which returns the promise of an object of string arrays. Because we don't know whether // someone else has come along before us and changed that, we need to check a few things along the way. The one thing From 4667b1062f6e5e8855672ff42a0741d21d6c5c85 Mon Sep 17 00:00:00 2001 From: Daniel Griesser Date: Fri, 30 Apr 2021 11:45:46 +0200 Subject: [PATCH 4/6] Update packages/nextjs/src/utils/config.ts Co-authored-by: iker barriocanal <32816711+iker-barriocanal@users.noreply.github.com> --- packages/nextjs/src/utils/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/nextjs/src/utils/config.ts b/packages/nextjs/src/utils/config.ts index 85fb3ecf4d80..0e8b8238a030 100644 --- a/packages/nextjs/src/utils/config.ts +++ b/packages/nextjs/src/utils/config.ts @@ -32,7 +32,7 @@ const _injectFile = (entryProperty: EntryPropertyObject, injectionPoint: string, let injectedInto = entryProperty[injectionPoint]; // Sometimes especially for older next.js versions it happens we don't have an entry point - if (injectedInto === undefined) { + if (!injectedInto) { // eslint-disable-next-line no-console console.error(`[Sentry] Can't inject ${injectee}, no entrypoint is defined.`); return; From 1667a222bfa0d006390e7d8659b95db95f56263d Mon Sep 17 00:00:00 2001 From: Daniel Griesser Date: Fri, 30 Apr 2021 11:48:20 +0200 Subject: [PATCH 5/6] ref: CR --- packages/nextjs/src/utils/config.ts | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/nextjs/src/utils/config.ts b/packages/nextjs/src/utils/config.ts index 0e8b8238a030..a9c0e6ae3bc4 100644 --- a/packages/nextjs/src/utils/config.ts +++ b/packages/nextjs/src/utils/config.ts @@ -26,6 +26,9 @@ type EntryProperty = (() => Promise) | EntryPropertyObject; type EntryPropertyObject = PlainObject | EntryPointObject>; type EntryPointObject = { import: string | Array }; +const sentryClientConfig = './sentry.client.config.js'; +const sentryServerConfig = './sentry.server.config.js'; + /** Add a file (`injectee`) to a given element (`injectionPoint`) of the `entry` property */ const _injectFile = (entryProperty: EntryPropertyObject, injectionPoint: string, injectee: string): void => { // can be a string, array of strings, or object whose `import` property is one of those two @@ -40,10 +43,13 @@ const _injectFile = (entryProperty: EntryPropertyObject, injectionPoint: string, // In case we inject our client config, we need to add it after the frontend code // otherwise the runtime config isn't loaded. See: https://github.com/getsentry/sentry-javascript/issues/3485 - const isClient = injectee === './sentry.client.config.js'; + const isClient = injectee === sentryClientConfig; - // Object -> Add it - if (injectedInto != null && typeof injectedInto == 'object' && !Array.isArray(injectedInto)) { + if (typeof injectedInto === 'string') { + injectedInto = isClient ? [injectedInto, injectee] : [injectee, injectedInto]; + } else if (Array.isArray(injectedInto)) { + injectedInto = isClient ? [...injectedInto, injectee] : [injectee, ...injectedInto]; + } else { let importVal: string | string[] | EntryPointObject; if (typeof injectedInto.import === 'string') { importVal = isClient ? [injectedInto.import, injectee] : [injectee, injectedInto.import]; @@ -58,16 +64,6 @@ const _injectFile = (entryProperty: EntryPropertyObject, injectionPoint: string, }; } - // Array -> Add it - if (Array.isArray(injectedInto)) { - injectedInto = isClient ? [...injectedInto, injectee] : [injectee, ...injectedInto]; - } - - // String -> We need to make it an array - if (typeof injectedInto === 'string') { - injectedInto = isClient ? [injectedInto, injectee] : [injectee, injectedInto]; - } - entryProperty[injectionPoint] = injectedInto; }; @@ -90,13 +86,13 @@ const injectSentry = async (origEntryProperty: EntryProperty, isServer: boolean) Object.keys(newEntryProperty).forEach(key => { if (key === 'pages/_document' || key.includes('pages/api')) { // for some reason, because we're now in a function, we have to cast again - _injectFile(newEntryProperty as EntryPropertyObject, key, './sentry.server.config.js'); + _injectFile(newEntryProperty as EntryPropertyObject, key, sentryServerConfig); } }); } // On the client, it's sufficient to inject it into the `main` JS code, which is included in every browser page. else { - _injectFile(newEntryProperty, 'main', './sentry.client.config.js'); + _injectFile(newEntryProperty, 'main', sentryClientConfig); } // TODO: hack made necessary because the async-ness of this function turns our object back into a promise, meaning the // internal `next` code which should do this doesn't From 5857c42564f30754138f34b021e84b913dcfada8 Mon Sep 17 00:00:00 2001 From: Daniel Griesser Date: Fri, 30 Apr 2021 13:55:34 +0200 Subject: [PATCH 6/6] meta: Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 893abce0f7e1..cced2e617e67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 6.3.5 + +- [nextjs] fix: Add tslib dependecy; change inject order (#3487) + ## 6.3.4 - [nextjs] fix: API routes logging (#3479)