@@ -2,6 +2,7 @@ import { getSentryRelease } from '@sentry/node';
2
2
import { logger } from '@sentry/utils' ;
3
3
import defaultWebpackPlugin , { SentryCliPluginOptions } from '@sentry/webpack-plugin' ;
4
4
import * as SentryWebpackPlugin from '@sentry/webpack-plugin' ;
5
+ import * as path from 'path' ;
5
6
6
7
// eslint-disable-next-line @typescript-eslint/no-explicit-any
7
8
type PlainObject < T = any > = { [ key : string ] : T } ;
@@ -14,7 +15,9 @@ type PlainObject<T = any> = { [key: string]: T };
14
15
type WebpackExport = ( config : WebpackConfig , options : WebpackOptions ) => WebpackConfig ;
15
16
16
17
// The two arguments passed to the exported `webpack` function, as well as the thing it returns
17
- type WebpackConfig = { devtool : string ; plugins : PlainObject [ ] ; entry : EntryProperty } ;
18
+ // TODO use real webpack types?
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ type WebpackConfig = { devtool : string ; plugins : PlainObject [ ] ; entry : EntryProperty ; module : { rules : any [ ] } } ;
18
21
type WebpackOptions = { dev : boolean ; isServer : boolean ; buildId : string } ;
19
22
20
23
// For our purposes, the value for `entry` is either an object, or a function which returns such an object
@@ -41,8 +44,9 @@ const _injectFile = (entryProperty: EntryPropertyObject, injectionPoint: string,
41
44
return ;
42
45
}
43
46
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
47
+ // We need to inject the user-provided config file after the frontend code so that it has access to the runtime config
48
+ // (which won't have loaded yet otherwise). See: https://github.com/getsentry/sentry-javascript/issues/3485. On the
49
+ // server side, we inject beforehand so that we can catch any errors in the code that follows.
46
50
const isClient = injectee === sentryClientConfig ;
47
51
48
52
if ( typeof injectedInto === 'string' ) {
@@ -68,8 +72,6 @@ const _injectFile = (entryProperty: EntryPropertyObject, injectionPoint: string,
68
72
} ;
69
73
70
74
const injectSentry = async ( origEntryProperty : EntryProperty , isServer : boolean ) : Promise < EntryProperty > => {
71
- // Out of the box, nextjs uses the `() => Promise<EntryPropertyObject>)` flavor of EntryProperty, where the returned
72
- // object has string arrays for values.
73
75
// The `entry` entry in a webpack config can be a string, array of strings, object, or function. By default, nextjs
74
76
// sets it to an async function which returns the promise of an object of string arrays. Because we don't know whether
75
77
// someone else has come along before us and changed that, we need to check a few things along the way. The one thing
@@ -151,6 +153,42 @@ export function withSentryConfig(
151
153
newConfig . devtool = 'source-map' ;
152
154
}
153
155
156
+ // Wherever the webpack process ultimately runs, it's not somewhere where modules resolve successfully, so get the
157
+ // absolute paths here, where resolution does work, and pass those into the config instead of the module names
158
+ const sdkResolvedMain = require . resolve ( '@sentry/nextjs' ) ;
159
+ const loaderPath = path . join ( path . dirname ( sdkResolvedMain ) , 'utils/api-wrapping-loader.js' ) ;
160
+ const babelLoaderResolvedMain = require . resolve ( 'babel-loader' ) ;
161
+ const babelPluginResolvedMain = require . resolve ( '@babel/plugin-transform-modules-commonjs' ) ;
162
+
163
+ newConfig . module = {
164
+ ...newConfig . module ,
165
+ rules : [
166
+ ...newConfig . module . rules ,
167
+ {
168
+ test : / p a g e s \/ a p i \/ .* / ,
169
+ use : [
170
+ {
171
+ // `sdkResolvedMain` is the path to `dist/index.server.js`
172
+ loader : loaderPath ,
173
+ options : {
174
+ // pass the path into the loader so it can be used there as well (it's another place where modules don't
175
+ // resolve well)
176
+ sdkPath : sdkResolvedMain ,
177
+ } ,
178
+ } ,
179
+ {
180
+ loader : babelLoaderResolvedMain ,
181
+ options : {
182
+ // this is here to turn any `import`s into `requires`, so that our loader can load the string as a
183
+ // module (loaders apply top to bottom, so this happens before ours)
184
+ plugins : [ babelPluginResolvedMain ] ,
185
+ } ,
186
+ } ,
187
+ ] ,
188
+ } ,
189
+ ] ,
190
+ } ;
191
+
154
192
// Inject user config files (`sentry.client.confg.js` and `sentry.server.config.js`), which is where `Sentry.init()`
155
193
// is called. By adding them here, we ensure that they're bundled by webpack as part of both server code and client code.
156
194
newConfig . entry = ( injectSentry ( newConfig . entry , options . isServer ) as unknown ) as EntryProperty ;
0 commit comments