mirror of https://github.com/sveltejs/svelte
fix: chain preprocessor sourcemaps with an empty sources[0] (#18518)
## Summary Fixes #18491 — the compiler discards an upstream plugin's sourcemap when it's generated without a `source` option (e.g. `new MagicString(code).generateMap()`), producing wrong devtools/stack-trace positions. ### Root cause `compile()` already supports composing an incoming sourcemap into its output via `options.sourcemap` (`merge_with_preprocessor_map` → `apply_preprocessor_sourcemap` → `combine_sourcemaps` in `packages/svelte/src/compiler/utils/mapped_code.js`). This is the same mechanism `preprocess()` uses internally, and it's the documented contract for tools that transform a `.svelte` file before compiling it (see `CompileOptions.sourcemap`'s doc comment). `combine_sourcemaps` composes maps by matching `sourcefile === filename` (the basename of the file being compiled). A sourcemap produced by `new MagicString(code).generateMap()` **without** a `source` option — which is exactly what the reporter's Vite plugin does — has `sources: ['']`. That empty string never equals `filename`, so `remapping()` treats the node as a leaf (the "original" file) instead of a branch to keep chaining through, and the whole incoming map is silently dropped. The result: every position that flows through that segment resolves to `{ source: null, line: null, column: null }`, which is what produces the wrong/missing devtools mapping described in the issue. Vite itself already has to handle this exact ambiguity: in `pluginContainer.ts`'s `_getCombinedSourcemap`, an empty `sources[0]` from a MagicString-based transform is patched to refer to the file being transformed before Vite uses it internally. This PR applies the same normalization on svelte's side, so the contract holds regardless of whether the caller happens to pass a `source` option to `generateMap()`. ### Fix In `apply_preprocessor_sourcemap`, normalize an incoming map's `sources: ['']` (or `[null]`/`[undefined]`) to `[filename]` before calling `combine_sourcemaps`, so the chain-matching step can actually find it.pull/18523/head
parent
41642b70ee
commit
5edd8b0602
@ -0,0 +1,5 @@
|
||||
---
|
||||
'svelte': patch
|
||||
---
|
||||
|
||||
fix: chain preprocessor sourcemaps with an empty `sources[0]` instead of dropping them
|
||||
@ -0,0 +1,29 @@
|
||||
import * as fs from 'node:fs';
|
||||
import MagicString from 'magic-string';
|
||||
import { test } from '../../test';
|
||||
|
||||
// Simulates a bundler plugin (e.g. a Vite plugin using `magic-string`) that transforms
|
||||
// the Svelte source *before* it reaches `compile()`, and hands its own sourcemap to
|
||||
// `compileOptions.sourcemap` — the documented way to let svelte chain an upstream map
|
||||
// into its own output map. Crucially, the upstream map is generated *without* a `source`
|
||||
// option, exactly like `new MagicString(code).generateMap()` — this yields a sourcemap
|
||||
// whose `sources` is `['']`, which previously broke the chain entirely (see #18491)
|
||||
// instead of being treated as "this file", causing every mapping through it to resolve
|
||||
// to `{ source: null, line: null, column: null }`.
|
||||
const input = fs.readFileSync(new URL('./input.svelte', import.meta.url), 'utf-8');
|
||||
const src = new MagicString(input);
|
||||
src.overwrite(
|
||||
src.original.indexOf('count * 2'),
|
||||
src.original.indexOf('count * 2') + 'count * 2'.length,
|
||||
'count * 2',
|
||||
{
|
||||
storeName: false
|
||||
}
|
||||
);
|
||||
|
||||
export default test({
|
||||
compileOptions: {
|
||||
sourcemap: src.generateMap({ hires: true })
|
||||
},
|
||||
client: [{ str: 'let doubled' }]
|
||||
});
|
||||
@ -0,0 +1,6 @@
|
||||
<script>
|
||||
let count = 0;
|
||||
let doubled = count * 2;
|
||||
</script>
|
||||
|
||||
<button>clicks: {count}</button>
|
||||
Loading…
Reference in new issue