fix: scope SSR boundary failed snippets to their boundary (#18593)

Each `<svelte:boundary>` with:

```svelte
{#snippet failed(error)}
  ...
{/snippet}
```

generated a function named failed. Sibling boundaries placed both
functions in the same SSR scope:

```js
{
  function failed() {}
  function failed() {} // Identifier `failed` has already been declared
}
```

Resulting in a `[PARSE_ERROR] Identifier `failed` has already been
declared` error during build time.

A let declaration such as `{const x = 0}` can create a nested lexical
block, exposing the collision.

The fix gives each boundary its own scope:

```js
{
  function failed() {}
  $$renderer.boundary({ failed }, ...);
}

{
  function failed() {}
  $$renderer.boundary({ failed }, ...);
}
``
pull/18680/merge
Kamil Jakubus 4 days ago committed by GitHub
parent f4918d86f3
commit 38ef714f1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: scope SSR boundary failed snippets to their boundary

@ -1,4 +1,4 @@
/** @import { BlockStatement } from 'estree' */
/** @import { BlockStatement, Statement } from 'estree' */
/** @import { AST } from '#compiler' */
/** @import { ComponentContext } from '../types' */
import * as b from '#compiler/builders';
@ -84,6 +84,9 @@ export function SvelteBoundary(node, context) {
}
const props = b.object([]);
/** @type {Statement[]} */
const init = [];
if (failed_attribute && !failed_snippet) {
const failed_callee = build_attribute_value(
failed_attribute.value,
@ -95,13 +98,15 @@ export function SvelteBoundary(node, context) {
props.properties.push(b.init('failed', failed_callee));
} else if (failed_snippet) {
context.visit(failed_snippet, context.state);
context.visit(failed_snippet, { ...context.state, init });
props.properties.push(b.init('failed', failed_snippet.expression));
}
context.state.template.push(
b.stmt(b.call('$$renderer.boundary', props, b.arrow([b.id('$$renderer')], children_body)))
const boundary = b.stmt(
b.call('$$renderer.boundary', props, b.arrow([b.id('$$renderer')], children_body))
);
context.state.template.push(init.length > 0 ? b.block([...init, boundary]) : boundary);
}
/**

@ -0,0 +1,11 @@
<div>
{const x = 0}
<svelte:boundary>
{#snippet failed()}{/snippet}
</svelte:boundary>
<svelte:boundary>
{#snippet failed()}{/snippet}
</svelte:boundary>
</div>
Loading…
Cancel
Save