Repository navigation
feat(bundler-plugins): Enable hidden source maps and remove them after upload in the webpack plugin - #25647
Conversation
size-limit report 📦Compared: 995ac9f (PR test merge using develop at 03492d2) Against: c536e1e (develop) · base run #65326
|
c0e6cee to
4709561
Compare
Optional E2E testsSome optional checks were not successful. They do not block the merge. 6 not successful, 50 successful. See the run.
Successful checks
|
4709561 to
63db386
Compare
247acf4 to
e7fe1ce
Compare
e7fe1ce to
24328a1
Compare
24328a1 to
4e1417f
Compare
4e1417f to
4163a84
Compare
e63c04d to
b5617e0
Compare
b5617e0 to
b059d9c
Compare
b059d9c to
35a6a1b
Compare
35a6a1b to
db3ec50
Compare
db3ec50 to
084f3d9
Compare
65dd2fc to
02f7a72
Compare
02f7a72 to
7ac38b2
Compare
7ac38b2 to
c9ff35a
Compare
c9ff35a to
4f8b5d4
Compare
Auto-merge was disabled for another pull request in the stack
4f8b5d4 to
a984831
Compare
isaacs
left a comment
There was a problem hiding this comment.
Deciding in apply(), before webpack's defaults, and staging after RealContentHashPlugin are both the right choices. Content hashes and file names are final, the staged JS matches the emitted file, and deleting assets in processAssets means the maps never reach disk. The per-compiler state and the emit/done/failed bookkeeping handle MultiCompiler, child compilers, watch mode and partial failures well.
The weak point is that webpack's devtool covers every asset type, while staging only handles JS. On the default path, that leaves CSS maps the user never had, and with the array form it can override per-type settings.
| code: asset.source.source().toString(), | ||
| mapSource: sourceMapAsset.source.source().toString(), | ||
| mapDir: path.join(outputPath, path.dirname(sourceMapName)), | ||
| chunkPath: path.join(outputPath, asset.name), |
There was a problem hiding this comment.
The new default writes CSS source maps that stay in the output. When devtool is unset, the plugin sets the string 'hidden-source-map', which webpack applies to every asset type. css-loader defaults its sourceMap option from devtool, so with mini-css-extract-plugin (or webpack's native CSS) webpack now emits main.css.map. stageSourceMaps only looks at JS assets, so the CSS map is written to disk.
Before this PR, an app with no devtool had no maps. After upgrading it ships a hidden CSS map that holds the original CSS (or Sass/Less) source, which is what this feature is meant to prevent. Bugbot raised this already, and your answer is right for Vite, but not for webpack.
I could see two options for a fix, both are pretty small.
- On webpack 5.105+, set
devtoolto[{ type: 'javascript', use: 'hidden-source-map' }]instead of the string, so CSS gets no maps at all. I checked that a JS-only array emits nomain.css.map([{"type":"javascript","use":"source-map"}]gavemain.css,main.js,main.js.map). Older webpack versions need a fallback. - Or, when the decision came from
kind === 'unset', also delete map assets of non-JS assets (related.sourceMapof any asset, or*.css.map) without uploading them, because the user never asked for them.
We should add a test with a CSS import either way.
| const decision = sentryBuildPluginManager.sourceMaps.decide(kind); | ||
|
|
||
| if (decision.enableHidden) { | ||
| compiler.options.devtool = kind === 'visible' ? `hidden-${compiler.options.devtool}` : 'hidden-source-map'; |
There was a problem hiding this comment.
Webpack 5.105 added an array form, devtool: [{ type: 'javascript' | 'css' | 'all', use }]. Bugbot's "Array devtool becomes invalid value" (hidden-[object Object]) does not happen. Webpack wraps the options in a compatibility proxy that returns the all or javascript entry's use as a string when a plugin reads options.devtool. But writing compiler.options.devtool = 'hidden-...' replaces the whole array with a string, which webpack then applies to type: 'all'. Reproduced:
[{ type: 'javascript', use: 'source-map' }]+true: the user asked for no CSS maps, and the output now hasmain.css.map(left on disk, see comment above).[{ type: 'javascript', use: 'source-map' }, { type: 'css', use: 'inline-source-map' }]+true: the user's inline CSS maps become a separatemain.css.mapfile left on disk.
This only affects users who opt in with true and use the new array form, so it is rare.
Suggestion: when Array.isArray holds for the raw value (read it through Reflect.get on the target, or check typeof before the proxy turns it into a string), add the hidden- prefix only to the javascript/all entries and leave the rest alone. If that is too much for this PR, skip the switch for array values and log at debug level.
| return hasSourceMapPlugin ? undefined : 'off'; | ||
| } | ||
| if (devtool.includes('inline') || devtool.includes('eval')) { | ||
| return 'inline'; |
There was a problem hiding this comment.
'eval' contains no source map at all, but includes('eval') maps it to inline. The user then gets "Source maps are inlined into your bundles. They are uploaded to Sentry, but…", which is false: nothing is uploaded. Plain eval in a production build is rare.
Suggestion: map devtool === 'eval' (no source-map in the value) to 'off'.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b97e871. Configure here.
…r upload in the webpack plugin When `devtool` is unset and an upload can run, the plugin sets `hidden-source-map`, copies each JS asset and its map into the upload staging folder at the end of `processAssets` and deletes the map assets, so webpack never emits them. A `devtool` with a manual `SourceMapDevToolPlugin` and development builds stay untouched.

Part of the stack for #25609. When
devtoolis unset and an upload can run, the plugin setshidden-source-map; a visible devtool withdeleteSourcemapsAfterUpload: truegets thehidden-prefix, and a plainevaldevtool counts as source maps turned off. The maps are staged at the end ofprocessAssets, after the real content hashes are in place, and deleted from the compilation together with any compressed variants, so webpack never emits them. When the plugin turned the maps on, every map goes, including CSS maps and the maps of chunks thatsourcemaps.assetsorsourcemaps.ignoreexclude; maps the user enabled stay unless their chunk was uploaded.Development builds and a
devtool: falsewith a manualSourceMapDevToolPluginstay untouched. With webpack's array form ofdevtool,truereplaces the array with one hidden setting for all asset types, because the compatibility proxy hides the array from plugins; the option docs say so. Rspack uses the same hooks but is not covered by tests, because@rspack/coreis not installed in the repo. Integration fixtures coverignoreandassetson the staged path for webpack 5.