Skip to content

Commit

Permalink
fix: Add tslib dependecy for next.js + fixes (#3487)
Browse files Browse the repository at this point in the history
* fix: Add tslib dependecy

* fix: Add safeguard

* fix: Inject client after entry in frontend

* Update packages/nextjs/src/utils/config.ts

Co-authored-by: iker barriocanal <[email protected]>

* ref: CR

* meta: Changelog

Co-authored-by: iker barriocanal <[email protected]>
  • Loading branch information
HazAT and iker-barriocanal authored Apr 30, 2021
1 parent 6874655 commit d5b335d
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 24 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion packages/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
60 changes: 37 additions & 23 deletions packages/nextjs/src/utils/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,36 +26,50 @@ type EntryProperty = (() => Promise<EntryPropertyObject>) | EntryPropertyObject;
type EntryPropertyObject = PlainObject<string | Array<string> | EntryPointObject>;
type EntryPointObject = { import: string | Array<string> };

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
let injectedInto = entryProperty[injectionPoint];
// 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],
};

// Sometimes especially for older next.js versions it happens we don't have an entry point
if (!injectedInto) {
// eslint-disable-next-line no-console
console.error(`[Sentry] Can't inject ${injectee}, no entrypoint is defined.`);
return;
}

// 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 === sentryClientConfig;

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];
} 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,
};
}

entryProperty[injectionPoint] = injectedInto;
};

const injectSentry = async (origEntryProperty: EntryProperty, isServer: boolean): Promise<EntryProperty> => {
// Out of the box, nextjs uses the `() => Promise<EntryPropertyObject>)` 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
Expand All @@ -72,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
Expand Down

0 comments on commit d5b335d

Please sign in to comment.