From cd528d5ab444f17fb1db671147b8e5bcda74e11e Mon Sep 17 00:00:00 2001 From: Socialpranker <273312799+Socialpranker@users.noreply.github.com> Date: Thu, 9 Jul 2026 11:56:49 +0200 Subject: [PATCH] fix: chain preprocessor sourcemaps with an empty sources[0] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `apply_preprocessor_sourcemap` matches the incoming preprocessor/upstream sourcemap against `filename` when combining it with svelte's own map. A map generated via `new MagicString(code).generateMap()` without a `source` option has `sources: ['']`, which never matches `filename` and causes the whole chain to be dropped instead of composed — every mapping through it resolves to `{ source: null, line: null, column: null }`. Normalize an empty `sources[0]` to `filename` before combining, mirroring how Vite already treats an empty source as referring to the file being transformed (`pluginContainer.ts`'s `_getCombinedSourcemap`). Fixes #18491 --- .changeset/tame-donkeys-jump.md | 5 ++++ .../svelte/src/compiler/utils/mapped_code.js | 7 +++++ .../samples/sourcemap-empty-source/_config.js | 29 +++++++++++++++++++ .../sourcemap-empty-source/input.svelte | 6 ++++ 4 files changed, 47 insertions(+) create mode 100644 .changeset/tame-donkeys-jump.md create mode 100644 packages/svelte/tests/sourcemaps/samples/sourcemap-empty-source/_config.js create mode 100644 packages/svelte/tests/sourcemaps/samples/sourcemap-empty-source/input.svelte diff --git a/.changeset/tame-donkeys-jump.md b/.changeset/tame-donkeys-jump.md new file mode 100644 index 0000000000..aae38910db --- /dev/null +++ b/.changeset/tame-donkeys-jump.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: chain preprocessor sourcemaps with an empty `sources[0]` instead of dropping them diff --git a/packages/svelte/src/compiler/utils/mapped_code.js b/packages/svelte/src/compiler/utils/mapped_code.js index 7686ba59c6..635743c0c4 100644 --- a/packages/svelte/src/compiler/utils/mapped_code.js +++ b/packages/svelte/src/compiler/utils/mapped_code.js @@ -311,6 +311,13 @@ function apply_preprocessor_sourcemap(filename, svelte_map, preprocessor_map_inp typeof preprocessor_map_input === 'string' ? JSON.parse(preprocessor_map_input) : preprocessor_map_input; + // A preprocessor map with a missing/empty `sources[0]` (e.g. from a MagicString transform + // created without a `source` option) can't be matched against `filename` during combination, + // which silently drops the chain instead of erroring. Normalize it to `filename` first, the + // same way Vite treats an empty `sources[0]` as referring to the file being transformed. + if (preprocessor_map.sources?.length === 1 && !preprocessor_map.sources[0]) { + preprocessor_map.sources = [filename]; + } const result_map = combine_sourcemaps(filename, [svelte_map, preprocessor_map]); // Svelte expects a SourceMap which includes toUrl and toString. Instead of wrapping our output in a class, // we just tack on the extra properties. diff --git a/packages/svelte/tests/sourcemaps/samples/sourcemap-empty-source/_config.js b/packages/svelte/tests/sourcemaps/samples/sourcemap-empty-source/_config.js new file mode 100644 index 0000000000..43a2a4c16a --- /dev/null +++ b/packages/svelte/tests/sourcemaps/samples/sourcemap-empty-source/_config.js @@ -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' }] +}); diff --git a/packages/svelte/tests/sourcemaps/samples/sourcemap-empty-source/input.svelte b/packages/svelte/tests/sourcemaps/samples/sourcemap-empty-source/input.svelte new file mode 100644 index 0000000000..b4e0b50bdb --- /dev/null +++ b/packages/svelte/tests/sourcemaps/samples/sourcemap-empty-source/input.svelte @@ -0,0 +1,6 @@ + + +