1
1
import commonjs from '@rollup/plugin-commonjs' ;
2
- import virtual from '@rollup/plugin-virtual' ;
3
2
import { stringMatchesSomePattern } from '@sentry/utils' ;
4
3
import * as fs from 'fs' ;
5
4
import * as path from 'path' ;
6
5
import { rollup } from 'rollup' ;
7
6
8
- import { LoaderThis } from './types' ;
7
+ import type { LoaderThis } from './types' ;
9
8
10
9
const apiWrapperTemplatePath = path . resolve ( __dirname , '..' , 'templates' , 'apiWrapperTemplate.js' ) ;
11
10
const apiWrapperTemplateCode = fs . readFileSync ( apiWrapperTemplatePath , { encoding : 'utf8' } ) ;
@@ -16,8 +15,7 @@ const pageWrapperTemplateCode = fs.readFileSync(pageWrapperTemplatePath, { encod
16
15
// Just a simple placeholder to make referencing module consistent
17
16
const SENTRY_WRAPPER_MODULE_NAME = 'sentry-wrapper-module' ;
18
17
19
- // needs to end in .cjs in order for the `commonjs` plugin to pick it up - unfortunately the plugin has no option to
20
- // make this work in combination with the`virtual` plugin
18
+ // Needs to end in .cjs in order for the `commonjs` plugin to pick it up
21
19
const WRAPPING_TARGET_MODULE_NAME = '__SENTRY_WRAPPING_TARGET__.cjs' ;
22
20
23
21
type LoaderOptions = {
@@ -31,7 +29,13 @@ type LoaderOptions = {
31
29
* any data-fetching functions (`getInitialProps`, `getStaticProps`, and `getServerSideProps`) or API routes it contains
32
30
* are wrapped, and then everything is re-exported.
33
31
*/
34
- export default async function wrappingLoader ( this : LoaderThis < LoaderOptions > , userCode : string ) : Promise < string > {
32
+ export default function wrappingLoader (
33
+ this : LoaderThis < LoaderOptions > ,
34
+ userCode : string ,
35
+ userModuleSourceMap : any ,
36
+ ) : void {
37
+ this . async ( ) ;
38
+
35
39
// We know one or the other will be defined, depending on the version of webpack being used
36
40
const {
37
41
pagesDir,
@@ -56,7 +60,8 @@ export default async function wrappingLoader(this: LoaderThis<LoaderOptions>, us
56
60
57
61
// Skip explicitly-ignored pages
58
62
if ( stringMatchesSomePattern ( parameterizedRoute , excludeServerRoutes , true ) ) {
59
- return userCode ;
63
+ this . callback ( null , userCode , userModuleSourceMap ) ;
64
+ return ;
60
65
}
61
66
62
67
let templateCode = parameterizedRoute . startsWith ( '/api' ) ? apiWrapperTemplateCode : pageWrapperTemplateCode ;
@@ -69,15 +74,18 @@ export default async function wrappingLoader(this: LoaderThis<LoaderOptions>, us
69
74
70
75
// Run the proxy module code through Rollup, in order to split the `export * from '<wrapped file>'` out into
71
76
// individual exports (which nextjs seems to require).
72
- try {
73
- return await wrapUserCode ( templateCode , userCode ) ;
74
- } catch ( err ) {
75
- // eslint-disable-next-line no-console
76
- console . warn (
77
- `[@sentry/nextjs] Could not instrument ${ this . resourcePath } . An error occurred while auto-wrapping:\n${ err } ` ,
78
- ) ;
79
- return userCode ;
80
- }
77
+ wrapUserCode ( templateCode , userCode , userModuleSourceMap )
78
+ . then ( ( { code : wrappedCode , map : wrappedCodeSourceMap } ) => {
79
+ this . callback ( null , wrappedCode , wrappedCodeSourceMap ) ;
80
+ } )
81
+ . catch ( err => {
82
+ // eslint-disable-next-line no-console
83
+ console . warn (
84
+ `[@sentry/nextjs] Could not instrument ${ this . resourcePath } . An error occurred while auto-wrapping:\n${ err } ` ,
85
+ ) ;
86
+ this . callback ( null , userCode , userModuleSourceMap ) ;
87
+ return ;
88
+ } ) ;
81
89
}
82
90
83
91
/**
@@ -92,26 +100,53 @@ export default async function wrappingLoader(this: LoaderThis<LoaderOptions>, us
92
100
*
93
101
* @param wrapperCode The wrapper module code
94
102
* @param userModuleCode The user module code
95
- * @returns The wrapped user code
103
+ * @returns The wrapped user code and a source map that describes the transformations done by this function
96
104
*/
97
- async function wrapUserCode ( wrapperCode : string , userModuleCode : string ) : Promise < string > {
105
+ async function wrapUserCode (
106
+ wrapperCode : string ,
107
+ userModuleCode : string ,
108
+ userModuleSourceMap : any ,
109
+ ) : Promise < { code : string ; map ?: any } > {
98
110
const rollupBuild = await rollup ( {
99
111
input : SENTRY_WRAPPER_MODULE_NAME ,
100
112
101
113
plugins : [
102
- // We're using virtual modules so we don't have to mess around with file paths
103
- virtual ( {
104
- [ SENTRY_WRAPPER_MODULE_NAME ] : wrapperCode ,
105
- [ WRAPPING_TARGET_MODULE_NAME ] : userModuleCode ,
106
- } ) ,
114
+ // We're using a simple custom plugin that virtualizes our wrapper module and the user module, so we don't have to
115
+ // mess around with file paths and so that we can pass the original user module source map to rollup so that
116
+ // rollup gives us a bundle with correct source mapping to the original file
117
+ {
118
+ name : 'virtualize-sentry-wrapper-modules' ,
119
+ resolveId : id => {
120
+ if ( id === SENTRY_WRAPPER_MODULE_NAME || id === WRAPPING_TARGET_MODULE_NAME ) {
121
+ return id ;
122
+ } else {
123
+ return null ;
124
+ }
125
+ } ,
126
+ load ( id ) {
127
+ if ( id === SENTRY_WRAPPER_MODULE_NAME ) {
128
+ return wrapperCode ;
129
+ } else if ( id === WRAPPING_TARGET_MODULE_NAME ) {
130
+ return {
131
+ code : userModuleCode ,
132
+ map : userModuleSourceMap , // give rollup acces to original user module source map
133
+ } ;
134
+ } else {
135
+ return null ;
136
+ }
137
+ } ,
138
+ } ,
107
139
108
140
// People may use `module.exports` in their API routes or page files. Next.js allows that and we also need to
109
141
// handle that correctly so we let a plugin to take care of bundling cjs exports for us.
110
- commonjs ( ) ,
142
+ commonjs ( {
143
+ transformMixedEsModules : true ,
144
+ sourceMap : true ,
145
+ } ) ,
111
146
] ,
112
147
113
148
// We only want to bundle our wrapper module and the wrappee module into one, so we mark everything else as external.
114
- external : source => source !== SENTRY_WRAPPER_MODULE_NAME && source !== WRAPPING_TARGET_MODULE_NAME ,
149
+ external : sourceId => sourceId !== SENTRY_WRAPPER_MODULE_NAME && sourceId !== WRAPPING_TARGET_MODULE_NAME ,
115
150
116
151
// Prevent rollup from stressing out about TS's use of global `this` when polyfilling await. (TS will polyfill if the
117
152
// user's tsconfig `target` is set to anything before `es2017`. See https://stackoverflow.com/a/72822340 and
@@ -138,22 +173,18 @@ async function wrapUserCode(wrapperCode: string, userModuleCode: string): Promis
138
173
// externals entirely, with the result that their paths remain untouched (which is what we want).
139
174
makeAbsoluteExternalsRelative : false ,
140
175
141
- onwarn : ( ) => {
176
+ onwarn : ( _warning , _warn ) => {
142
177
// Suppress all warnings - we don't want to bother people with this output
143
- } ,
144
-
145
- output : {
146
- // TODO
147
- interop : 'auto' ,
148
- // TODO
149
- exports : 'named' ,
178
+ // Might be stuff like "you have unused imports"
179
+ // _warn(_warning); // uncomment to debug
150
180
} ,
151
181
} ) ;
152
182
153
183
const finalBundle = await rollupBuild . generate ( {
154
184
format : 'esm' ,
185
+ sourcemap : 'hidden' , // put source map data in the bundle but don't generate a source map commment in the output
155
186
} ) ;
156
187
157
188
// The module at index 0 is always the entrypoint, which in this case is the proxy module.
158
- return finalBundle . output [ 0 ] . code ;
189
+ return finalBundle . output [ 0 ] ;
159
190
}
0 commit comments