Correction (edited shortly after filing): the trigger is esrap 2.3.3, not a Svelte version. The original text bisected over Svelte releases, which was confounded — each install resolved a different transitive esrap. Details below.
Describe the bug
With esrap 2.3.3, generate: 'server' emits syntactically invalid JavaScript when a /** @type {...} */ comment sits on an export let prop and the component also has a $: declaration.
The hoisted declaration for the reactive variable now has its declarator wrapped in parentheses, which is not legal after let:
// esrap 2.3.2 — valid
let /** @type {string} */
c;
// esrap 2.3.3 — invalid
let /** @type {string} */ (
c);
V8 rejects it with SyntaxError: Unexpected strict mode reserved word, oxc/rolldown with The keyword 'let' is reserved — since let cannot start a declaration here, it parses as an identifier reference, which is reserved in strict mode/modules.
generate: 'client' is unaffected, so this only surfaces in SSR builds.
Because svelte depends on esrap: ^2.2.9, a fresh npm install resolves 2.3.3 and any project of this shape starts failing to build with no change on its own side. sveltekit-superforms is one concrete path in: its package entry re-exports dist/client/SuperDebug.svelte as the default export, so the component enters the module graph from every import { superForm } from 'sveltekit-superforms' and fails to parse even though it is never rendered.
Reproduction
MinRepro.svelte:
<script>
/** @type {string} */
export let a = 'x';
$: c = a;
</script>
{c}
import fs from 'node:fs';
import { compile } from 'svelte/compiler';
fs.writeFileSync(
'out.mjs',
compile(fs.readFileSync('MinRepro.svelte', 'utf8'), {
generate: 'server',
filename: 'MinRepro.svelte'
}).js.code
);
// node --check out.mjs -> SyntaxError: Unexpected strict mode reserved word
Both ingredients are required, and the trigger is specifically @type. Removing the $: line, or using /** Docs. */, @param, @deprecated, @typedef, @returns, a non-JSDoc /* … */ block, or // comments, all produce valid output.
Version boundary
Pinning esrap through overrides with svelte held fixed, on a clean node_modules each time:
| esrap |
result |
| 2.2.10 … 2.3.2 |
ok |
| 2.3.3 (latest, published 2026-08-13) |
broken |
Independent of the Svelte version — 5.56.1 and 5.56.9 are both ok on esrap 2.3.2 and both broken on 2.3.3.
This may belong in sveltejs/esrap rather than here; filing where it surfaces.
Stack
svelte: 5.56.9 (also reproduced on 5.56.1)
esrap: 2.3.3 (2.3.2 and earlier are fine)
sveltekit-superforms: 2.30.2
@sveltejs/kit: 2.70.2
@sveltejs/vite-plugin-svelte: 7.3.0
vite: 8.2.1 (rolldown 1.2.4)
typescript: 6.0.2
node: 22.18.0
npm: 10.9.3
OS: macOS 26.5.2
Severity
annoyance — the compiler produces invalid output silently rather than erroring, so it surfaces as a bundler parse error in an unrelated node_modules file.
Describe the bug
With
esrap2.3.3,generate: 'server'emits syntactically invalid JavaScript when a/** @type {...} */comment sits on anexport letprop and the component also has a$:declaration.The hoisted declaration for the reactive variable now has its declarator wrapped in parentheses, which is not legal after
let:V8 rejects it with
SyntaxError: Unexpected strict mode reserved word, oxc/rolldown withThe keyword 'let' is reserved— sinceletcannot start a declaration here, it parses as an identifier reference, which is reserved in strict mode/modules.generate: 'client'is unaffected, so this only surfaces in SSR builds.Because
sveltedepends onesrap: ^2.2.9, a freshnpm installresolves 2.3.3 and any project of this shape starts failing to build with no change on its own side.sveltekit-superformsis one concrete path in: its package entry re-exportsdist/client/SuperDebug.svelteas the default export, so the component enters the module graph from everyimport { superForm } from 'sveltekit-superforms'and fails to parse even though it is never rendered.Reproduction
MinRepro.svelte:Both ingredients are required, and the trigger is specifically
@type. Removing the$:line, or using/** Docs. */,@param,@deprecated,@typedef,@returns, a non-JSDoc/* … */block, or//comments, all produce valid output.Version boundary
Pinning
esrapthroughoverrideswithsvelteheld fixed, on a cleannode_moduleseach time:Independent of the Svelte version — 5.56.1 and 5.56.9 are both ok on esrap 2.3.2 and both broken on 2.3.3.
This may belong in
sveltejs/esraprather than here; filing where it surfaces.Stack
Severity
annoyance — the compiler produces invalid output silently rather than erroring, so it surfaces as a bundler parse error in an unrelated
node_modulesfile.