Skip to content

Commit 14469f6

Browse files
author
Luca Forstner
committed
Fix source mapping
1 parent 7af6c7b commit 14469f6

File tree

5 files changed

+73
-42
lines changed

5 files changed

+73
-42
lines changed

packages/nextjs/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
},
1919
"dependencies": {
2020
"@rollup/plugin-commonjs": "24.0.0",
21-
"@rollup/plugin-virtual": "3.0.0",
2221
"@sentry/core": "7.29.0",
2322
"@sentry/integrations": "7.29.0",
2423
"@sentry/node": "7.29.0",

packages/nextjs/rollup.npm.config.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ export default [
3636
...makeNPMConfigVariants(
3737
makeBaseNPMConfig({
3838
entrypoints: ['src/config/loaders/index.ts'],
39-
// Needed in order to successfully import sucrase
40-
esModuleInterop: true,
4139

4240
packageSpecificConfig: {
4341
output: {
@@ -47,7 +45,7 @@ export default [
4745
// make it so Rollup calms down about the fact that we're combining default and named exports
4846
exports: 'named',
4947
},
50-
external: ['@rollup/plugin-commonjs', '@rollup/plugin-virtual', 'rollup'],
48+
external: ['@rollup/plugin-commonjs', 'rollup'],
5149
},
5250
}),
5351
),

packages/nextjs/src/config/loaders/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type webpack from 'webpack';
2+
13
export type LoaderThis<Options> = {
24
/** Path to the file being loaded */
35
resourcePath: string;
@@ -7,6 +9,12 @@ export type LoaderThis<Options> = {
79

810
// Function to add outside file used by loader to `watch` process
911
addDependency: (filepath: string) => void;
12+
13+
// Marks a loader as asynchronous
14+
async: webpack.loader.LoaderContext['async'];
15+
16+
// Return errors, code, and sourcemaps from an asynchronous loader
17+
callback: webpack.loader.LoaderContext['callback'];
1018
} & (
1119
| {
1220
// Loader options in Webpack 4

packages/nextjs/src/config/loaders/wrappingLoader.ts

Lines changed: 64 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import commonjs from '@rollup/plugin-commonjs';
2-
import virtual from '@rollup/plugin-virtual';
32
import { stringMatchesSomePattern } from '@sentry/utils';
43
import * as fs from 'fs';
54
import * as path from 'path';
65
import { rollup } from 'rollup';
76

8-
import { LoaderThis } from './types';
7+
import type { LoaderThis } from './types';
98

109
const apiWrapperTemplatePath = path.resolve(__dirname, '..', 'templates', 'apiWrapperTemplate.js');
1110
const apiWrapperTemplateCode = fs.readFileSync(apiWrapperTemplatePath, { encoding: 'utf8' });
@@ -16,8 +15,7 @@ const pageWrapperTemplateCode = fs.readFileSync(pageWrapperTemplatePath, { encod
1615
// Just a simple placeholder to make referencing module consistent
1716
const SENTRY_WRAPPER_MODULE_NAME = 'sentry-wrapper-module';
1817

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
2119
const WRAPPING_TARGET_MODULE_NAME = '__SENTRY_WRAPPING_TARGET__.cjs';
2220

2321
type LoaderOptions = {
@@ -31,7 +29,13 @@ type LoaderOptions = {
3129
* any data-fetching functions (`getInitialProps`, `getStaticProps`, and `getServerSideProps`) or API routes it contains
3230
* are wrapped, and then everything is re-exported.
3331
*/
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+
3539
// We know one or the other will be defined, depending on the version of webpack being used
3640
const {
3741
pagesDir,
@@ -56,7 +60,8 @@ export default async function wrappingLoader(this: LoaderThis<LoaderOptions>, us
5660

5761
// Skip explicitly-ignored pages
5862
if (stringMatchesSomePattern(parameterizedRoute, excludeServerRoutes, true)) {
59-
return userCode;
63+
this.callback(null, userCode, userModuleSourceMap);
64+
return;
6065
}
6166

6267
let templateCode = parameterizedRoute.startsWith('/api') ? apiWrapperTemplateCode : pageWrapperTemplateCode;
@@ -69,15 +74,18 @@ export default async function wrappingLoader(this: LoaderThis<LoaderOptions>, us
6974

7075
// Run the proxy module code through Rollup, in order to split the `export * from '<wrapped file>'` out into
7176
// 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+
});
8189
}
8290

8391
/**
@@ -92,26 +100,53 @@ export default async function wrappingLoader(this: LoaderThis<LoaderOptions>, us
92100
*
93101
* @param wrapperCode The wrapper module code
94102
* @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
96104
*/
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 }> {
98110
const rollupBuild = await rollup({
99111
input: SENTRY_WRAPPER_MODULE_NAME,
100112

101113
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+
},
107139

108140
// People may use `module.exports` in their API routes or page files. Next.js allows that and we also need to
109141
// 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+
}),
111146
],
112147

113148
// 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,
115150

116151
// Prevent rollup from stressing out about TS's use of global `this` when polyfilling await. (TS will polyfill if the
117152
// 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
138173
// externals entirely, with the result that their paths remain untouched (which is what we want).
139174
makeAbsoluteExternalsRelative: false,
140175

141-
onwarn: () => {
176+
onwarn: (_warning, _warn) => {
142177
// 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
150180
},
151181
});
152182

153183
const finalBundle = await rollupBuild.generate({
154184
format: 'esm',
185+
sourcemap: 'hidden', // put source map data in the bundle but don't generate a source map commment in the output
155186
});
156187

157188
// 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];
159190
}

yarn.lock

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3478,11 +3478,6 @@
34783478
"@rollup/pluginutils" "^3.1.0"
34793479
resolve "^1.17.0"
34803480

3481-
"@rollup/plugin-virtual@3.0.0":
3482-
version "3.0.0"
3483-
resolved "https://registry.yarnpkg.com/@rollup/plugin-virtual/-/plugin-virtual-3.0.0.tgz#8c3f54b4ab4b267d9cd3dcbaedc58d4fd1deddca"
3484-
integrity sha512-K9KORe1myM62o0lKkNR4MmCxjwuAXsZEtIHpaILfv4kILXTOrXt/R2ha7PzMcCHPYdnkWPiBZK8ed4Zr3Ll5lQ==
3485-
34863481
"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.0.9", "@rollup/pluginutils@^3.1.0":
34873482
version "3.1.0"
34883483
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"

0 commit comments

Comments
 (0)