@@ -23,6 +23,7 @@ import type {
2323 UserSentryOptions ,
2424 WebpackConfigFunction ,
2525 WebpackConfigObject ,
26+ WebpackConfigObjectWithModuleRules ,
2627 WebpackEntryProperty ,
2728 WebpackModuleRule ,
2829 WebpackPluginInstance ,
@@ -67,35 +68,21 @@ export function constructWebpackConfigFunction(
6768 buildContext : BuildContext ,
6869 ) : WebpackConfigObject {
6970 const { isServer, dev : isDev , dir : projectDir } = buildContext ;
70- let newConfig = { ...incomingConfig } ;
71+ let rawNewConfig = { ...incomingConfig } ;
7172
7273 // if user has custom webpack config (which always takes the form of a function), run it so we have actual values to
7374 // work with
7475 if ( 'webpack' in userNextConfig && typeof userNextConfig . webpack === 'function' ) {
75- newConfig = userNextConfig . webpack ( newConfig , buildContext ) ;
76+ rawNewConfig = userNextConfig . webpack ( rawNewConfig , buildContext ) ;
7677 }
7778
79+ // This mutates `rawNewConfig` in place, but also returns it in order to switch its type to one in which
80+ // `newConfig.module.rules` is required, so we don't have to keep asserting its existence
81+ const newConfig = setUpModuleRules ( rawNewConfig ) ;
82+
7883 if ( isServer ) {
79- newConfig . module = {
80- ...newConfig . module ,
81- rules : [
82- ...( newConfig . module ?. rules || [ ] ) ,
83- {
84- test : / s e n t r y \. s e r v e r \. c o n f i g \. ( j s x ? | t s x ? ) / ,
85- use : [
86- {
87- // Support non-default output directories by making the output path (easy to get here at build-time)
88- // available to the server SDK's default `RewriteFrames` instance (which needs it at runtime), by
89- // injecting code to attach it to `global`.
90- loader : path . resolve ( __dirname , 'loaders/prefixLoader.js' ) ,
91- options : {
92- distDir : userNextConfig . distDir || '.next' ,
93- } ,
94- } ,
95- ] ,
96- } ,
97- ] ,
98- } ;
84+ // This loader will inject code setting global values for use by `RewriteFrames`
85+ addRewriteFramesLoader ( newConfig , 'server' , userNextConfig ) ;
9986
10087 if ( userSentryOptions . autoInstrumentServerFunctions !== false ) {
10188 const pagesDir = newConfig . resolve ?. alias ?. [ 'private-next-pages' ] as string ;
@@ -628,3 +615,59 @@ function handleSourcemapHidingOptionWarning(userSentryOptions: UserSentryOptions
628615 // );
629616 // }
630617}
618+
619+ /**
620+ * Ensure that `newConfig.module.rules` exists. Modifies the given config in place but also returns it in order to
621+ * change its type.
622+ *
623+ * @param newConfig A webpack config object which may or may not contain `module` and `module.rules`
624+ * @returns The same object, with an empty `module.rules` array added if necessary
625+ */
626+ function setUpModuleRules ( newConfig : WebpackConfigObject ) : WebpackConfigObjectWithModuleRules {
627+ newConfig . module = {
628+ ...newConfig . module ,
629+ rules : [ ...( newConfig . module ?. rules || [ ] ) ] ,
630+ } ;
631+ // Surprising that we have to assert the type here, since we've demonstrably guaranteed the existence of
632+ // `newConfig.module.rules` just above, but ¯\_(ツ)_/¯
633+ return newConfig as WebpackConfigObjectWithModuleRules ;
634+ }
635+
636+ /**
637+ * Support the `distDir` option by making its value (easy to get here at build-time) available to the server SDK's
638+ * default `RewriteFrames` instance (which needs it at runtime), by injecting code to attach it to `global`.
639+ *
640+ * @param newConfig The webpack config object being constructed
641+ * @param target Either 'server' or 'client'
642+ * @param userNextConfig The user's nextjs config options
643+ */
644+ function addRewriteFramesLoader (
645+ newConfig : WebpackConfigObjectWithModuleRules ,
646+ target : 'server' | 'client' ,
647+ userNextConfig : NextConfigObject ,
648+ ) : void {
649+ const replacements = {
650+ server : [
651+ [
652+ '__DIST_DIR__' ,
653+ // Make sure that if we have a windows path, the backslashes are interpreted as such (rather than as escape
654+ // characters)
655+ userNextConfig . distDir ?. replace ( / \\ / g, '\\\\' ) || '.next' ,
656+ ] ,
657+ ] ,
658+ } ;
659+
660+ newConfig . module . rules . push ( {
661+ test : new RegExp ( `sentry\\.${ target } \\.config\\.(jsx?|tsx?)` ) ,
662+ use : [
663+ {
664+ loader : path . resolve ( __dirname , 'loaders/prefixLoader.js' ) ,
665+ options : {
666+ templatePrefix : `${ target } RewriteFrames` ,
667+ // This weird cast will go away as soon as we add the client half of this function in
668+ replacements : replacements [ target as 'server' ] ,
669+ } ,
670+ } ,
671+ ] ,
672+ } ) ;
673+ }
0 commit comments