11import  type  {  Nuxt  }  from  '@nuxt/schema' ; 
22import  {  type  SentryRollupPluginOptions ,  sentryRollupPlugin  }  from  '@sentry/rollup-plugin' ; 
3+ import  {  consoleSandbox  }  from  '@sentry/utils' ; 
34import  {  type  SentryVitePluginOptions ,  sentryVitePlugin  }  from  '@sentry/vite-plugin' ; 
45import  type  {  NitroConfig  }  from  'nitropack' ; 
6+ import  type  {  OutputOptions  }  from  'rollup' ; 
57import  type  {  SentryNuxtModuleOptions  }  from  '../common/types' ; 
68
9+ /** 
10+  * Whether the user enabled (true, 'hidden', 'inline') or disabled (false) source maps 
11+  */ 
12+ export  type  UserSourceMapSetting  =  'enabled'  |  'disabled'  |  'unset'  |  undefined ; 
13+ 
714/** 
815 *  Setup source maps for Sentry inside the Nuxt module during build time (in Vite for Nuxt and Rollup for Nitro). 
916 */ 
1017export  function  setupSourceMaps ( moduleOptions : SentryNuxtModuleOptions ,  nuxt : Nuxt ) : void { 
1118  const  sourceMapsUploadOptions  =  moduleOptions . sourceMapsUploadOptions  ||  { } ; 
1219  const  sourceMapsEnabled  =  sourceMapsUploadOptions . enabled  ??  true ; 
1320
14-   nuxt . hook ( 'vite:extendConfig ' ,  async   ( viteInlineConfig ,   _env )  =>  { 
15-     if  ( sourceMapsEnabled  &&  viteInlineConfig . mode   !==   'development' )  { 
16-       // Add Sentry plugin 
17-        viteInlineConfig . plugins   =   viteInlineConfig . plugins   ||   [ ] ; 
18-        viteInlineConfig . plugins . push ( sentryVitePlugin ( getPluginOptions ( moduleOptions ) ) ) ; 
21+   nuxt . hook ( 'modules:done ' ,  ( )  =>  { 
22+     if  ( sourceMapsEnabled  &&  ! nuxt . options . dev )  { 
23+       changeNuxtSourceMapSettings ( nuxt ,   moduleOptions ) ; 
24+     } 
25+   } ) ; 
1926
20-        // Enable source maps 
21-        viteInlineConfig . build   =   viteInlineConfig . build   ||   { } ; 
22-       viteInlineConfig . build . sourcemap   =   true ; 
27+   nuxt . hook ( 'vite:extendConfig' ,   async   ( viteConfig ,   _env )   =>   { 
28+     if   ( sourceMapsEnabled   &&   viteConfig . mode   !==   'development' )   { 
29+       const   previousUserSourceMapSetting   =   changeViteSourceMapSettings ( viteConfig ,   moduleOptions ) ; 
2330
24-       logDebugInfo ( moduleOptions ,  viteInlineConfig . build ?. sourcemap ) ; 
31+       //  Add Sentry plugin 
32+       viteConfig . plugins  =  viteConfig . plugins  ||  [ ] ; 
33+       viteConfig . plugins . push ( 
34+         sentryVitePlugin ( getPluginOptions ( moduleOptions ,  previousUserSourceMapSetting  ===  'unset' ) ) , 
35+       ) ; 
2536    } 
2637  } ) ; 
2738
@@ -38,15 +49,12 @@ export function setupSourceMaps(moduleOptions: SentryNuxtModuleOptions, nuxt: Nu
3849        nitroConfig . rollupConfig . plugins  =  [ nitroConfig . rollupConfig . plugins ] ; 
3950      } 
4051
41-       // Add Sentry plugin 
42-       nitroConfig . rollupConfig . plugins . push ( sentryRollupPlugin ( getPluginOptions ( moduleOptions ) ) ) ; 
43- 
44-       // Enable source maps 
45-       nitroConfig . rollupConfig . output  =  nitroConfig ?. rollupConfig ?. output  ||  { } ; 
46-       nitroConfig . rollupConfig . output . sourcemap  =  true ; 
47-       nitroConfig . rollupConfig . output . sourcemapExcludeSources  =  false ;  // Adding "sourcesContent" to the source map (Nitro sets this eto `true`) 
52+       const  previousUserSourceMapSetting  =  changeRollupSourceMapSettings ( nitroConfig ,  moduleOptions ) ; 
4853
49-       logDebugInfo ( moduleOptions ,  nitroConfig . rollupConfig . output ?. sourcemap ) ; 
54+       // Add Sentry plugin 
55+       nitroConfig . rollupConfig . plugins . push ( 
56+         sentryRollupPlugin ( getPluginOptions ( moduleOptions ,  previousUserSourceMapSetting  ===  'unset' ) ) , 
57+       ) ; 
5058    } 
5159  } ) ; 
5260} 
@@ -65,9 +73,19 @@ function normalizePath(path: string): string {
6573 */ 
6674export  function  getPluginOptions ( 
6775  moduleOptions : SentryNuxtModuleOptions , 
76+   deleteFilesAfterUpload : boolean , 
6877) : SentryVitePluginOptions  |  SentryRollupPluginOptions  { 
6978  const  sourceMapsUploadOptions  =  moduleOptions . sourceMapsUploadOptions  ||  { } ; 
7079
80+   if  ( typeof  sourceMapsUploadOptions . sourcemaps ?. filesToDeleteAfterUpload  ===  'undefined'  &&  deleteFilesAfterUpload )  { 
81+     consoleSandbox ( ( )  =>  { 
82+       // eslint-disable-next-line no-console 
83+       console . log ( 
84+         '[Sentry] Setting `sentry.sourceMapsUploadOptions.sourcemaps.filesToDeleteAfterUpload: [".*/**/*.map"]` to delete generated source maps after they were uploaded to Sentry.' , 
85+       ) ; 
86+     } ) ; 
87+   } 
88+ 
7189  return  { 
7290    org : sourceMapsUploadOptions . org  ??  process . env . SENTRY_ORG , 
7391    project : sourceMapsUploadOptions . project  ??  process . env . SENTRY_PROJECT , 
@@ -83,29 +101,192 @@ export function getPluginOptions(
83101
84102    sourcemaps : { 
85103      // The server/client files are in different places depending on the nitro preset (e.g. '.output/server' or '.netlify/functions-internal/server') 
86-       // We cannot determine automatically how the build folder looks like (depends on the preset), so we have to accept that sourcemaps  are uploaded multiple times (with the vitePlugin for Nuxt and the rollupPlugin for Nitro). 
104+       // We cannot determine automatically how the build folder looks like (depends on the preset), so we have to accept that source maps  are uploaded multiple times (with the vitePlugin for Nuxt and the rollupPlugin for Nitro). 
87105      // If we could know where the server/client assets are located, we could do something like this (based on the Nitro preset): isNitro ? ['./.output/server/**/*'] : ['./.output/public/**/*'], 
88106      assets : sourceMapsUploadOptions . sourcemaps ?. assets  ??  undefined , 
89107      ignore : sourceMapsUploadOptions . sourcemaps ?. ignore  ??  undefined , 
90-       filesToDeleteAfterUpload : sourceMapsUploadOptions . sourcemaps ?. filesToDeleteAfterUpload  ??  undefined , 
108+       filesToDeleteAfterUpload : sourceMapsUploadOptions . sourcemaps ?. filesToDeleteAfterUpload 
109+         ? sourceMapsUploadOptions . sourcemaps ?. filesToDeleteAfterUpload 
110+         : deleteFilesAfterUpload 
111+           ? [ '.*/**/*.map' ] 
112+           : undefined , 
91113      rewriteSources : ( source : string )  =>  normalizePath ( source ) , 
92114      ...moduleOptions ?. unstable_sentryBundlerPluginOptions ?. sourcemaps , 
93115    } , 
94116  } ; 
95117} 
96118
97- function  logDebugInfo ( moduleOptions : SentryNuxtModuleOptions ,  sourceMapsPreviouslyEnabled : boolean ) : void { 
98-   if  ( moduleOptions . debug  &&  ! sourceMapsPreviouslyEnabled )  { 
119+ /*  There are 3 ways to set up source maps (https://github.com/getsentry/sentry-javascript/issues/13993) 
120+     1. User explicitly disabled source maps 
121+       - keep this setting (emit a warning that errors won't be unminified in Sentry) 
122+       - We will not upload anything 
123+     2. users enabled source map generation (true, hidden, inline). 
124+       - keep this setting (don't do anything - like deletion - besides uploading) 
125+     3. users did not set source maps generation 
126+       - we enable 'hidden' source maps generation 
127+       - configure `filesToDeleteAfterUpload` to delete all .map files (we emit a log about this) 
128+ 
129+     Nuxt has 3 places to set source maps: vite options, rollup options, nuxt itself 
130+     Ideally, all 3 are enabled to get all source maps. 
131+  */ 
132+ 
133+ /** only exported for testing */ 
134+ export  function  changeNuxtSourceMapSettings ( 
135+   nuxt : Nuxt , 
136+   sentryModuleOptions : SentryNuxtModuleOptions , 
137+ ) : {  client : UserSourceMapSetting ;  server : UserSourceMapSetting  }  { 
138+   nuxt . options  =  nuxt . options  ||  { } ; 
139+   nuxt . options . sourcemap  =  nuxt . options . sourcemap  ??  {  server : undefined ,  client : undefined  } ; 
140+ 
141+   let  previousUserSourceMapSetting : {  client : UserSourceMapSetting ;  server : UserSourceMapSetting  }  =  { 
142+     client : undefined , 
143+     server : undefined , 
144+   } ; 
145+ 
146+   const  nuxtSourceMap  =  nuxt . options . sourcemap ; 
147+ 
148+   if  ( typeof  nuxtSourceMap  ===  'string'  ||  typeof  nuxtSourceMap  ===  'boolean'  ||  typeof  nuxtSourceMap  ===  'undefined' )  { 
149+     switch  ( nuxtSourceMap )  { 
150+       case  false :
151+         warnExplicitlyDisabledSourceMap ( 'sourcemap' ) ; 
152+         previousUserSourceMapSetting  =  {  client : 'disabled' ,  server : 'disabled'  } ; 
153+         break ; 
154+ 
155+       case  'hidden' :
156+       case  true :
157+         logKeepSourceMapSetting ( sentryModuleOptions ,  'sourcemap' ,  ( nuxtSourceMap  as  true ) . toString ( ) ) ; 
158+         previousUserSourceMapSetting  =  {  client : 'enabled' ,  server : 'enabled'  } ; 
159+         break ; 
160+       case  undefined :
161+         nuxt . options . sourcemap  =  {  server : 'hidden' ,  client : 'hidden'  } ; 
162+         logSentryEnablesSourceMap ( 'sourcemap.client' ,  'hidden' ) ; 
163+         logSentryEnablesSourceMap ( 'sourcemap.server' ,  'hidden' ) ; 
164+         previousUserSourceMapSetting  =  {  client : 'unset' ,  server : 'unset'  } ; 
165+         break ; 
166+     } 
167+   }  else  { 
168+     if  ( nuxtSourceMap . client  ===  false )  { 
169+       warnExplicitlyDisabledSourceMap ( 'sourcemap.client' ) ; 
170+       previousUserSourceMapSetting . client  =  'disabled' ; 
171+     }  else  if  ( [ 'hidden' ,  true ] . includes ( nuxtSourceMap . client ) )  { 
172+       logKeepSourceMapSetting ( sentryModuleOptions ,  'sourcemap.client' ,  nuxtSourceMap . client . toString ( ) ) ; 
173+       previousUserSourceMapSetting . client  =  'enabled' ; 
174+     }  else  { 
175+       nuxt . options . sourcemap . client  =  'hidden' ; 
176+       logSentryEnablesSourceMap ( 'sourcemap.client' ,  'hidden' ) ; 
177+       previousUserSourceMapSetting . client  =  'unset' ; 
178+     } 
179+ 
180+     if  ( nuxtSourceMap . server  ===  false )  { 
181+       warnExplicitlyDisabledSourceMap ( 'sourcemap.server' ) ; 
182+       previousUserSourceMapSetting . server  =  'disabled' ; 
183+     }  else  if  ( [ 'hidden' ,  true ] . includes ( nuxtSourceMap . server ) )  { 
184+       logKeepSourceMapSetting ( sentryModuleOptions ,  'sourcemap.server' ,  nuxtSourceMap . server . toString ( ) ) ; 
185+       previousUserSourceMapSetting . server  =  'enabled' ; 
186+     }  else  { 
187+       nuxt . options . sourcemap . server  =  'hidden' ; 
188+       logSentryEnablesSourceMap ( 'sourcemap.server' ,  'hidden' ) ; 
189+       previousUserSourceMapSetting . server  =  'unset' ; 
190+     } 
191+   } 
192+ 
193+   return  previousUserSourceMapSetting ; 
194+ } 
195+ 
196+ /** only exported for testing */ 
197+ export  function  changeViteSourceMapSettings ( 
198+   viteConfig : {  build ?: {  sourcemap ?: boolean  |  'inline'  |  'hidden'  }  } , 
199+   sentryModuleOptions : SentryNuxtModuleOptions , 
200+ ) : UserSourceMapSetting  { 
201+   viteConfig . build  =  viteConfig . build  ||  { } ; 
202+   const  viteSourceMap  =  viteConfig . build . sourcemap ; 
203+ 
204+   let  previousUserSourceMapSetting : UserSourceMapSetting ; 
205+ 
206+   if  ( viteSourceMap  ===  false )  { 
207+     warnExplicitlyDisabledSourceMap ( 'vite.build.sourcemap' ) ; 
208+     previousUserSourceMapSetting  =  'disabled' ; 
209+   }  else  if  ( viteSourceMap  &&  [ 'hidden' ,  'inline' ,  true ] . includes ( viteSourceMap ) )  { 
210+     logKeepSourceMapSetting ( sentryModuleOptions ,  'vite.build.sourcemap' ,  viteSourceMap . toString ( ) ) ; 
211+     previousUserSourceMapSetting  =  'enabled' ; 
212+   }  else  { 
213+     viteConfig . build . sourcemap  =  'hidden' ; 
214+     logSentryEnablesSourceMap ( 'vite.build.sourcemap' ,  'hidden' ) ; 
215+     previousUserSourceMapSetting  =  'unset' ; 
216+   } 
217+ 
218+   return  previousUserSourceMapSetting ; 
219+ } 
220+ 
221+ /** only exported for testing */ 
222+ export  function  changeRollupSourceMapSettings ( 
223+   nitroConfig : { 
224+     rollupConfig ?: { 
225+       output ?: { 
226+         sourcemap ?: OutputOptions [ 'sourcemap' ] ; 
227+         sourcemapExcludeSources ?: OutputOptions [ 'sourcemapExcludeSources' ] ; 
228+       } ; 
229+     } ; 
230+   } , 
231+   sentryModuleOptions : SentryNuxtModuleOptions , 
232+ ) : UserSourceMapSetting  { 
233+   nitroConfig . rollupConfig  =  nitroConfig . rollupConfig  ||  { } ; 
234+   nitroConfig . rollupConfig . output  =  nitroConfig . rollupConfig . output  ||  {  sourcemap : undefined  } ; 
235+ 
236+   let  previousUserSourceMapSetting : UserSourceMapSetting ; 
237+ 
238+   const  nitroSourceMap  =  nitroConfig . rollupConfig . output . sourcemap ; 
239+ 
240+   if  ( nitroSourceMap  ===  false )  { 
241+     warnExplicitlyDisabledSourceMap ( 'nitro.rollupConfig.output.sourcemap' ) ; 
242+     previousUserSourceMapSetting  =  'disabled' ; 
243+   }  else  if  ( nitroSourceMap  &&  [ 'hidden' ,  'inline' ,  true ] . includes ( nitroSourceMap ) )  { 
244+     logKeepSourceMapSetting ( sentryModuleOptions ,  'nitro.rollupConfig.output.sourcemap' ,  nitroSourceMap . toString ( ) ) ; 
245+     previousUserSourceMapSetting  =  'enabled' ; 
246+   }  else  { 
247+     nitroConfig . rollupConfig . output . sourcemap  =  'hidden' ; 
248+     logSentryEnablesSourceMap ( 'nitro.rollupConfig.output.sourcemap' ,  'hidden' ) ; 
249+     previousUserSourceMapSetting  =  'unset' ; 
250+   } 
251+ 
252+   nitroConfig . rollupConfig . output . sourcemapExcludeSources  =  false ; 
253+   consoleSandbox ( ( )  =>  { 
99254    // eslint-disable-next-line no-console 
100-     console . log ( '[Sentry]: Enabled source maps generation in the Vite build options.' ) ; 
255+     console . log ( 
256+       '[Sentry] Disabled source map setting in the Nuxt config: `nitro.rollupConfig.output.sourcemapExcludeSources`. Source maps will include the actual code to be able to un-minify code snippets in Sentry.' , 
257+     ) ; 
258+   } ) ; 
101259
102-     const  sourceMapsUploadOptions  =  moduleOptions . sourceMapsUploadOptions  ||  { } ; 
260+   return  previousUserSourceMapSetting ; 
261+ } 
103262
104-     if  ( ! sourceMapsUploadOptions . sourcemaps ?. filesToDeleteAfterUpload )  { 
263+ function  logKeepSourceMapSetting ( 
264+   sentryNuxtModuleOptions : SentryNuxtModuleOptions , 
265+   settingKey : string , 
266+   settingValue : string , 
267+ ) : void { 
268+   if  ( sentryNuxtModuleOptions . debug )  { 
269+     consoleSandbox ( ( )  =>  { 
105270      // eslint-disable-next-line no-console 
106-       console . warn ( 
107-         ' [Sentry] We recommend setting the `sourceMapsUploadOptions.sourcemaps.filesToDeleteAfterUpload` option to clean up  source maps after uploading. Otherwise, source maps might be deployed to production, depending on your configuration' , 
271+       console . log ( 
272+         ` [Sentry] We discovered \` ${ settingKey } \` is set to \` ${ settingValue } \`. Sentry will keep this  source map setting. This will un-minify the code snippet on the Sentry Issue page.` , 
108273      ) ; 
109-     } 
274+     } ) ; 
110275  } 
111276} 
277+ 
278+ function  warnExplicitlyDisabledSourceMap ( settingKey : string ) : void { 
279+   consoleSandbox ( ( )  =>  { 
280+     //  eslint-disable-next-line no-console 
281+     console . warn ( 
282+       `[Sentry] Parts of source map generation are currently disabled in your Nuxt configuration (\`${ settingKey } ${ settingKey }  , 
283+     ) ; 
284+   } ) ; 
285+ } 
286+ 
287+ function  logSentryEnablesSourceMap ( settingKey : string ,  settingValue : string ) : void { 
288+   consoleSandbox ( ( )  =>  { 
289+     //  eslint-disable-next-line no-console 
290+     console . log ( `[Sentry] Enabled source map generation in the build options with \`${ settingKey } ${ settingValue }  ) ; 
291+   } ) ; 
292+ } 
0 commit comments