@@ -2,38 +2,264 @@ import type { Options as SentryBuildPluginOptions } from '@sentry/bundler-plugin
2
2
import * as path from 'path' ;
3
3
import type { SentryBuildOptions } from './types' ;
4
4
5
+ const LOGGER_PREFIXES = {
6
+ 'webpack-nodejs' : '[@sentry/nextjs - Node.js]' ,
7
+ 'webpack-edge' : '[@sentry/nextjs - Edge]' ,
8
+ 'webpack-client' : '[@sentry/nextjs - Client]' ,
9
+ 'after-production-compile-webpack' : '[@sentry/nextjs - After Production Compile (Webpack)]' ,
10
+ 'after-production-compile-turbopack' : '[@sentry/nextjs - After Production Compile (Turbopack)]' ,
11
+ } as const ;
12
+
13
+ // File patterns for source map operations
14
+ // We use both glob patterns and directory paths for the sourcemap upload and deletion
15
+ // -> Direct CLI invocation handles file paths better than glob patterns
16
+ // -> Webpack/Bundler needs glob patterns as this is the format that is used by the plugin
17
+ const FILE_PATTERNS = {
18
+ SERVER : {
19
+ GLOB : 'server/**' ,
20
+ PATH : 'server' ,
21
+ } ,
22
+ SERVERLESS : 'serverless/**' ,
23
+ STATIC_CHUNKS : {
24
+ GLOB : 'static/chunks/**' ,
25
+ PATH : 'static/chunks' ,
26
+ } ,
27
+ STATIC_CHUNKS_PAGES : {
28
+ GLOB : 'static/chunks/pages/**' ,
29
+ PATH : 'static/chunks/pages' ,
30
+ } ,
31
+ STATIC_CHUNKS_APP : {
32
+ GLOB : 'static/chunks/app/**' ,
33
+ PATH : 'static/chunks/app' ,
34
+ } ,
35
+ MAIN_CHUNKS : 'static/chunks/main-*' ,
36
+ FRAMEWORK_CHUNKS : 'static/chunks/framework-*' ,
37
+ FRAMEWORK_CHUNKS_DOT : 'static/chunks/framework.*' ,
38
+ POLYFILLS_CHUNKS : 'static/chunks/polyfills-*' ,
39
+ WEBPACK_CHUNKS : 'static/chunks/webpack-*' ,
40
+ } as const ;
41
+
42
+ // Source map file extensions to delete
43
+ const SOURCEMAP_EXTENSIONS = [ '*.js.map' , '*.mjs.map' , '*.cjs.map' ] as const ;
44
+
45
+ type BuildTool = keyof typeof LOGGER_PREFIXES ;
46
+
47
+ /**
48
+ * Normalizes Windows paths to POSIX format for glob patterns
49
+ */
50
+ export function normalizePathForGlob ( distPath : string ) : string {
51
+ return distPath . replace ( / \\ / g, '/' ) ;
52
+ }
53
+
54
+ /**
55
+ * These functions are used to get the correct pattern for the sourcemap upload based on the build tool and the usage context
56
+ * -> Direct CLI invocation handles file paths better than glob patterns
57
+ */
58
+ function getServerPattern ( { useDirectoryPath = false } : { useDirectoryPath ?: boolean } ) : string {
59
+ return useDirectoryPath ? FILE_PATTERNS . SERVER . PATH : FILE_PATTERNS . SERVER . GLOB ;
60
+ }
61
+
62
+ function getStaticChunksPattern ( { useDirectoryPath = false } : { useDirectoryPath ?: boolean } ) : string {
63
+ return useDirectoryPath ? FILE_PATTERNS . STATIC_CHUNKS . PATH : FILE_PATTERNS . STATIC_CHUNKS . GLOB ;
64
+ }
65
+
66
+ function getStaticChunksPagesPattern ( { useDirectoryPath = false } : { useDirectoryPath ?: boolean } ) : string {
67
+ return useDirectoryPath ? FILE_PATTERNS . STATIC_CHUNKS_PAGES . PATH : FILE_PATTERNS . STATIC_CHUNKS_PAGES . GLOB ;
68
+ }
69
+
70
+ function getStaticChunksAppPattern ( { useDirectoryPath = false } : { useDirectoryPath ?: boolean } ) : string {
71
+ return useDirectoryPath ? FILE_PATTERNS . STATIC_CHUNKS_APP . PATH : FILE_PATTERNS . STATIC_CHUNKS_APP . GLOB ;
72
+ }
73
+
5
74
/**
6
- * Get Sentry Build Plugin options for the runAfterProductionCompile hook.
75
+ * Creates file patterns for source map uploads based on build tool and options
76
+ */
77
+ function createSourcemapUploadAssetPatterns (
78
+ normalizedDistPath : string ,
79
+ buildTool : BuildTool ,
80
+ widenClientFileUpload : boolean = false ,
81
+ ) : string [ ] {
82
+ const assets : string [ ] = [ ] ;
83
+
84
+ if ( buildTool . startsWith ( 'after-production-compile' ) ) {
85
+ assets . push ( path . posix . join ( normalizedDistPath , getServerPattern ( { useDirectoryPath : true } ) ) ) ;
86
+
87
+ if ( buildTool === 'after-production-compile-turbopack' ) {
88
+ // In turbopack we always want to upload the full static chunks directory
89
+ // as the build output is not split into pages|app chunks
90
+ assets . push ( path . posix . join ( normalizedDistPath , getStaticChunksPattern ( { useDirectoryPath : true } ) ) ) ;
91
+ } else {
92
+ // Webpack client builds in after-production-compile mode
93
+ if ( widenClientFileUpload ) {
94
+ assets . push ( path . posix . join ( normalizedDistPath , getStaticChunksPattern ( { useDirectoryPath : true } ) ) ) ;
95
+ } else {
96
+ assets . push (
97
+ path . posix . join ( normalizedDistPath , getStaticChunksPagesPattern ( { useDirectoryPath : true } ) ) ,
98
+ path . posix . join ( normalizedDistPath , getStaticChunksAppPattern ( { useDirectoryPath : true } ) ) ,
99
+ ) ;
100
+ }
101
+ }
102
+ } else {
103
+ if ( buildTool === 'webpack-nodejs' || buildTool === 'webpack-edge' ) {
104
+ // Server builds
105
+ assets . push (
106
+ path . posix . join ( normalizedDistPath , getServerPattern ( { useDirectoryPath : false } ) ) ,
107
+ path . posix . join ( normalizedDistPath , FILE_PATTERNS . SERVERLESS ) ,
108
+ ) ;
109
+ } else if ( buildTool === 'webpack-client' ) {
110
+ // Client builds
111
+ if ( widenClientFileUpload ) {
112
+ assets . push ( path . posix . join ( normalizedDistPath , getStaticChunksPattern ( { useDirectoryPath : false } ) ) ) ;
113
+ } else {
114
+ assets . push (
115
+ path . posix . join ( normalizedDistPath , getStaticChunksPagesPattern ( { useDirectoryPath : false } ) ) ,
116
+ path . posix . join ( normalizedDistPath , getStaticChunksAppPattern ( { useDirectoryPath : false } ) ) ,
117
+ ) ;
118
+ }
119
+ }
120
+ }
121
+
122
+ return assets ;
123
+ }
124
+
125
+ /**
126
+ * Creates ignore patterns for source map uploads
127
+ */
128
+ function createSourcemapUploadIgnorePattern (
129
+ normalizedDistPath : string ,
130
+ widenClientFileUpload : boolean = false ,
131
+ ) : string [ ] {
132
+ const ignore : string [ ] = [ ] ;
133
+
134
+ // We only add main-* files if the user has not opted into it
135
+ if ( ! widenClientFileUpload ) {
136
+ ignore . push ( path . posix . join ( normalizedDistPath , FILE_PATTERNS . MAIN_CHUNKS ) ) ;
137
+ }
138
+
139
+ // Always ignore these patterns
140
+ ignore . push (
141
+ path . posix . join ( normalizedDistPath , FILE_PATTERNS . FRAMEWORK_CHUNKS ) ,
142
+ path . posix . join ( normalizedDistPath , FILE_PATTERNS . FRAMEWORK_CHUNKS_DOT ) ,
143
+ path . posix . join ( normalizedDistPath , FILE_PATTERNS . POLYFILLS_CHUNKS ) ,
144
+ path . posix . join ( normalizedDistPath , FILE_PATTERNS . WEBPACK_CHUNKS ) ,
145
+ ) ;
146
+
147
+ return ignore ;
148
+ }
149
+
150
+ /**
151
+ * Creates file patterns for deletion after source map upload
152
+ */
153
+ function createFilesToDeleteAfterUploadPattern (
154
+ normalizedDistPath : string ,
155
+ buildTool : BuildTool ,
156
+ deleteSourcemapsAfterUpload : boolean ,
157
+ useRunAfterProductionCompileHook : boolean = false ,
158
+ ) : string [ ] | undefined {
159
+ if ( ! deleteSourcemapsAfterUpload ) {
160
+ return undefined ;
161
+ }
162
+
163
+ // We don't want to delete source maps for server builds as this led to errors on Vercel in the past
164
+ // See: https://github.com/getsentry/sentry-javascript/issues/13099
165
+ if ( buildTool === 'webpack-nodejs' || buildTool === 'webpack-edge' ) {
166
+ return undefined ;
167
+ }
168
+
169
+ // Skip deletion for webpack client builds when using the experimental hook
170
+ if ( buildTool === 'webpack-client' && useRunAfterProductionCompileHook ) {
171
+ return undefined ;
172
+ }
173
+
174
+ return SOURCEMAP_EXTENSIONS . map ( ext => path . posix . join ( normalizedDistPath , 'static' , '**' , ext ) ) ;
175
+ }
176
+
177
+ /**
178
+ * Determines if sourcemap uploads should be skipped
179
+ */
180
+ function shouldSkipSourcemapUpload ( buildTool : BuildTool , useRunAfterProductionCompileHook : boolean = false ) : boolean {
181
+ return useRunAfterProductionCompileHook && buildTool . startsWith ( 'webpack' ) ;
182
+ }
183
+
184
+ /**
185
+ * Source rewriting function for webpack sources
186
+ */
187
+ function rewriteWebpackSources ( source : string ) : string {
188
+ return source . replace ( / ^ w e b p a c k : \/ \/ (?: _ N _ E \/ ) ? / , '' ) ;
189
+ }
190
+
191
+ /**
192
+ * Creates release configuration
193
+ */
194
+ function createReleaseConfig (
195
+ releaseName : string | undefined ,
196
+ sentryBuildOptions : SentryBuildOptions ,
197
+ ) : SentryBuildPluginOptions [ 'release' ] {
198
+ if ( releaseName !== undefined ) {
199
+ return {
200
+ inject : false , // The webpack plugin's release injection breaks the `app` directory - we inject the release manually with the value injection loader instead.
201
+ name : releaseName ,
202
+ create : sentryBuildOptions . release ?. create ,
203
+ finalize : sentryBuildOptions . release ?. finalize ,
204
+ dist : sentryBuildOptions . release ?. dist ,
205
+ vcsRemote : sentryBuildOptions . release ?. vcsRemote ,
206
+ setCommits : sentryBuildOptions . release ?. setCommits ,
207
+ deploy : sentryBuildOptions . release ?. deploy ,
208
+ ...sentryBuildOptions . unstable_sentryWebpackPluginOptions ?. release ,
209
+ } ;
210
+ }
211
+
212
+ return {
213
+ inject : false ,
214
+ create : false ,
215
+ finalize : false ,
216
+ } ;
217
+ }
218
+
219
+ /**
220
+ * Get Sentry Build Plugin options for both webpack and turbopack builds.
221
+ * These options can be used in two ways:
222
+ * 1. The options can be built in a single operation after the production build completes
223
+ * 2. The options can be built in multiple operations, one for each webpack build
7
224
*/
8
225
export function getBuildPluginOptions ( {
9
226
sentryBuildOptions,
10
227
releaseName,
11
228
distDirAbsPath,
229
+ buildTool,
230
+ useRunAfterProductionCompileHook,
12
231
} : {
13
232
sentryBuildOptions : SentryBuildOptions ;
14
233
releaseName : string | undefined ;
15
234
distDirAbsPath : string ;
235
+ buildTool : BuildTool ;
236
+ useRunAfterProductionCompileHook ?: boolean ; // Whether the user has opted into using the experimental hook
16
237
} ) : SentryBuildPluginOptions {
17
- const sourcemapUploadAssets : string [ ] = [ ] ;
18
- const sourcemapUploadIgnore : string [ ] = [ ] ;
19
-
20
- const filesToDeleteAfterUpload : string [ ] = [ ] ;
21
-
22
238
// We need to convert paths to posix because Glob patterns use `\` to escape
23
239
// glob characters. This clashes with Windows path separators.
24
240
// See: https://www.npmjs.com/package/glob
25
- const normalizedDistDirAbsPath = distDirAbsPath . replace ( / \\ / g , '/' ) ;
241
+ const normalizedDistDirAbsPath = normalizePathForGlob ( distDirAbsPath ) ;
26
242
27
- sourcemapUploadAssets . push (
28
- path . posix . join ( normalizedDistDirAbsPath , '**' ) , // Next.js build output
243
+ const loggerPrefix = LOGGER_PREFIXES [ buildTool ] ;
244
+ const widenClientFileUpload = sentryBuildOptions . widenClientFileUpload ?? false ;
245
+ const deleteSourcemapsAfterUpload = sentryBuildOptions . sourcemaps ?. deleteSourcemapsAfterUpload ?? false ;
246
+
247
+ const sourcemapUploadAssets = createSourcemapUploadAssetPatterns (
248
+ normalizedDistDirAbsPath ,
249
+ buildTool ,
250
+ widenClientFileUpload ,
29
251
) ;
30
- if ( sentryBuildOptions . sourcemaps ?. deleteSourcemapsAfterUpload ) {
31
- filesToDeleteAfterUpload . push (
32
- path . posix . join ( normalizedDistDirAbsPath , '**' , '*.js.map' ) ,
33
- path . posix . join ( normalizedDistDirAbsPath , '**' , '*.mjs.map' ) ,
34
- path . posix . join ( normalizedDistDirAbsPath , '**' , '*.cjs.map' ) ,
35
- ) ;
36
- }
252
+
253
+ const sourcemapUploadIgnore = createSourcemapUploadIgnorePattern ( normalizedDistDirAbsPath , widenClientFileUpload ) ;
254
+
255
+ const filesToDeleteAfterUpload = createFilesToDeleteAfterUploadPattern (
256
+ normalizedDistDirAbsPath ,
257
+ buildTool ,
258
+ deleteSourcemapsAfterUpload ,
259
+ useRunAfterProductionCompileHook ,
260
+ ) ;
261
+
262
+ const skipSourcemapsUpload = shouldSkipSourcemapUpload ( buildTool , useRunAfterProductionCompileHook ) ;
37
263
38
264
return {
39
265
authToken : sentryBuildOptions . authToken ,
@@ -43,51 +269,28 @@ export function getBuildPluginOptions({
43
269
telemetry : sentryBuildOptions . telemetry ,
44
270
debug : sentryBuildOptions . debug ,
45
271
errorHandler : sentryBuildOptions . errorHandler ,
46
- reactComponentAnnotation : {
47
- ...sentryBuildOptions . reactComponentAnnotation ,
48
- ...sentryBuildOptions . unstable_sentryWebpackPluginOptions ?. reactComponentAnnotation ,
49
- } ,
272
+ reactComponentAnnotation : buildTool . startsWith ( 'after-production-compile' )
273
+ ? undefined
274
+ : {
275
+ ...sentryBuildOptions . reactComponentAnnotation ,
276
+ ...sentryBuildOptions . unstable_sentryWebpackPluginOptions ?. reactComponentAnnotation ,
277
+ } ,
50
278
silent : sentryBuildOptions . silent ,
51
279
url : sentryBuildOptions . sentryUrl ,
52
280
sourcemaps : {
53
- disable : sentryBuildOptions . sourcemaps ?. disable ,
54
- rewriteSources ( source ) {
55
- if ( source . startsWith ( 'webpack://_N_E/' ) ) {
56
- return source . replace ( 'webpack://_N_E/' , '' ) ;
57
- } else if ( source . startsWith ( 'webpack://' ) ) {
58
- return source . replace ( 'webpack://' , '' ) ;
59
- } else {
60
- return source ;
61
- }
62
- } ,
281
+ disable : skipSourcemapsUpload ? true : ( sentryBuildOptions . sourcemaps ?. disable ?? false ) ,
282
+ rewriteSources : rewriteWebpackSources ,
63
283
assets : sentryBuildOptions . sourcemaps ?. assets ?? sourcemapUploadAssets ,
64
284
ignore : sentryBuildOptions . sourcemaps ?. ignore ?? sourcemapUploadIgnore ,
65
285
filesToDeleteAfterUpload,
66
286
...sentryBuildOptions . unstable_sentryWebpackPluginOptions ?. sourcemaps ,
67
287
} ,
68
- release :
69
- releaseName !== undefined
70
- ? {
71
- inject : false , // The webpack plugin's release injection breaks the `app` directory - we inject the release manually with the value injection loader instead.
72
- name : releaseName ,
73
- create : sentryBuildOptions . release ?. create ,
74
- finalize : sentryBuildOptions . release ?. finalize ,
75
- dist : sentryBuildOptions . release ?. dist ,
76
- vcsRemote : sentryBuildOptions . release ?. vcsRemote ,
77
- setCommits : sentryBuildOptions . release ?. setCommits ,
78
- deploy : sentryBuildOptions . release ?. deploy ,
79
- ...sentryBuildOptions . unstable_sentryWebpackPluginOptions ?. release ,
80
- }
81
- : {
82
- inject : false ,
83
- create : false ,
84
- finalize : false ,
85
- } ,
288
+ release : createReleaseConfig ( releaseName , sentryBuildOptions ) ,
86
289
bundleSizeOptimizations : {
87
290
...sentryBuildOptions . bundleSizeOptimizations ,
88
291
} ,
89
292
_metaOptions : {
90
- loggerPrefixOverride : '[@sentry/nextjs]' ,
293
+ loggerPrefixOverride : loggerPrefix ,
91
294
telemetry : {
92
295
metaFramework : 'nextjs' ,
93
296
} ,
0 commit comments