Pagedeck

2. No sourcemaps in production output

Date: 2026-08-25

Status

Accepted. Forward-looking: nothing in the repo sets build.sourcemap, so no sourcemap is emitted today and there is no code change attached to this. Raised by #83.

Context

The question came up from compileIslands (packages/core/src/react-compiler.ts), whose plugin passes sourceMaps: true to Babel and returns compiled.map alongside the code. Measuring what that map actually contains answered a different question than the one asked, so both answers are recorded here.

The plugin's map is not the risk, and sourceMaps: true is not the setting that matters. A production build of a fixture island through the real Vite + Rolldown pipeline emits a byte-identical map whether compileIslands is in the plugin list or not. The plugin has to pass sourceMaps: true regardless: a Vite plugin that transforms code and returns no map breaks every map downstream of it. What that returned map does is keep the chain intact through the transform, and whether anything reaches disk is decided elsewhere, by build.sourcemap. Setting it is the whole decision.

What an emitted map discloses is the original source, in full. Babel includes sourcesContent by default when sourceMaps is on, Rolldown carries it through, and the emitted .map holds the complete original text of every authored module — comments, dead branches, anything the bundler would otherwise have dropped. This was confirmed by building a fixture whose source carried a marker comment and finding that comment in the emitted map. For a site built on a private design system, publishing that map publishes the design system.

It does not disclose build-machine paths. An earlier draft of this ADR claimed the map's filename would carry the absolute path of the module on the machine that ran the build, on the grounds that the plugin passes the resolved module id as Babel's filename. That is wrong and was corrected by measurement. Babel defaults the map's sources to a basename rather than to the filename it was given, and Vite rewrites sources again relative to the output file. The emitted map records ../../src/Hero.tsx, and no absolute path appears anywhere in it. What it does disclose is the shape of the source tree above the output directory, which is a much smaller thing.

Vite also writes a //# sourceMappingURL= comment into each chunk, so an emitted map is not merely present on the CDN, it is advertised to every browser that loads the page.

The decision has to be made before someone sets build.sourcemap to debug a production issue, because at that moment it looks like a one-line change with an obvious payoff, and nothing in the diff shows what rides along with it.

Decision

A production build emits no sourcemap. Do not set build.sourcemap for the production build.

Leave sourceMaps: true in the React Compiler plugin. It is what keeps the map chain intact through the transform, it is not what puts anything on a CDN, and removing it would degrade every downstream map while protecting nothing.

This says nothing about development builds or the dev server (#50). The machine that builds those is the machine reading them.

Consequences

A production stack trace is against compiled output, so a crash reported from the field names a minified frame rather than a component and a line. That is the cost, and it is paid against publishing every authored component's source to anyone who fetches the site.

A production build config landed with buildClient (packages/core/src/client-build.ts, issue #164), which sets no build.sourcemap, and the check this section asked for is on it: client-build.test.ts asserts that no emitted file's path ends .map and that no chunk carries a sourceMappingURL comment. The output directory is still #37's to write — buildClient hands its files back rather than placing them — so the check reads the emitted set instead of a tree on disk, which is the same claim about the same bytes.

If the trade is ever reopened, reopen it on the disclosure, not on the paths — the paths were the weaker of the two reasons and measurement removed them.

Alternatives considered

Emit sourcemaps and upload them to an error reporter. The strongest alternative, and the one to take if production stack traces become necessary. build.sourcemap: "hidden" produces the map without the sourceMappingURL comment, the upload step sends it to the reporter, and the file is deleted from the output directory before publish. Not adopted now because it requires a release pipeline and an error reporter, neither of which exists yet, and because a half-built version of it — maps emitted, upload not wired — publishes exactly what this ADR refuses. It is a decision for whoever builds the release pipeline, made with the pipeline in front of them.

Emit sourcemaps and delete them from the output directory after the build. Rejected as a standalone measure. It has the same failure mode as the alternative above with none of the payoff: the window between emit and delete is a build step someone can reorder, and a sourceMappingURL comment left in the chunks points browsers at files that are meant to be gone. If the maps are not going anywhere useful, not producing them is strictly simpler than producing and removing them.

Strip sourceMaps: true from the compiler plugin. Rejected, and it was the original suspicion. Measurement showed the plugin is not the source of the risk, and dropping the option would break the map chain for the dev server and for any future tooling that consumes it, in exchange for nothing.