diff --git a/.changeset/calm-derived-connections.md b/.changeset/calm-derived-connections.md new file mode 100644 index 0000000000..4ce38365ff --- /dev/null +++ b/.changeset/calm-derived-connections.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent untracked derived reads from retaining disconnected dependencies diff --git a/.changeset/hydration-custom-element-error-boundary.md b/.changeset/hydration-custom-element-error-boundary.md new file mode 100644 index 0000000000..f94aa6265a --- /dev/null +++ b/.changeset/hydration-custom-element-error-boundary.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: restore hydration state when custom element attribute updates throw diff --git a/.changeset/major-papayas-run.md b/.changeset/major-papayas-run.md new file mode 100644 index 0000000000..59909fb448 --- /dev/null +++ b/.changeset/major-papayas-run.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: flush anything pending before invoking flushSync callback function diff --git a/.changeset/major-roses-help.md b/.changeset/major-roses-help.md new file mode 100644 index 0000000000..c3c06792a4 --- /dev/null +++ b/.changeset/major-roses-help.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: preserve location information for `await` wrappers diff --git a/.changeset/migrate-keep-assignment.md b/.changeset/migrate-keep-assignment.md new file mode 100644 index 0000000000..6b5be9d6be --- /dev/null +++ b/.changeset/migrate-keep-assignment.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: keep a reactive assignment with no dependencies when `migrate` cannot turn it into a declaration diff --git a/.changeset/migrate-tag-line.md b/.changeset/migrate-tag-line.md new file mode 100644 index 0000000000..414e4a249c --- /dev/null +++ b/.changeset/migrate-tag-line.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't hang `migrate` on a declaration that shares a line with its script tag diff --git a/.changeset/quiet-areas-link.md b/.changeset/quiet-areas-link.md new file mode 100644 index 0000000000..af4e31f3cb --- /dev/null +++ b/.changeset/quiet-areas-link.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't warn about a redundant `link` role on `` elements without an `href` diff --git a/.changeset/short-radios-sneeze.md b/.changeset/short-radios-sneeze.md new file mode 100644 index 0000000000..847c3eef17 --- /dev/null +++ b/.changeset/short-radios-sneeze.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: read batch-local array on each-block commit diff --git a/.changeset/snippet-parameter-redeclared.md b/.changeset/snippet-parameter-redeclared.md new file mode 100644 index 0000000000..9bb14cf3af --- /dev/null +++ b/.changeset/snippet-parameter-redeclared.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: error at compile time when a declaration in a snippet redeclares one of its parameters diff --git a/.changeset/sour-lets-hide.md b/.changeset/sour-lets-hide.md new file mode 100644 index 0000000000..6175027cff --- /dev/null +++ b/.changeset/sour-lets-hide.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: error when using `let:` directives on a component with a `children` snippet diff --git a/.changeset/stale-boundary-reset.md b/.changeset/stale-boundary-reset.md new file mode 100644 index 0000000000..1f158c5713 --- /dev/null +++ b/.changeset/stale-boundary-reset.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ignore stale boundary reset callbacks diff --git a/.changeset/tidy-ducks-reset.md b/.changeset/tidy-ducks-reset.md new file mode 100644 index 0000000000..41a00cf649 --- /dev/null +++ b/.changeset/tidy-ducks-reset.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: handle deferred transitions aborted before initialization diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index 2ec8985c66..8bf17f9330 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -683,6 +683,12 @@ Cannot use `$$restProps` in runes mode `let:` directive at invalid position ``` +### let_directive_snippet_conflict + +``` +Cannot use `let:` directives on a component that has a `children` snippet. Use snippet parameters instead (e.g. `{#snippet children({ %name% })}`) +``` + ### mixed_event_handler_syntaxes ``` diff --git a/packages/svelte/messages/compile-errors/template.md b/packages/svelte/messages/compile-errors/template.md index 7fdb56ce55..797b2c53c2 100644 --- a/packages/svelte/messages/compile-errors/template.md +++ b/packages/svelte/messages/compile-errors/template.md @@ -283,6 +283,10 @@ The same applies to components: > `let:` directive at invalid position +## let_directive_snippet_conflict + +> Cannot use `let:` directives on a component that has a `children` snippet. Use snippet parameters instead (e.g. `{#snippet children({ %name% })}`) + ## mixed_event_handler_syntaxes > Mixing old (on:%name%) and new syntaxes for event handling is not allowed. Use only the on%name% syntax diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index f43e8cae63..98f1e79279 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -1223,6 +1223,16 @@ export function let_directive_invalid_placement(node) { e(node, 'let_directive_invalid_placement', `\`let:\` directive at invalid position\nhttps://svelte.dev/e/let_directive_invalid_placement`); } +/** + * Cannot use `let:` directives on a component that has a `children` snippet. Use snippet parameters instead (e.g. `{#snippet children({ %name% })}`) + * @param {null | number | NodeLike} node + * @param {string} name + * @returns {never} + */ +export function let_directive_snippet_conflict(node, name) { + e(node, 'let_directive_snippet_conflict', `Cannot use \`let:\` directives on a component that has a \`children\` snippet. Use snippet parameters instead (e.g. \`{#snippet children({ ${name} })}\`)\nhttps://svelte.dev/e/let_directive_snippet_conflict`); +} + /** * Mixing old (on:%name%) and new syntaxes for event handling is not allowed. Use only the on%name% syntax * @param {null | number | NodeLike} node diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 76c061a275..ac667bf26b 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -911,8 +911,10 @@ const instance_script = { start = /** @type {number} */ (parent.start); end = /** @type {number} */ (parent.end); } - while (state.str.original[start] !== '\n') start--; - while (state.str.original[end] !== '\n') end++; + // remove the declaration's whole line, or what there is of it inside the script + const script = /** @type {{ start: number; end: number }} */ (state.analysis.instance.ast); + while (start > script.start && state.str.original[start] !== '\n') start--; + while (end < script.end && state.str.original[end] !== '\n') end++; state.str.update(start, end, ''); } }, @@ -988,8 +990,11 @@ const instance_script = { return; } + let declared = 0; for (const binding of bindings) { - if (binding.reassigned && (ids.includes(binding.node) || expression_ids.length === 0)) { + // a binding this statement declares needs its `let` whether or not it is reassigned + if (ids.includes(binding.node) || (binding.reassigned && expression_ids.length === 0)) { + declared++; check_rune_binding('state'); const init = binding.kind === 'state' @@ -1005,7 +1010,13 @@ const instance_script = { } } - if (expression_ids.length === 0 && bindings.every((b) => b.kind !== 'store_sub')) { + // the statement goes only once every target has a declaration standing in for it + if ( + expression_ids.length === 0 && + bindings.length > 0 && + declared === bindings.length && + bindings.every((b) => b.kind !== 'store_sub') + ) { state.str.remove(/** @type {number} */ (node.start), /** @type {number} */ (node.end)); return; } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/LetDirective.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/LetDirective.js index e9a56ead0c..2eccac9d8a 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/LetDirective.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/LetDirective.js @@ -1,6 +1,7 @@ /** @import { AST } from '#compiler' */ /** @import { Context } from '../types' */ import * as e from '../../../errors.js'; +import { determine_slot } from '../../../utils/slot.js'; /** * @param {AST.LetDirective} node @@ -21,4 +22,25 @@ export function LetDirective(node, context) { ) { e.let_directive_invalid_placement(node); } + + // an explicit `children` snippet replaces the default slot content, so the `let:` + // directive would never be provided. if the component has a `slot` attribute, the + // directive applies to the component itself instead, which is fine + if ( + (parent.type === 'Component' || + parent.type === 'SvelteComponent' || + parent.type === 'SvelteSelf') && + !determine_slot(parent) && + parent.fragment.nodes.some( + (child) => child.type === 'SnippetBlock' && child.expression.name === 'children' + ) + ) { + const { expression } = node; + const pattern = + expression === null || (expression.type === 'Identifier' && expression.name === node.name) + ? node.name + : `${node.name}: ${context.state.analysis.source.slice(expression.start, expression.end)}`; + + e.let_directive_snippet_conflict(node, pattern); + } } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js index f06a41d088..c38b03bf69 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y/index.js @@ -173,8 +173,8 @@ export function check_element(node, context) { current_role === get_implicit_role(node.name, attribute_map) && //