sveltejs / svelte · Issue No. 18643
Correction (edited shortly after filing): the trigger is
esrap2.3.3, not a Svelte version. The original text bisected over Svelte releases, which was confounded — each install resolved a different transitiveesrap. Details below.
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.
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.
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.
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
annoyance — the compiler produces invalid output silently rather than erroring, so it surfaces as a bundler parse error in an unrelated node_modules file.
Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.