@@ -26,36 +26,50 @@ type EntryProperty = (() => Promise<EntryPropertyObject>) | EntryPropertyObject;
26
26
type EntryPropertyObject = PlainObject < string | Array < string > | EntryPointObject > ;
27
27
type EntryPointObject = { import : string | Array < string > } ;
28
28
29
+ const sentryClientConfig = './sentry.client.config.js' ;
30
+ const sentryServerConfig = './sentry.server.config.js' ;
31
+
29
32
/** Add a file (`injectee`) to a given element (`injectionPoint`) of the `entry` property */
30
33
const _injectFile = ( entryProperty : EntryPropertyObject , injectionPoint : string , injectee : string ) : void => {
31
34
// can be a string, array of strings, or object whose `import` property is one of those two
32
35
let injectedInto = entryProperty [ injectionPoint ] ;
33
- // whatever the format, add in the sentry file
34
- injectedInto =
35
- typeof injectedInto === 'string'
36
- ? // string case
37
- [ injectee , injectedInto ]
38
- : // not a string, must be an array or object
39
- Array . isArray ( injectedInto )
40
- ? // array case
41
- [ injectee , ...injectedInto ]
42
- : // object case
43
- {
44
- ...injectedInto ,
45
- import :
46
- typeof injectedInto . import === 'string'
47
- ? // string case for inner property
48
- [ injectee , injectedInto . import ]
49
- : // array case for inner property
50
- [ injectee , ...injectedInto . import ] ,
51
- } ;
36
+
37
+ // Sometimes especially for older next.js versions it happens we don't have an entry point
38
+ if ( ! injectedInto ) {
39
+ // eslint-disable-next-line no-console
40
+ console . error ( `[Sentry] Can't inject ${ injectee } , no entrypoint is defined.` ) ;
41
+ return ;
42
+ }
43
+
44
+ // In case we inject our client config, we need to add it after the frontend code
45
+ // otherwise the runtime config isn't loaded. See: https://github.com/getsentry/sentry-javascript/issues/3485
46
+ const isClient = injectee === sentryClientConfig ;
47
+
48
+ if ( typeof injectedInto === 'string' ) {
49
+ injectedInto = isClient ? [ injectedInto , injectee ] : [ injectee , injectedInto ] ;
50
+ } else if ( Array . isArray ( injectedInto ) ) {
51
+ injectedInto = isClient ? [ ...injectedInto , injectee ] : [ injectee , ...injectedInto ] ;
52
+ } else {
53
+ let importVal : string | string [ ] | EntryPointObject ;
54
+ if ( typeof injectedInto . import === 'string' ) {
55
+ importVal = isClient ? [ injectedInto . import , injectee ] : [ injectee , injectedInto . import ] ;
56
+ } else {
57
+ // If it's not a string, the inner value is an array
58
+ importVal = isClient ? [ ...injectedInto . import , injectee ] : [ injectee , ...injectedInto . import ] ;
59
+ }
60
+
61
+ injectedInto = {
62
+ ...injectedInto ,
63
+ import : importVal ,
64
+ } ;
65
+ }
66
+
52
67
entryProperty [ injectionPoint ] = injectedInto ;
53
68
} ;
54
69
55
70
const injectSentry = async ( origEntryProperty : EntryProperty , isServer : boolean ) : Promise < EntryProperty > => {
56
71
// Out of the box, nextjs uses the `() => Promise<EntryPropertyObject>)` flavor of EntryProperty, where the returned
57
- // object has string arrays for values. But because we don't know whether someone else has come along before us and
58
- // changed that, we need to check a few things along the way.
72
+ // object has string arrays for values.
59
73
// The `entry` entry in a webpack config can be a string, array of strings, object, or function. By default, nextjs
60
74
// sets it to an async function which returns the promise of an object of string arrays. Because we don't know whether
61
75
// someone else has come along before us and changed that, we need to check a few things along the way. The one thing
@@ -72,13 +86,13 @@ const injectSentry = async (origEntryProperty: EntryProperty, isServer: boolean)
72
86
Object . keys ( newEntryProperty ) . forEach ( key => {
73
87
if ( key === 'pages/_document' || key . includes ( 'pages/api' ) ) {
74
88
// for some reason, because we're now in a function, we have to cast again
75
- _injectFile ( newEntryProperty as EntryPropertyObject , key , './sentry.server.config.js' ) ;
89
+ _injectFile ( newEntryProperty as EntryPropertyObject , key , sentryServerConfig ) ;
76
90
}
77
91
} ) ;
78
92
}
79
93
// On the client, it's sufficient to inject it into the `main` JS code, which is included in every browser page.
80
94
else {
81
- _injectFile ( newEntryProperty , 'main' , './sentry.client.config.js' ) ;
95
+ _injectFile ( newEntryProperty , 'main' , sentryClientConfig ) ;
82
96
}
83
97
// TODO: hack made necessary because the async-ness of this function turns our object back into a promise, meaning the
84
98
// internal `next` code which should do this doesn't
0 commit comments