Skip to content

Commit 626b9fc

Browse files
committed
Merge branch 'main' into boundary-batch-nullpointer-fix
2 parents bde51cd + 7105736 commit 626b9fc

File tree

64 files changed

+757
-229
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+757
-229
lines changed

.changeset/cool-garlics-fail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: place instance-level snippets inside async body

.changeset/silent-pigs-relax.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: restore batch along with effect context

documentation/docs/98-reference/.generated/compile-errors.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,51 @@ Cyclical dependency detected: %cycle%
196196
`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>`, `<svelte:boundary` or `<Component>`
197197
```
198198

199+
### const_tag_invalid_reference
200+
201+
```
202+
The `{@const %name% = ...}` declaration is not available in this snippet
203+
```
204+
205+
The following is an error:
206+
207+
```svelte
208+
<svelte:boundary>
209+
{@const foo = 'bar'}
210+
211+
{#snippet failed()}
212+
{foo}
213+
{/snippet}
214+
</svelte:boundary>
215+
```
216+
217+
Here, `foo` is not available inside `failed`. The top level code inside `<svelte:boundary>` becomes part of the implicit `children` snippet, in other words the above code is equivalent to this:
218+
219+
```svelte
220+
<svelte:boundary>
221+
{#snippet children()}
222+
{@const foo = 'bar'}
223+
{/snippet}
224+
225+
{#snippet failed()}
226+
{foo}
227+
{/snippet}
228+
</svelte:boundary>
229+
```
230+
231+
The same applies to components:
232+
233+
```svelte
234+
<Component>
235+
{@const foo = 'bar'}
236+
237+
{#snippet someProp()}
238+
<!-- error -->
239+
{foo}
240+
{/snippet}
241+
</Component>
242+
```
243+
199244
### constant_assignment
200245

201246
```

packages/svelte/CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
# svelte
22

3+
## 5.38.3
4+
5+
### Patch Changes
6+
7+
- fix: ensure correct order of template effect values ([#16655](https://github.com/sveltejs/svelte/pull/16655))
8+
9+
- fix: allow async `{@const}` in more places ([#16643](https://github.com/sveltejs/svelte/pull/16643))
10+
11+
- fix: properly catch top level await errors ([#16619](https://github.com/sveltejs/svelte/pull/16619))
12+
13+
- perf: prune effects without dependencies ([#16625](https://github.com/sveltejs/svelte/pull/16625))
14+
15+
- fix: only emit `for_await_track_reactivity_loss` in async mode ([#16644](https://github.com/sveltejs/svelte/pull/16644))
16+
17+
## 5.38.2
18+
19+
### Patch Changes
20+
21+
- perf: run blocks eagerly during flush instead of aborting ([#16631](https://github.com/sveltejs/svelte/pull/16631))
22+
23+
- fix: don't clone non-proxies in `$inspect` ([#16617](https://github.com/sveltejs/svelte/pull/16617))
24+
25+
- fix: avoid recursion error when tagging circular references ([#16622](https://github.com/sveltejs/svelte/pull/16622))
26+
327
## 5.38.1
428

529
### Patch Changes

packages/svelte/messages/compile-errors/template.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,49 @@
124124

125125
> `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>`, `<svelte:boundary` or `<Component>`
126126
127+
## const_tag_invalid_reference
128+
129+
> The `{@const %name% = ...}` declaration is not available in this snippet
130+
131+
The following is an error:
132+
133+
```svelte
134+
<svelte:boundary>
135+
{@const foo = 'bar'}
136+
137+
{#snippet failed()}
138+
{foo}
139+
{/snippet}
140+
</svelte:boundary>
141+
```
142+
143+
Here, `foo` is not available inside `failed`. The top level code inside `<svelte:boundary>` becomes part of the implicit `children` snippet, in other words the above code is equivalent to this:
144+
145+
```svelte
146+
<svelte:boundary>
147+
{#snippet children()}
148+
{@const foo = 'bar'}
149+
{/snippet}
150+
151+
{#snippet failed()}
152+
{foo}
153+
{/snippet}
154+
</svelte:boundary>
155+
```
156+
157+
The same applies to components:
158+
159+
```svelte
160+
<Component>
161+
{@const foo = 'bar'}
162+
163+
{#snippet someProp()}
164+
<!-- error -->
165+
{foo}
166+
{/snippet}
167+
</Component>
168+
```
169+
127170
## debug_tag_invalid_arguments
128171

129172
> {@debug ...} arguments must be identifiers, not arbitrary expressions

packages/svelte/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "svelte",
33
"description": "Cybernetically enhanced web apps",
44
"license": "MIT",
5-
"version": "5.38.1",
5+
"version": "5.38.3",
66
"type": "module",
77
"types": "./types/index.d.ts",
88
"engines": {

packages/svelte/src/compiler/errors.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,16 @@ export function const_tag_invalid_placement(node) {
985985
e(node, 'const_tag_invalid_placement', `\`{@const}\` must be the immediate child of \`{#snippet}\`, \`{#if}\`, \`{:else if}\`, \`{:else}\`, \`{#each}\`, \`{:then}\`, \`{:catch}\`, \`<svelte:fragment>\`, \`<svelte:boundary\` or \`<Component>\`\nhttps://svelte.dev/e/const_tag_invalid_placement`);
986986
}
987987

988+
/**
989+
* The `{@const %name% = ...}` declaration is not available in this snippet
990+
* @param {null | number | NodeLike} node
991+
* @param {string} name
992+
* @returns {never}
993+
*/
994+
export function const_tag_invalid_reference(node, name) {
995+
e(node, 'const_tag_invalid_reference', `The \`{@const ${name} = ...}\` declaration is not available in this snippet \nhttps://svelte.dev/e/const_tag_invalid_reference`);
996+
}
997+
988998
/**
989999
* {@debug ...} arguments must be identifiers, not arbitrary expressions
9901000
* @param {null | number | NodeLike} node

packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as w from '../../../warnings.js';
77
import { is_rune } from '../../../../utils.js';
88
import { mark_subtree_dynamic } from './shared/fragment.js';
99
import { get_rune } from '../../scope.js';
10+
import { is_component_node } from '../../nodes.js';
1011

1112
/**
1213
* @param {Identifier} node
@@ -155,5 +156,37 @@ export function Identifier(node, context) {
155156
) {
156157
w.reactive_declaration_module_script_dependency(node);
157158
}
159+
160+
if (binding.metadata?.is_template_declaration && context.state.options.experimental.async) {
161+
let snippet_name;
162+
163+
// Find out if this references a {@const ...} declaration of an implicit children snippet
164+
// when it is itself inside a snippet block at the same level. If so, error.
165+
for (let i = context.path.length - 1; i >= 0; i--) {
166+
const parent = context.path[i];
167+
const grand_parent = context.path[i - 1];
168+
169+
if (parent.type === 'SnippetBlock') {
170+
snippet_name = parent.expression.name;
171+
} else if (
172+
snippet_name &&
173+
grand_parent &&
174+
parent.type === 'Fragment' &&
175+
(is_component_node(grand_parent) ||
176+
(grand_parent.type === 'SvelteBoundary' &&
177+
(snippet_name === 'failed' || snippet_name === 'pending')))
178+
) {
179+
if (
180+
is_component_node(grand_parent)
181+
? grand_parent.metadata.scopes.default === binding.scope
182+
: context.state.scopes.get(parent) === binding.scope
183+
) {
184+
e.const_tag_invalid_reference(node, node.name);
185+
} else {
186+
break;
187+
}
188+
}
189+
}
190+
}
158191
}
159192
}

packages/svelte/src/compiler/phases/3-transform/client/transform-client.js

Lines changed: 24 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -359,16 +359,34 @@ export function client_component(analysis, options) {
359359
if (dev) push_args.push(b.id(analysis.name));
360360

361361
let component_block = b.block([
362+
store_init,
362363
...store_setup,
363364
...legacy_reactive_declarations,
364-
...group_binding_declarations,
365-
...state.instance_level_snippets,
366-
.../** @type {ESTree.Statement[]} */ (instance.body),
367-
analysis.runes || !analysis.needs_context
368-
? b.empty
369-
: b.stmt(b.call('$.init', analysis.immutable ? b.true : undefined))
365+
...group_binding_declarations
370366
]);
371367

368+
if (analysis.instance.has_await) {
369+
const body = b.block([
370+
...state.instance_level_snippets,
371+
.../** @type {ESTree.Statement[]} */ (instance.body),
372+
b.if(b.call('$.aborted'), b.return()),
373+
.../** @type {ESTree.Statement[]} */ (template.body)
374+
]);
375+
376+
component_block.body.push(b.stmt(b.call(`$.async_body`, b.arrow([], body, true))));
377+
} else {
378+
component_block.body.push(
379+
...state.instance_level_snippets,
380+
.../** @type {ESTree.Statement[]} */ (instance.body)
381+
);
382+
383+
if (!analysis.runes && analysis.needs_context) {
384+
component_block.body.push(b.stmt(b.call('$.init', analysis.immutable ? b.true : undefined)));
385+
}
386+
387+
component_block.body.push(.../** @type {ESTree.Statement[]} */ (template.body));
388+
}
389+
372390
if (analysis.needs_mutation_validation) {
373391
component_block.body.unshift(
374392
b.var('$$ownership_validator', b.call('$.create_ownership_validator', b.id('$$props')))
@@ -389,41 +407,6 @@ export function client_component(analysis, options) {
389407
analysis.uses_slots ||
390408
analysis.slot_names.size > 0;
391409

392-
if (analysis.instance.has_await) {
393-
const params = [b.id('$$anchor')];
394-
if (should_inject_props) {
395-
params.push(b.id('$$props'));
396-
}
397-
if (store_setup.length > 0) {
398-
params.push(b.id('$$stores'));
399-
}
400-
const body = b.function_declaration(
401-
b.id('$$body'),
402-
params,
403-
b.block([
404-
b.var('$$unsuspend', b.call('$.suspend')),
405-
...component_block.body,
406-
b.if(b.call('$.aborted'), b.return()),
407-
.../** @type {ESTree.Statement[]} */ (template.body),
408-
b.stmt(b.call('$$unsuspend'))
409-
]),
410-
true
411-
);
412-
413-
state.hoisted.push(body);
414-
415-
component_block = b.block([
416-
b.var('fragment', b.call('$.comment')),
417-
b.var('node', b.call('$.first_child', b.id('fragment'))),
418-
store_init,
419-
b.stmt(b.call(body.id, b.id('node'), ...params.slice(1))),
420-
b.stmt(b.call('$.append', b.id('$$anchor'), b.id('fragment')))
421-
]);
422-
} else {
423-
component_block.body.unshift(store_init);
424-
component_block.body.push(.../** @type {ESTree.Statement[]} */ (template.body));
425-
}
426-
427410
// trick esrap into including comments
428411
component_block.loc = instance.loc;
429412

packages/svelte/src/compiler/phases/3-transform/client/visitors/ForOfStatement.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@ import { dev, is_ignored } from '../../../../state.js';
88
* @param {ComponentContext} context
99
*/
1010
export function ForOfStatement(node, context) {
11-
if (node.await && dev && !is_ignored(node, 'await_reactivity_loss')) {
11+
if (
12+
node.await &&
13+
dev &&
14+
!is_ignored(node, 'await_reactivity_loss') &&
15+
context.state.options.experimental.async
16+
) {
1217
const left = /** @type {VariableDeclaration | Pattern} */ (context.visit(node.left));
1318
const argument = /** @type {Expression} */ (context.visit(node.right));
1419
const body = /** @type {Statement} */ (context.visit(node.body));

0 commit comments

Comments
 (0)