From 699dc6e4f7bb9c47d19a5d7200d57bd06b6c8c09 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Mon, 16 Dec 2024 20:05:08 +0000 Subject: [PATCH 0001/1429] fix: improve unowned derived performance (#14724) * fix: improve unowned derived performance * fix: improve unowned derived performance * changeset --- .changeset/many-worms-attend.md | 5 +++++ packages/svelte/src/internal/client/runtime.js | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .changeset/many-worms-attend.md diff --git a/.changeset/many-worms-attend.md b/.changeset/many-worms-attend.md new file mode 100644 index 0000000000..0da3699967 --- /dev/null +++ b/.changeset/many-worms-attend.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve unowned derived performance diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index ff8eaa8ef9..27e174d3aa 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -230,8 +230,9 @@ export function check_dirtiness(reaction) { } } - // Unowned signals should never be marked as clean. - if (!is_unowned) { + // Unowned signals should never be marked as clean unless they + // are used within an active_effect without skip_reaction + if (!is_unowned || (active_effect !== null && !skip_reaction)) { set_signal_status(reaction, CLEAN); } } From e0e999055d1e019a928947dbcb837f6df495e51d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:13:46 +0000 Subject: [PATCH 0002/1429] Version Packages (#14728) Co-authored-by: github-actions[bot] --- .changeset/many-worms-attend.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/many-worms-attend.md diff --git a/.changeset/many-worms-attend.md b/.changeset/many-worms-attend.md deleted file mode 100644 index 0da3699967..0000000000 --- a/.changeset/many-worms-attend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: improve unowned derived performance diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 09e21aec84..598d75ca9b 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.14.1 + +### Patch Changes + +- fix: improve unowned derived performance ([#14724](https://github.com/sveltejs/svelte/pull/14724)) + ## 5.14.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 27e78e681e..db861eced2 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.14.0", + "version": "5.14.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 4d4d7e6e86..78ec824169 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.14.0'; +export const VERSION = '5.14.1'; export const PUBLIC_VERSION = '5'; From 36d73cad55cacd6f2db43c6c83186005cd1da072 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 17 Dec 2024 16:14:10 +0100 Subject: [PATCH 0003/1429] fix: correctly highlight first rerun of `$inspect.trace` (#14734) --- .changeset/tame-eggs-work.md | 5 ++ .../svelte/src/internal/client/runtime.js | 4 +- .../samples/inspect-trace/_config.js | 64 +++++++++++++++++-- .../samples/inspect-trace/main.svelte | 6 +- 4 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 .changeset/tame-eggs-work.md diff --git a/.changeset/tame-eggs-work.md b/.changeset/tame-eggs-work.md new file mode 100644 index 0000000000..2c1c59de4a --- /dev/null +++ b/.changeset/tame-eggs-work.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly highlight first rerun of `$inspect.trace` diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 27e174d3aa..4aa3d17a15 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -127,8 +127,8 @@ export function set_untracked_writes(value) { untracked_writes = value; } -/** @type {number} Used by sources and deriveds for handling updates to unowned deriveds */ -let current_version = 0; +/** @type {number} Used by sources and deriveds for handling updates to unowned deriveds it starts from 1 to differentiate between a created effect and a run one for tracing */ +let current_version = 1; // If we are working with a get() chain that has no active container, // to prevent memory leaks, we skip adding the reaction. diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-trace/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-trace/_config.js index 517e11d74e..efa5985e4e 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-trace/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-trace/_config.js @@ -11,12 +11,15 @@ function normalise_trace_logs(logs) { if (typeof log === 'string' && log.includes('%c')) { const split = log.split('%c'); - normalised.push((split[0].length !== 0 ? split[0] : split[1]).trim()); + normalised.push({ + log: (split[0].length !== 0 ? split[0] : split[1]).trim(), + highlighted: logs[i + 1] === 'color: CornflowerBlue; font-weight: bold' + }); i++; } else if (log instanceof Error) { continue; } else { - normalised.push(log); + normalised.push({ log }); } } return normalised; @@ -28,7 +31,17 @@ export default test({ }, test({ assert, target, logs }) { - assert.deepEqual(normalise_trace_logs(logs), ['effect', '$derived', 0, '$state', 0]); + // initial log, everything is highlighted + + assert.deepEqual(normalise_trace_logs(logs), [ + { log: 'effect', highlighted: false }, + { log: '$derived', highlighted: true }, + { log: 0 }, + { log: '$state', highlighted: true }, + { log: 0 }, + { log: '$state', highlighted: true }, + { log: false } + ]); logs.length = 0; @@ -36,6 +49,49 @@ export default test({ button?.click(); flushSync(); - assert.deepEqual(normalise_trace_logs(logs), ['effect', '$derived', 2, '$state', 1]); + // count changed, derived and state are highlighted, last state is not + + assert.deepEqual(normalise_trace_logs(logs), [ + { log: 'effect', highlighted: false }, + { log: '$derived', highlighted: true }, + { log: 2 }, + { log: '$state', highlighted: true }, + { log: 1 }, + { log: '$state', highlighted: false }, + { log: false } + ]); + + logs.length = 0; + + const input = target.querySelector('input'); + input?.click(); + flushSync(); + + // checked changed, last state is highlighted, first two are not + + assert.deepEqual(normalise_trace_logs(logs), [ + { log: 'effect', highlighted: false }, + { log: '$derived', highlighted: false }, + { log: 2 }, + { log: '$state', highlighted: false }, + { log: 1 }, + { log: '$state', highlighted: true }, + { log: true } + ]); + + logs.length = 0; + + button?.click(); + flushSync(); + + // count change and derived it's >=4, checked is not in the dependencies anymore + + assert.deepEqual(normalise_trace_logs(logs), [ + { log: 'effect', highlighted: false }, + { log: '$derived', highlighted: true }, + { log: 4 }, + { log: '$state', highlighted: true }, + { log: 2 } + ]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-trace/main.svelte b/packages/svelte/tests/runtime-runes/samples/inspect-trace/main.svelte index 99c30a07bf..99f246aa73 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-trace/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/inspect-trace/main.svelte @@ -2,11 +2,15 @@ let count = $state(0); let double = $derived(count * 2); + let checked = $state(false); + $effect(() => { $inspect.trace('effect'); double; - }) + double >= 4 || checked; + }); + From 3146e93bbb34fff06dad9c87c9fd53b3865896f7 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 17 Dec 2024 16:53:33 +0100 Subject: [PATCH 0004/1429] chore: loose parser improvements (#14733) * handle invalid expression inside each key * handle invalid expression inside each expression * handle invalid expression inside await block * handle "in the middle of typing" components with starting lowercase and dot at the end * changeset --- .changeset/tiny-kings-serve.md | 5 + .../phases/1-parse/read/expression.js | 5 +- .../compiler/phases/1-parse/state/element.js | 9 +- .../src/compiler/phases/1-parse/state/tag.js | 55 +++- .../compiler/phases/1-parse/utils/bracket.js | 5 + .../loose-invalid-expression/input.svelte | 12 + .../loose-invalid-expression/output.json | 268 +++++++++++++++++- .../samples/loose-unclosed-tag/input.svelte | 8 + .../samples/loose-unclosed-tag/output.json | 146 +++++++--- .../loose-invalid-expression/input.svelte | 12 + .../loose-invalid-expression/output.json | 234 ++++++++++++++- .../samples/loose-unclosed-tag/input.svelte | 8 + .../samples/loose-unclosed-tag/output.json | 168 ++++++++--- 13 files changed, 836 insertions(+), 99 deletions(-) create mode 100644 .changeset/tiny-kings-serve.md diff --git a/.changeset/tiny-kings-serve.md b/.changeset/tiny-kings-serve.md new file mode 100644 index 0000000000..b883767132 --- /dev/null +++ b/.changeset/tiny-kings-serve.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: more loose parser improvements diff --git a/packages/svelte/src/compiler/phases/1-parse/read/expression.js b/packages/svelte/src/compiler/phases/1-parse/read/expression.js index 021bd95a6e..907608b11b 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/expression.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/expression.js @@ -7,9 +7,10 @@ import { find_matching_bracket } from '../utils/bracket.js'; /** * @param {Parser} parser + * @param {string} [opening_token] * @returns {Expression} */ -export default function read_expression(parser) { +export default function read_expression(parser, opening_token) { try { const node = parse_expression_at(parser.template, parser.ts, parser.index); @@ -42,7 +43,7 @@ export default function read_expression(parser) { } catch (err) { if (parser.loose) { // Find the next } and treat it as the end of the expression - const end = find_matching_bracket(parser.template, parser.index, '{'); + const end = find_matching_bracket(parser.template, parser.index, opening_token ?? '{'); if (end) { const start = parser.index; parser.index = end; diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js index c112cf6789..66946a8f8d 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -123,8 +123,11 @@ export default function element(parser) { } if (!regex_valid_element_name.test(name) && !regex_valid_component_name.test(name)) { - const bounds = { start: start + 1, end: start + 1 + name.length }; - e.tag_invalid_name(bounds); + // in the middle of typing -> allow in loose mode + if (!parser.loose || !name.endsWith('.')) { + const bounds = { start: start + 1, end: start + 1 + name.length }; + e.tag_invalid_name(bounds); + } } if (root_only_meta_tags.has(name)) { @@ -141,7 +144,7 @@ export default function element(parser) { const type = meta_tags.has(name) ? meta_tags.get(name) - : regex_valid_component_name.test(name) + : regex_valid_component_name.test(name) || (parser.loose && name.endsWith('.')) ? 'Component' : name === 'title' && parent_is_head(parser.stack) ? 'TitleElement' diff --git a/packages/svelte/src/compiler/phases/1-parse/state/tag.js b/packages/svelte/src/compiler/phases/1-parse/state/tag.js index 423ada792c..7996d64ded 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -174,13 +174,30 @@ function open(parser) { if (parser.eat('(')) { parser.allow_whitespace(); - key = read_expression(parser); + key = read_expression(parser, '('); parser.allow_whitespace(); parser.eat(')', true); parser.allow_whitespace(); } - parser.eat('}', true); + const matches = parser.eat('}', true, false); + + if (!matches) { + // Parser may have read the `as` as part of the expression (e.g. in `{#each foo. as x}`) + if (parser.template.slice(parser.index - 4, parser.index) === ' as ') { + const prev_index = parser.index; + context = read_pattern(parser); + parser.eat('}', true); + expression = { + type: 'Identifier', + name: '', + start: expression.start, + end: prev_index - 4 + }; + } else { + parser.eat('}', true); // rerun to produce the parser error + } + } /** @type {AST.EachBlock} */ const block = parser.append({ @@ -246,7 +263,39 @@ function open(parser) { parser.fragments.push(block.pending); } - parser.eat('}', true); + const matches = parser.eat('}', true, false); + + // Parser may have read the `then/catch` as part of the expression (e.g. in `{#await foo. then x}`) + if (!matches) { + if (parser.template.slice(parser.index - 6, parser.index) === ' then ') { + const prev_index = parser.index; + block.value = read_pattern(parser); + parser.eat('}', true); + block.expression = { + type: 'Identifier', + name: '', + start: expression.start, + end: prev_index - 6 + }; + block.then = block.pending; + block.pending = null; + } else if (parser.template.slice(parser.index - 7, parser.index) === ' catch ') { + const prev_index = parser.index; + block.error = read_pattern(parser); + parser.eat('}', true); + block.expression = { + type: 'Identifier', + name: '', + start: expression.start, + end: prev_index - 7 + }; + block.catch = block.pending; + block.pending = null; + } else { + parser.eat('}', true); // rerun to produce the parser error + } + } + parser.stack.push(block); return; diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js b/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js index 4e02f06de6..e7576af955 100644 --- a/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js +++ b/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js @@ -4,6 +4,8 @@ const SQUARE_BRACKET_OPEN = '['.charCodeAt(0); const SQUARE_BRACKET_CLOSE = ']'.charCodeAt(0); const CURLY_BRACKET_OPEN = '{'.charCodeAt(0); const CURLY_BRACKET_CLOSE = '}'.charCodeAt(0); +const PARENTHESES_OPEN = '('.charCodeAt(0); +const PARENTHESES_CLOSE = ')'.charCodeAt(0); /** @param {number} code */ export function is_bracket_open(code) { @@ -34,6 +36,9 @@ export function get_bracket_close(open) { if (open === CURLY_BRACKET_OPEN) { return CURLY_BRACKET_CLOSE; } + if (open === PARENTHESES_OPEN) { + return PARENTHESES_CLOSE; + } } /** diff --git a/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/input.svelte b/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/input.svelte index a0977b9a63..b64f15c5c1 100644 --- a/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/input.svelte +++ b/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/input.svelte @@ -9,3 +9,15 @@ asd{a.}asd {foo[bar.]} + +{#if x.}{/if} + +{#each array as item (item.)}{/each} + +{#each obj. as item}{/each} + +{#await x.}{/await} + +{#await x. then y}{/await} + +{#await x. catch y}{/await} diff --git a/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/output.json b/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/output.json index d27b6cd914..0564d6d295 100644 --- a/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/output.json +++ b/packages/svelte/tests/parser-legacy/samples/loose-invalid-expression/output.json @@ -2,7 +2,7 @@ "html": { "type": "Fragment", "start": 0, - "end": 164, + "end": 324, "children": [ { "type": "Element", @@ -236,6 +236,272 @@ "end": 163, "name": "" } + }, + { + "type": "Text", + "start": 164, + "end": 166, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "IfBlock", + "start": 166, + "end": 179, + "expression": { + "type": "Identifier", + "start": 171, + "end": 173, + "name": "" + }, + "children": [] + }, + { + "type": "Text", + "start": 179, + "end": 181, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 181, + "end": 217, + "children": [], + "context": { + "type": "Identifier", + "name": "item", + "start": 197, + "loc": { + "start": { + "line": 15, + "column": 16, + "character": 197 + }, + "end": { + "line": 15, + "column": 20, + "character": 201 + } + }, + "end": 201 + }, + "expression": { + "type": "Identifier", + "start": 188, + "end": 193, + "loc": { + "start": { + "line": 15, + "column": 7 + }, + "end": { + "line": 15, + "column": 12 + } + }, + "name": "array" + }, + "key": { + "type": "Identifier", + "start": 203, + "end": 208, + "name": "" + } + }, + { + "type": "Text", + "start": 217, + "end": 219, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 219, + "end": 246, + "children": [], + "context": { + "type": "Identifier", + "name": "item", + "start": 234, + "loc": { + "start": { + "line": 17, + "column": 15, + "character": 234 + }, + "end": { + "line": 17, + "column": 19, + "character": 238 + } + }, + "end": 238 + }, + "expression": { + "type": "Identifier", + "name": "", + "start": 226, + "end": 230 + } + }, + { + "type": "Text", + "start": 246, + "end": 248, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "AwaitBlock", + "start": 248, + "end": 267, + "expression": { + "type": "Identifier", + "start": 256, + "end": 258, + "name": "" + }, + "value": null, + "error": null, + "pending": { + "type": "PendingBlock", + "start": 259, + "end": 259, + "children": [], + "skip": false + }, + "then": { + "type": "ThenBlock", + "start": null, + "end": null, + "children": [], + "skip": true + }, + "catch": { + "type": "CatchBlock", + "start": null, + "end": null, + "children": [], + "skip": true + } + }, + { + "type": "Text", + "start": 267, + "end": 269, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "AwaitBlock", + "start": 269, + "end": 295, + "expression": { + "type": "Identifier", + "name": "", + "start": 277, + "end": 279 + }, + "value": { + "type": "Identifier", + "name": "y", + "start": 285, + "loc": { + "start": { + "line": 21, + "column": 16, + "character": 285 + }, + "end": { + "line": 21, + "column": 17, + "character": 286 + } + }, + "end": 286 + }, + "error": null, + "pending": { + "type": "PendingBlock", + "start": null, + "end": null, + "children": [], + "skip": true + }, + "then": { + "type": "ThenBlock", + "start": 287, + "end": 267, + "children": [], + "skip": false + }, + "catch": { + "type": "CatchBlock", + "start": null, + "end": null, + "children": [], + "skip": true + } + }, + { + "type": "Text", + "start": 295, + "end": 297, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "AwaitBlock", + "start": 297, + "end": 324, + "expression": { + "type": "Identifier", + "name": "", + "start": 305, + "end": 307 + }, + "value": null, + "error": { + "type": "Identifier", + "name": "y", + "start": 314, + "loc": { + "start": { + "line": 23, + "column": 17, + "character": 314 + }, + "end": { + "line": 23, + "column": 18, + "character": 315 + } + }, + "end": 315 + }, + "pending": { + "type": "PendingBlock", + "start": null, + "end": null, + "children": [], + "skip": true + }, + "then": { + "type": "ThenBlock", + "start": null, + "end": null, + "children": [], + "skip": true + }, + "catch": { + "type": "CatchBlock", + "start": 316, + "end": 295, + "children": [], + "skip": false + } } ] } diff --git a/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/input.svelte b/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/input.svelte index 10a3876b7c..83017c79aa 100644 --- a/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/input.svelte +++ b/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/input.svelte @@ -10,6 +10,14 @@ +
+ + +
+ + {#if foo}
{/if} diff --git a/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/output.json b/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/output.json index d91cc68098..2205a00e20 100644 --- a/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/output.json +++ b/packages/svelte/tests/parser-legacy/samples/loose-unclosed-tag/output.json @@ -2,7 +2,7 @@ "html": { "type": "Fragment", "start": 0, - "end": 160, + "end": 204, "children": [ { "type": "Element", @@ -136,20 +136,82 @@ "data": "\n\n" }, { - "type": "IfBlock", + "type": "Element", "start": 74, + "end": 94, + "name": "div", + "attributes": [], + "children": [ + { + "type": "Text", + "start": 79, + "end": 81, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "InlineComponent", + "start": 81, + "end": 88, + "name": "Comp.", + "attributes": [], + "children": [] + } + ] + }, + { + "type": "Text", + "start": 94, "end": 96, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "Element", + "start": 96, + "end": 116, + "name": "div", + "attributes": [], + "children": [ + { + "type": "Text", + "start": 101, + "end": 103, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "InlineComponent", + "start": 103, + "end": 110, + "name": "comp.", + "attributes": [], + "children": [] + } + ] + }, + { + "type": "Text", + "start": 116, + "end": 118, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "IfBlock", + "start": 118, + "end": 140, "expression": { "type": "Identifier", - "start": 79, - "end": 82, + "start": 123, + "end": 126, "loc": { "start": { - "line": 13, + "line": 21, "column": 5 }, "end": { - "line": 13, + "line": 21, "column": 8 } }, @@ -158,15 +220,15 @@ "children": [ { "type": "Element", - "start": 85, - "end": 91, + "start": 129, + "end": 135, "name": "div", "attributes": [], "children": [ { "type": "Text", - "start": 90, - "end": 91, + "start": 134, + "end": 135, "raw": "\n", "data": "\n" } @@ -176,26 +238,26 @@ }, { "type": "Text", - "start": 96, - "end": 98, + "start": 140, + "end": 142, "raw": "\n\n", "data": "\n\n" }, { "type": "IfBlock", - "start": 98, - "end": 130, + "start": 142, + "end": 174, "expression": { "type": "Identifier", - "start": 103, - "end": 106, + "start": 147, + "end": 150, "loc": { "start": { - "line": 17, + "line": 25, "column": 5 }, "end": { - "line": 17, + "line": 25, "column": 8 } }, @@ -204,31 +266,31 @@ "children": [ { "type": "InlineComponent", - "start": 109, - "end": 125, + "start": 153, + "end": 169, "name": "Comp", "attributes": [ { "type": "Attribute", - "start": 115, - "end": 124, + "start": 159, + "end": 168, "name": "foo", "value": [ { "type": "MustacheTag", - "start": 119, - "end": 124, + "start": 163, + "end": 168, "expression": { "type": "Identifier", - "start": 120, - "end": 123, + "start": 164, + "end": 167, "loc": { "start": { - "line": 18, + "line": 26, "column": 12 }, "end": { - "line": 18, + "line": 26, "column": 15 } }, @@ -244,36 +306,36 @@ }, { "type": "Text", - "start": 130, - "end": 132, + "start": 174, + "end": 176, "raw": "\n\n", "data": "\n\n" }, { "type": "Element", - "start": 132, - "end": 160, + "start": 176, + "end": 204, "name": "div", "attributes": [], "children": [ { "type": "Text", - "start": 137, - "end": 138, + "start": 181, + "end": 182, "raw": "\n", "data": "\n" }, { "type": "Element", - "start": 138, - "end": 147, + "start": 182, + "end": 191, "name": "p", "attributes": [], "children": [ { "type": "Text", - "start": 141, - "end": 143, + "start": 185, + "end": 187, "raw": "hi", "data": "hi" } @@ -281,15 +343,15 @@ }, { "type": "Text", - "start": 147, - "end": 149, + "start": 191, + "end": 193, "raw": "\n\n", "data": "\n\n" }, { "type": "Element", - "start": 149, - "end": 160, + "start": 193, + "end": 204, "name": "open-ended", "attributes": [], "children": [] diff --git a/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/input.svelte b/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/input.svelte index a0977b9a63..b64f15c5c1 100644 --- a/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/input.svelte +++ b/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/input.svelte @@ -9,3 +9,15 @@ asd{a.}asd {foo[bar.]} + +{#if x.}{/if} + +{#each array as item (item.)}{/each} + +{#each obj. as item}{/each} + +{#await x.}{/await} + +{#await x. then y}{/await} + +{#await x. catch y}{/await} diff --git a/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/output.json b/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/output.json index cdb7f66c58..56fa4286dd 100644 --- a/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/output.json +++ b/packages/svelte/tests/parser-modern/samples/loose-invalid-expression/output.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 164, + "end": 324, "type": "Root", "fragment": { "type": "Fragment", @@ -247,6 +247,238 @@ "end": 163, "name": "" } + }, + { + "type": "Text", + "start": 164, + "end": 166, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "IfBlock", + "elseif": false, + "start": 166, + "end": 179, + "test": { + "type": "Identifier", + "start": 171, + "end": 173, + "name": "" + }, + "consequent": { + "type": "Fragment", + "nodes": [] + }, + "alternate": null + }, + { + "type": "Text", + "start": 179, + "end": 181, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 181, + "end": 217, + "expression": { + "type": "Identifier", + "start": 188, + "end": 193, + "loc": { + "start": { + "line": 15, + "column": 7 + }, + "end": { + "line": 15, + "column": 12 + } + }, + "name": "array" + }, + "body": { + "type": "Fragment", + "nodes": [] + }, + "context": { + "type": "Identifier", + "name": "item", + "start": 197, + "loc": { + "start": { + "line": 15, + "column": 16, + "character": 197 + }, + "end": { + "line": 15, + "column": 20, + "character": 201 + } + }, + "end": 201 + }, + "key": { + "type": "Identifier", + "start": 203, + "end": 208, + "name": "" + } + }, + { + "type": "Text", + "start": 217, + "end": 219, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 219, + "end": 246, + "expression": { + "type": "Identifier", + "name": "", + "start": 226, + "end": 230 + }, + "body": { + "type": "Fragment", + "nodes": [] + }, + "context": { + "type": "Identifier", + "name": "item", + "start": 234, + "loc": { + "start": { + "line": 17, + "column": 15, + "character": 234 + }, + "end": { + "line": 17, + "column": 19, + "character": 238 + } + }, + "end": 238 + } + }, + { + "type": "Text", + "start": 246, + "end": 248, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "AwaitBlock", + "start": 248, + "end": 267, + "expression": { + "type": "Identifier", + "start": 256, + "end": 258, + "name": "" + }, + "value": null, + "error": null, + "pending": { + "type": "Fragment", + "nodes": [] + }, + "then": null, + "catch": null + }, + { + "type": "Text", + "start": 267, + "end": 269, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "AwaitBlock", + "start": 269, + "end": 295, + "expression": { + "type": "Identifier", + "name": "", + "start": 277, + "end": 279 + }, + "value": { + "type": "Identifier", + "name": "y", + "start": 285, + "loc": { + "start": { + "line": 21, + "column": 16, + "character": 285 + }, + "end": { + "line": 21, + "column": 17, + "character": 286 + } + }, + "end": 286 + }, + "error": null, + "pending": null, + "then": { + "type": "Fragment", + "nodes": [] + }, + "catch": null + }, + { + "type": "Text", + "start": 295, + "end": 297, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "AwaitBlock", + "start": 297, + "end": 324, + "expression": { + "type": "Identifier", + "name": "", + "start": 305, + "end": 307 + }, + "value": null, + "error": { + "type": "Identifier", + "name": "y", + "start": 314, + "loc": { + "start": { + "line": 23, + "column": 17, + "character": 314 + }, + "end": { + "line": 23, + "column": 18, + "character": 315 + } + }, + "end": 315 + }, + "pending": null, + "then": null, + "catch": { + "type": "Fragment", + "nodes": [] + } } ] }, diff --git a/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/input.svelte b/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/input.svelte index 10a3876b7c..83017c79aa 100644 --- a/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/input.svelte +++ b/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/input.svelte @@ -10,6 +10,14 @@ +
+ + +
+ + {#if foo}
{/if} diff --git a/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/output.json b/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/output.json index ec47bfc738..cf9138c026 100644 --- a/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/output.json +++ b/packages/svelte/tests/parser-modern/samples/loose-unclosed-tag/output.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 160, + "end": 204, "type": "Root", "fragment": { "type": "Fragment", @@ -155,21 +155,95 @@ "data": "\n\n" }, { - "type": "IfBlock", - "elseif": false, + "type": "RegularElement", "start": 74, + "end": 94, + "name": "div", + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 79, + "end": 81, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 81, + "end": 88, + "name": "Comp.", + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + } + ] + } + }, + { + "type": "Text", + "start": 94, "end": 96, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "RegularElement", + "start": 96, + "end": 116, + "name": "div", + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 101, + "end": 103, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "Component", + "start": 103, + "end": 110, + "name": "comp.", + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [] + } + } + ] + } + }, + { + "type": "Text", + "start": 116, + "end": 118, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "IfBlock", + "elseif": false, + "start": 118, + "end": 140, "test": { "type": "Identifier", - "start": 79, - "end": 82, + "start": 123, + "end": 126, "loc": { "start": { - "line": 13, + "line": 21, "column": 5 }, "end": { - "line": 13, + "line": 21, "column": 8 } }, @@ -180,15 +254,15 @@ "nodes": [ { "type": "Text", - "start": 83, - "end": 85, + "start": 127, + "end": 129, "raw": "\n\t", "data": "\n\t" }, { "type": "RegularElement", - "start": 85, - "end": 91, + "start": 129, + "end": 135, "name": "div", "attributes": [], "fragment": { @@ -196,8 +270,8 @@ "nodes": [ { "type": "Text", - "start": 90, - "end": 91, + "start": 134, + "end": 135, "raw": "\n", "data": "\n" } @@ -210,27 +284,27 @@ }, { "type": "Text", - "start": 96, - "end": 98, + "start": 140, + "end": 142, "raw": "\n\n", "data": "\n\n" }, { "type": "IfBlock", "elseif": false, - "start": 98, - "end": 130, + "start": 142, + "end": 174, "test": { "type": "Identifier", - "start": 103, - "end": 106, + "start": 147, + "end": 150, "loc": { "start": { - "line": 17, + "line": 25, "column": 5 }, "end": { - "line": 17, + "line": 25, "column": 8 } }, @@ -241,37 +315,37 @@ "nodes": [ { "type": "Text", - "start": 107, - "end": 109, + "start": 151, + "end": 153, "raw": "\n\t", "data": "\n\t" }, { "type": "Component", - "start": 109, - "end": 125, + "start": 153, + "end": 169, "name": "Comp", "attributes": [ { "type": "Attribute", - "start": 115, - "end": 124, + "start": 159, + "end": 168, "name": "foo", "value": { "type": "ExpressionTag", - "start": 119, - "end": 124, + "start": 163, + "end": 168, "expression": { "type": "Identifier", - "start": 120, - "end": 123, + "start": 164, + "end": 167, "loc": { "start": { - "line": 18, + "line": 26, "column": 12 }, "end": { - "line": 18, + "line": 26, "column": 15 } }, @@ -291,15 +365,15 @@ }, { "type": "Text", - "start": 130, - "end": 132, + "start": 174, + "end": 176, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 132, - "end": 160, + "start": 176, + "end": 204, "name": "div", "attributes": [], "fragment": { @@ -307,15 +381,15 @@ "nodes": [ { "type": "Text", - "start": 137, - "end": 138, + "start": 181, + "end": 182, "raw": "\n", "data": "\n" }, { "type": "RegularElement", - "start": 138, - "end": 147, + "start": 182, + "end": 191, "name": "p", "attributes": [], "fragment": { @@ -323,8 +397,8 @@ "nodes": [ { "type": "Text", - "start": 141, - "end": 143, + "start": 185, + "end": 187, "raw": "hi", "data": "hi" } @@ -333,15 +407,15 @@ }, { "type": "Text", - "start": 147, - "end": 149, + "start": 191, + "end": 193, "raw": "\n\n", "data": "\n\n" }, { "type": "RegularElement", - "start": 149, - "end": 160, + "start": 193, + "end": 204, "name": "open-ended", "attributes": [], "fragment": { From 5529ad241fd2d6b7adbc95a38ebd9765d3943ac3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 10:56:24 -0500 Subject: [PATCH 0005/1429] Version Packages (#14735) Co-authored-by: github-actions[bot] --- .changeset/tame-eggs-work.md | 5 ----- .changeset/tiny-kings-serve.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/tame-eggs-work.md delete mode 100644 .changeset/tiny-kings-serve.md diff --git a/.changeset/tame-eggs-work.md b/.changeset/tame-eggs-work.md deleted file mode 100644 index 2c1c59de4a..0000000000 --- a/.changeset/tame-eggs-work.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly highlight first rerun of `$inspect.trace` diff --git a/.changeset/tiny-kings-serve.md b/.changeset/tiny-kings-serve.md deleted file mode 100644 index b883767132..0000000000 --- a/.changeset/tiny-kings-serve.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: more loose parser improvements diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 598d75ca9b..0b187be338 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.14.2 + +### Patch Changes + +- fix: correctly highlight first rerun of `$inspect.trace` ([#14734](https://github.com/sveltejs/svelte/pull/14734)) + +- chore: more loose parser improvements ([#14733](https://github.com/sveltejs/svelte/pull/14733)) + ## 5.14.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index db861eced2..a6eb2f4a8a 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.14.1", + "version": "5.14.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 78ec824169..c43155a159 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.14.1'; +export const VERSION = '5.14.2'; export const PUBLIC_VERSION = '5'; From d7e4bd2c665f22daac82a4b80cbc973d9a54d92c Mon Sep 17 00:00:00 2001 From: joakim Date: Tue, 17 Dec 2024 17:46:40 +0100 Subject: [PATCH 0006/1429] Link to migration guide from documentation (#14737) --- documentation/docs/99-legacy/30-legacy-svelte-component.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/99-legacy/30-legacy-svelte-component.md b/documentation/docs/99-legacy/30-legacy-svelte-component.md index 5d385a9c02..3da2e3350e 100644 --- a/documentation/docs/99-legacy/30-legacy-svelte-component.md +++ b/documentation/docs/99-legacy/30-legacy-svelte-component.md @@ -2,7 +2,7 @@ title: --- -In runes mode, `` will re-render if the value of `MyComponent` changes. +In runes mode, `` will re-render if the value of `MyComponent` changes. See the [Svelte 5 migration guide](/docs/svelte/v5-migration-guide#Breaking-changes-in-runes-mode-svelte:component-is-no-longer-necessary) for an example. In legacy mode, it won't — we must use ``, which destroys and recreates the component instance when the value of its `this` expression changes: From 36a437c2f9a4ac4b10d5f6f1bead546fa7fcbd88 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 17 Dec 2024 13:04:16 -0500 Subject: [PATCH 0007/1429] fix: compare array contents for equality mismatch detections, not the arrays themselves (#14738) --- .changeset/tasty-deers-agree.md | 5 +++ .../src/internal/client/dev/equality.js | 33 +++++++-------- .../state-proxy-equality-mismatch/_config.js | 41 +++++++++++++++++++ .../state-proxy-equality-mismatch/main.svelte | 23 +++++++++++ 4 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 .changeset/tasty-deers-agree.md create mode 100644 packages/svelte/tests/runtime-runes/samples/state-proxy-equality-mismatch/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/state-proxy-equality-mismatch/main.svelte diff --git a/.changeset/tasty-deers-agree.md b/.changeset/tasty-deers-agree.md new file mode 100644 index 0000000000..92901c199c --- /dev/null +++ b/.changeset/tasty-deers-agree.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: compare array contents for equality mismatch detections, not the arrays themselves diff --git a/packages/svelte/src/internal/client/dev/equality.js b/packages/svelte/src/internal/client/dev/equality.js index 170f7baf95..bbc28dc638 100644 --- a/packages/svelte/src/internal/client/dev/equality.js +++ b/packages/svelte/src/internal/client/dev/equality.js @@ -17,10 +17,11 @@ export function init_array_prototype_warnings() { const index = indexOf.call(this, item, from_index); if (index === -1) { - const test = indexOf.call(get_proxied_value(this), get_proxied_value(item), from_index); - - if (test !== -1) { - w.state_proxy_equality_mismatch('array.indexOf(...)'); + for (let i = from_index ?? 0; i < this.length; i += 1) { + if (get_proxied_value(this[i]) === item) { + w.state_proxy_equality_mismatch('array.indexOf(...)'); + break; + } } } @@ -33,16 +34,11 @@ export function init_array_prototype_warnings() { const index = lastIndexOf.call(this, item, from_index ?? this.length - 1); if (index === -1) { - // we need to specify this.length - 1 because it's probably using something like - // `arguments` inside so passing undefined is different from not passing anything - const test = lastIndexOf.call( - get_proxied_value(this), - get_proxied_value(item), - from_index ?? this.length - 1 - ); - - if (test !== -1) { - w.state_proxy_equality_mismatch('array.lastIndexOf(...)'); + for (let i = 0; i <= (from_index ?? this.length - 1); i += 1) { + if (get_proxied_value(this[i]) === item) { + w.state_proxy_equality_mismatch('array.lastIndexOf(...)'); + break; + } } } @@ -53,10 +49,11 @@ export function init_array_prototype_warnings() { const has = includes.call(this, item, from_index); if (!has) { - const test = includes.call(get_proxied_value(this), get_proxied_value(item), from_index); - - if (test) { - w.state_proxy_equality_mismatch('array.includes(...)'); + for (let i = 0; i < this.length; i += 1) { + if (get_proxied_value(this[i]) === item) { + w.state_proxy_equality_mismatch('array.includes(...)'); + break; + } } } diff --git a/packages/svelte/tests/runtime-runes/samples/state-proxy-equality-mismatch/_config.js b/packages/svelte/tests/runtime-runes/samples/state-proxy-equality-mismatch/_config.js new file mode 100644 index 0000000000..0fb086c8e0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/state-proxy-equality-mismatch/_config.js @@ -0,0 +1,41 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + + async test({ assert, target, warnings }) { + const [btn1, btn2, btn3, btn4, btn5, btn6, clear] = target.querySelectorAll('button'); + + flushSync(() => { + btn1.click(); + btn2.click(); + btn3.click(); + btn4.click(); + btn5.click(); + btn6.click(); + }); + + assert.deepEqual(warnings, [ + 'Reactive `$state(...)` proxies and the values they proxy have different identities. Because of this, comparisons with `array.includes(...)` will produce unexpected results', + 'Reactive `$state(...)` proxies and the values they proxy have different identities. Because of this, comparisons with `array.indexOf(...)` will produce unexpected results', + 'Reactive `$state(...)` proxies and the values they proxy have different identities. Because of this, comparisons with `array.lastIndexOf(...)` will produce unexpected results' + ]); + + flushSync(() => clear.click()); + warnings.length = 0; + + flushSync(() => { + btn1.click(); + btn2.click(); + btn3.click(); + btn4.click(); + btn5.click(); + btn6.click(); + }); + + assert.deepEqual(warnings, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/state-proxy-equality-mismatch/main.svelte b/packages/svelte/tests/runtime-runes/samples/state-proxy-equality-mismatch/main.svelte new file mode 100644 index 0000000000..1b7bf444f1 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/state-proxy-equality-mismatch/main.svelte @@ -0,0 +1,23 @@ + + + + + +
+ + + + +
+ + + + +
+ + From c033dae04915d5dd83c0e174c6b0b6bc7bdf85ec Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 17 Dec 2024 20:20:57 -0500 Subject: [PATCH 0008/1429] fix: bump esrap, prevent malformed AST (#14742) * bump esrap, start fixing stuff * ignore type exports when looking for undefineds * sanitize stuff * changeset * fix * oops * try this? * prettier --- .changeset/purple-donuts-talk.md | 5 +++++ packages/svelte/package.json | 2 +- .../phases/1-parse/remove_typescript_nodes.js | 21 +++++++++---------- .../src/compiler/phases/2-analyze/index.js | 10 ++++++++- .../src/compiler/phases/3-transform/index.js | 4 ++++ packages/svelte/src/compiler/phases/scope.js | 6 ++++++ .../samples/typescript/main.svelte | 2 +- packages/svelte/tsconfig.json | 6 +++++- pnpm-lock.yaml | 12 +++++------ 9 files changed, 47 insertions(+), 21 deletions(-) create mode 100644 .changeset/purple-donuts-talk.md diff --git a/.changeset/purple-donuts-talk.md b/.changeset/purple-donuts-talk.md new file mode 100644 index 0000000000..2bb348cd19 --- /dev/null +++ b/.changeset/purple-donuts-talk.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: bump esrap, prevent malformed AST diff --git a/packages/svelte/package.json b/packages/svelte/package.json index a6eb2f4a8a..6603fe9b42 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -147,7 +147,7 @@ "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "esm-env": "^1.2.1", - "esrap": "^1.2.3", + "esrap": "^1.3.1", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", diff --git a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js index b968e95213..5e4fa231de 100644 --- a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js +++ b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js @@ -17,6 +17,16 @@ function remove_this_param(node, context) { /** @type {Visitors} */ const visitors = { + _(node, context) { + context.next(); + + // TODO there may come a time when we decide to preserve type annotations. + // until that day comes, we just delete them so they don't confuse esrap + delete node.typeAnnotation; + delete node.typeParameters; + delete node.returnType; + delete node.accessibility; + }, Decorator(node) { e.typescript_invalid_feature(node, 'decorators (related TSC proposal is not stage 4 yet)'); }, @@ -78,23 +88,12 @@ const visitors = { TSNonNullExpression(node, context) { return context.visit(node.expression); }, - TSTypeAnnotation() { - // This isn't correct, strictly speaking, and could result in invalid ASTs (like an empty statement within function parameters), - // but esrap, our printing tool, just ignores these AST nodes at invalid positions, so it's fine - return b.empty; - }, TSInterfaceDeclaration() { return b.empty; }, TSTypeAliasDeclaration() { return b.empty; }, - TSTypeParameterDeclaration() { - return b.empty; - }, - TSTypeParameterInstantiation() { - return b.empty; - }, TSEnumDeclaration(node) { e.typescript_invalid_feature(node, 'enums'); }, diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 042e88fa2f..d17cc34a5f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -698,8 +698,16 @@ export function analyze_component(root, source, options) { } for (const node of analysis.module.ast.body) { - if (node.type === 'ExportNamedDeclaration' && node.specifiers !== null && node.source == null) { + if ( + node.type === 'ExportNamedDeclaration' && + // @ts-expect-error + node.exportKind !== 'type' && + node.specifiers !== null && + node.source == null + ) { for (const specifier of node.specifiers) { + // @ts-expect-error + if (specifier.exportKind === 'type') continue; if (specifier.local.type !== 'Identifier') continue; const binding = analysis.module.scope.get(specifier.local.name); diff --git a/packages/svelte/src/compiler/phases/3-transform/index.js b/packages/svelte/src/compiler/phases/3-transform/index.js index 8f6597ee12..d0739d6cc4 100644 --- a/packages/svelte/src/compiler/phases/3-transform/index.js +++ b/packages/svelte/src/compiler/phases/3-transform/index.js @@ -33,12 +33,15 @@ export function transform_component(analysis, source, options) { : client_component(analysis, options); const js_source_name = get_source_name(options.filename, options.outputFilename, 'input.svelte'); + + // @ts-expect-error const js = print(program, { // include source content; makes it easier/more robust looking up the source map code // (else esrap does return null for source and sourceMapContent which may trip up tooling) sourceMapContent: source, sourceMapSource: js_source_name }); + merge_with_preprocessor_map(js, options, js_source_name); const css = @@ -92,6 +95,7 @@ export function transform_module(analysis, source, options) { } return { + // @ts-expect-error js: print(program, { // include source content; makes it easier/more robust looking up the source map code // (else esrap does return null for source and sourceMapContent which may trip up tooling) diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 3536dd6a18..20dcfa2821 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -433,7 +433,13 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) { }, ImportDeclaration(node, { state }) { + // @ts-expect-error + if (node.importKind === 'type') return; + for (const specifier of node.specifiers) { + // @ts-expect-error + if (specifier.importKind === 'type') continue; + state.scope.declare(specifier.local, 'normal', 'import', node); } }, diff --git a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte index 3b98dafa05..cc31350601 100644 --- a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte @@ -24,7 +24,7 @@ declare module 'foobar' {} namespace SomeNamespace { - export type Foo = true + export type Foo = true; } export function overload(a: boolean): boolean; diff --git a/packages/svelte/tsconfig.json b/packages/svelte/tsconfig.json index 380307901e..c9f0fb3b2b 100644 --- a/packages/svelte/tsconfig.json +++ b/packages/svelte/tsconfig.json @@ -40,5 +40,9 @@ "./tests/runtime-browser/test-ssr.ts", "./tests/*/samples/*/_config.js" ], - "exclude": ["./scripts/process-messages/templates/", "./src/compiler/optimizer/"] + "exclude": [ + "./scripts/process-messages/templates/", + "./scripts/_bundle.js", + "./src/compiler/optimizer/" + ] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 250a03744e..1de6883a48 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,8 +84,8 @@ importers: specifier: ^1.2.1 version: 1.2.1 esrap: - specifier: ^1.2.3 - version: 1.2.3 + specifier: ^1.3.1 + version: 1.3.1 is-reference: specifier: ^3.0.3 version: 3.0.3 @@ -1111,8 +1111,8 @@ packages: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} - esrap@1.2.3: - resolution: {integrity: sha512-ZlQmCCK+n7SGoqo7DnfKaP1sJZa49P01/dXzmjCASSo04p72w8EksT2NMK8CEX8DhKsfJXANioIw8VyHNsBfvQ==} + esrap@1.3.1: + resolution: {integrity: sha512-KpAH3+QsDmtOP1KOW04CbD1PgzWsIHjB8tOCk3PCb8xzNGn8XkjI8zl80i09fmXdzQyaS8tcsKCCDzHF7AcowA==} esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} @@ -3315,10 +3315,10 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@1.2.3: + esrap@1.3.1: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - '@types/estree': 1.0.6 + '@typescript-eslint/types': 8.2.0 esrecurse@4.3.0: dependencies: From 00ff60e8d562d5f8397104072d9e8a7faece01d8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 20:22:44 -0500 Subject: [PATCH 0009/1429] Version Packages (#14740) Co-authored-by: github-actions[bot] --- .changeset/purple-donuts-talk.md | 5 ----- .changeset/tasty-deers-agree.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/purple-donuts-talk.md delete mode 100644 .changeset/tasty-deers-agree.md diff --git a/.changeset/purple-donuts-talk.md b/.changeset/purple-donuts-talk.md deleted file mode 100644 index 2bb348cd19..0000000000 --- a/.changeset/purple-donuts-talk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: bump esrap, prevent malformed AST diff --git a/.changeset/tasty-deers-agree.md b/.changeset/tasty-deers-agree.md deleted file mode 100644 index 92901c199c..0000000000 --- a/.changeset/tasty-deers-agree.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: compare array contents for equality mismatch detections, not the arrays themselves diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 0b187be338..4dd1cb994b 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.14.3 + +### Patch Changes + +- fix: bump esrap, prevent malformed AST ([#14742](https://github.com/sveltejs/svelte/pull/14742)) + +- fix: compare array contents for equality mismatch detections, not the arrays themselves ([#14738](https://github.com/sveltejs/svelte/pull/14738)) + ## 5.14.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 6603fe9b42..dabe70eecc 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.14.2", + "version": "5.14.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index c43155a159..7458849459 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.14.2'; +export const VERSION = '5.14.3'; export const PUBLIC_VERSION = '5'; From e38975eac9dc883ef00ab75230a0ffc7eb01d7e3 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 17 Dec 2024 22:18:00 -0500 Subject: [PATCH 0010/1429] fix: remove unwanted properties from both replaced and unreplaced nodes (#14744) --- .changeset/famous-mugs-rhyme.md | 5 +++++ .../compiler/phases/1-parse/remove_typescript_nodes.js | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 .changeset/famous-mugs-rhyme.md diff --git a/.changeset/famous-mugs-rhyme.md b/.changeset/famous-mugs-rhyme.md new file mode 100644 index 0000000000..433d1024df --- /dev/null +++ b/.changeset/famous-mugs-rhyme.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: remove unwanted properties from both replaced and unreplaced nodes diff --git a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js index 5e4fa231de..96639829bb 100644 --- a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js +++ b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js @@ -18,14 +18,14 @@ function remove_this_param(node, context) { /** @type {Visitors} */ const visitors = { _(node, context) { - context.next(); + const n = context.next() ?? node; // TODO there may come a time when we decide to preserve type annotations. // until that day comes, we just delete them so they don't confuse esrap - delete node.typeAnnotation; - delete node.typeParameters; - delete node.returnType; - delete node.accessibility; + delete n.typeAnnotation; + delete n.typeParameters; + delete n.returnType; + delete n.accessibility; }, Decorator(node) { e.typescript_invalid_feature(node, 'decorators (related TSC proposal is not stage 4 yet)'); From c79b909458b6265a18cc554dd2affd832b9797af Mon Sep 17 00:00:00 2001 From: Taku Fukada Date: Wed, 18 Dec 2024 18:15:49 +0900 Subject: [PATCH 0011/1429] fix: remove 'implements' from class declaration (#14749) * fix: remove implements from class declaration * add a smoke test --- .changeset/calm-maps-cover.md | 5 +++++ .../src/compiler/phases/1-parse/remove_typescript_nodes.js | 1 + .../samples/typescript-class-and-interface/_config.js | 3 +++ .../samples/typescript-class-and-interface/main.svelte | 5 +++++ 4 files changed, 14 insertions(+) create mode 100644 .changeset/calm-maps-cover.md create mode 100644 packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/_config.js create mode 100644 packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/main.svelte diff --git a/.changeset/calm-maps-cover.md b/.changeset/calm-maps-cover.md new file mode 100644 index 0000000000..b7bd449c1a --- /dev/null +++ b/.changeset/calm-maps-cover.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: remove implements from class declarations diff --git a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js index 96639829bb..18c805128d 100644 --- a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js +++ b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js @@ -115,6 +115,7 @@ const visitors = { if (node.declare) { return b.empty; } + delete node.implements; return context.next(); }, VariableDeclaration(node, context) { diff --git a/packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/_config.js b/packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/_config.js new file mode 100644 index 0000000000..4e8f43d5db --- /dev/null +++ b/packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../assert'; + +export default test({}); diff --git a/packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/main.svelte b/packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/main.svelte new file mode 100644 index 0000000000..3d58f1072d --- /dev/null +++ b/packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/main.svelte @@ -0,0 +1,5 @@ + \ No newline at end of file From 2bfdd1b436d767b20c896117992757b229263324 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:47:17 +0100 Subject: [PATCH 0012/1429] Version Packages (#14745) Co-authored-by: github-actions[bot] --- .changeset/calm-maps-cover.md | 5 ----- .changeset/famous-mugs-rhyme.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/calm-maps-cover.md delete mode 100644 .changeset/famous-mugs-rhyme.md diff --git a/.changeset/calm-maps-cover.md b/.changeset/calm-maps-cover.md deleted file mode 100644 index b7bd449c1a..0000000000 --- a/.changeset/calm-maps-cover.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: remove implements from class declarations diff --git a/.changeset/famous-mugs-rhyme.md b/.changeset/famous-mugs-rhyme.md deleted file mode 100644 index 433d1024df..0000000000 --- a/.changeset/famous-mugs-rhyme.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: remove unwanted properties from both replaced and unreplaced nodes diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 4dd1cb994b..47a67006c1 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.14.4 + +### Patch Changes + +- fix: remove implements from class declarations ([#14749](https://github.com/sveltejs/svelte/pull/14749)) + +- fix: remove unwanted properties from both replaced and unreplaced nodes ([#14744](https://github.com/sveltejs/svelte/pull/14744)) + ## 5.14.3 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index dabe70eecc..1b3df47bdf 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.14.3", + "version": "5.14.4", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 7458849459..e6f7bd2716 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.14.3'; +export const VERSION = '5.14.4'; export const PUBLIC_VERSION = '5'; From 88a15cd7064f04732f1f616a6bf7964de2b0631c Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 18 Dec 2024 17:22:05 +0100 Subject: [PATCH 0013/1429] chore: TS fixes cleanup (#14750) * chore: TS fixes cleanup - move a test - revert seemginly unnecessary code changes * add another test --- packages/svelte/src/compiler/phases/2-analyze/index.js | 10 +--------- packages/svelte/src/compiler/phases/scope.js | 6 ------ .../samples/typescript-class-and-interface/_config.js | 3 --- .../samples/typescript-class-and-interface/main.svelte | 5 ----- .../tests/runtime-runes/samples/typescript/main.svelte | 6 ++++++ 5 files changed, 7 insertions(+), 23 deletions(-) delete mode 100644 packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/_config.js delete mode 100644 packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/main.svelte diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index d17cc34a5f..042e88fa2f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -698,16 +698,8 @@ export function analyze_component(root, source, options) { } for (const node of analysis.module.ast.body) { - if ( - node.type === 'ExportNamedDeclaration' && - // @ts-expect-error - node.exportKind !== 'type' && - node.specifiers !== null && - node.source == null - ) { + if (node.type === 'ExportNamedDeclaration' && node.specifiers !== null && node.source == null) { for (const specifier of node.specifiers) { - // @ts-expect-error - if (specifier.exportKind === 'type') continue; if (specifier.local.type !== 'Identifier') continue; const binding = analysis.module.scope.get(specifier.local.name); diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 20dcfa2821..3536dd6a18 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -433,13 +433,7 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) { }, ImportDeclaration(node, { state }) { - // @ts-expect-error - if (node.importKind === 'type') return; - for (const specifier of node.specifiers) { - // @ts-expect-error - if (specifier.importKind === 'type') continue; - state.scope.declare(specifier.local, 'normal', 'import', node); } }, diff --git a/packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/_config.js b/packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/_config.js deleted file mode 100644 index 4e8f43d5db..0000000000 --- a/packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -import { test } from '../../assert'; - -export default test({}); diff --git a/packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/main.svelte b/packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/main.svelte deleted file mode 100644 index 3d58f1072d..0000000000 --- a/packages/svelte/tests/runtime-browser/samples/typescript-class-and-interface/main.svelte +++ /dev/null @@ -1,5 +0,0 @@ - \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte index cc31350601..cd23b31096 100644 --- a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte @@ -8,6 +8,10 @@ console.log(this); } + function foo(): string { + return ""!; + } + class Foo { public name: string; x = 'x' as const; @@ -16,6 +20,8 @@ } } + class MyClass implements Hello {} + declare const declared_const: number; declare function declared_fn(): void; declare class declared_class { From bfa0b3466375b5cf4c979884d9f77fe21c678966 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 18 Dec 2024 16:48:16 -0500 Subject: [PATCH 0014/1429] add self-closing-tags info (#14758) * add self-closing-tags info * regenerate --- .../.generated/compile-warnings.md | 26 +++++++++++++++++++ .../messages/compile-warnings/template.md | 26 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/documentation/docs/98-reference/.generated/compile-warnings.md b/documentation/docs/98-reference/.generated/compile-warnings.md index f6f2585df2..3d515305fe 100644 --- a/documentation/docs/98-reference/.generated/compile-warnings.md +++ b/documentation/docs/98-reference/.generated/compile-warnings.md @@ -630,6 +630,32 @@ In some situations a selector may target an element that is not 'visible' to the Self-closing HTML tags for non-void elements are ambiguous — use `<%name% ...>` rather than `<%name% ... />` ``` +In HTML, there's [no such thing as a self-closing tag](https://jakearchibald.com/2023/against-self-closing-tags-in-html/). While this _looks_ like a self-contained element with some text next to it... + +```html +
+ some text! +
+``` + +...a spec-compliant HTML parser (such as a browser) will in fact parse it like this, with the text _inside_ the icon: + +```html +
+ some text! +
+``` + +Some templating languages (including Svelte) will 'fix' HTML by turning `` into ``. Others adhere to the spec. Both result in ambiguity and confusion when copy-pasting code between different contexts, and as such Svelte prompts you to resolve the ambiguity directly by having an explicit closing tag. + +To automate this, run the dedicated migration: + +```bash +npx sv migrate self-closing-tags +``` + +In a future version of Svelte, self-closing tags may be upgraded from a warning to an error. + ### event_directive_deprecated ``` diff --git a/packages/svelte/messages/compile-warnings/template.md b/packages/svelte/messages/compile-warnings/template.md index 33e635bdb2..c1675e5995 100644 --- a/packages/svelte/messages/compile-warnings/template.md +++ b/packages/svelte/messages/compile-warnings/template.md @@ -34,6 +34,32 @@ > Self-closing HTML tags for non-void elements are ambiguous — use `<%name% ...>` rather than `<%name% ... />` +In HTML, there's [no such thing as a self-closing tag](https://jakearchibald.com/2023/against-self-closing-tags-in-html/). While this _looks_ like a self-contained element with some text next to it... + +```html +
+ some text! +
+``` + +...a spec-compliant HTML parser (such as a browser) will in fact parse it like this, with the text _inside_ the icon: + +```html +
+ some text! +
+``` + +Some templating languages (including Svelte) will 'fix' HTML by turning `` into ``. Others adhere to the spec. Both result in ambiguity and confusion when copy-pasting code between different contexts, and as such Svelte prompts you to resolve the ambiguity directly by having an explicit closing tag. + +To automate this, run the dedicated migration: + +```bash +npx sv migrate self-closing-tags +``` + +In a future version of Svelte, self-closing tags may be upgraded from a warning to an error. + ## event_directive_deprecated > Using `on:%name%` to listen to the %name% event is deprecated. Use the event attribute `on%name%` instead From 8705d44c6487e642f4cb23b725311a37db19f0d8 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Wed, 18 Dec 2024 23:11:11 +0000 Subject: [PATCH 0015/1429] fix: ensure svg namespace for `` elements is correct (#14756) * fix: ensure svg namespace persists from parent of if blocks * better fix * better fix * address feedback --- .changeset/nine-buses-own.md | 5 +++++ .../2-analyze/visitors/RegularElement.js | 18 +++++++++++++++++- .../svg-namespace-if-block-2/_config.js | 11 +++++++++++ .../svg-namespace-if-block-2/main.svelte | 7 +++++++ 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 .changeset/nine-buses-own.md create mode 100644 packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block-2/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block-2/main.svelte diff --git a/.changeset/nine-buses-own.md b/.changeset/nine-buses-own.md new file mode 100644 index 0000000000..039f7b2001 --- /dev/null +++ b/.changeset/nine-buses-own.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure svg namespace for `` elements is correct diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js index 7454ab8103..9a891c0c49 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js @@ -86,7 +86,23 @@ export function RegularElement(node, context) { (attribute) => attribute.type === 'SpreadAttribute' ); - node.metadata.svg = is_svg(node.name); + const is_svg_element = () => { + if (is_svg(node.name)) { + return true; + } + if (node.name === 'a') { + for (let i = context.path.length - 1; i >= 0; i--) { + const ancestor = context.path[i]; + if (ancestor.type === 'RegularElement') { + return ancestor.metadata.svg; + } + } + } + + return false; + }; + + node.metadata.svg = is_svg_element(); node.metadata.mathml = is_mathml(node.name); if (is_custom_element_node(node) && node.attributes.length > 0) { diff --git a/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block-2/_config.js b/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block-2/_config.js new file mode 100644 index 0000000000..7373a4043e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block-2/_config.js @@ -0,0 +1,11 @@ +import { test, ok } from '../../test'; + +export default test({ + html: ``, + test({ assert, target }) { + const a = target.querySelector('a'); + ok(a); + + assert.equal(a.namespaceURI, 'http://www.w3.org/2000/svg'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block-2/main.svelte new file mode 100644 index 0000000000..3676278c6c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svg-namespace-if-block-2/main.svelte @@ -0,0 +1,7 @@ + + {#if true} + + {name} + + {/if} + From d0f156c0f9630d21a1fef40232b2d9aa87338c35 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 19 Dec 2024 14:50:00 +0000 Subject: [PATCH 0016/1429] fix: ensure $inspect.trace code is treeshaken out if unused (#14770) * fix: ensure $inspect.trace code is treeshaken out if unused * Update .changeset/tough-dingos-deliver.md --------- Co-authored-by: Rich Harris --- .changeset/tough-dingos-deliver.md | 5 +++++ packages/svelte/package.json | 3 +++ packages/svelte/scripts/check-treeshakeability.js | 11 +++++++++++ .../svelte/src/compiler/phases/2-analyze/index.js | 1 + .../phases/2-analyze/visitors/CallExpression.js | 2 ++ .../phases/3-transform/client/transform-client.js | 4 ++++ packages/svelte/src/compiler/phases/types.d.ts | 1 + packages/svelte/src/internal/client/proxy.js | 3 ++- .../svelte/src/internal/client/reactivity/deriveds.js | 3 ++- .../svelte/src/internal/client/reactivity/sources.js | 6 +++--- packages/svelte/src/internal/client/runtime.js | 3 ++- packages/svelte/src/internal/flags/index.js | 5 +++++ packages/svelte/src/internal/flags/tracing.js | 3 +++ 13 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 .changeset/tough-dingos-deliver.md create mode 100644 packages/svelte/src/internal/flags/tracing.js diff --git a/.changeset/tough-dingos-deliver.md b/.changeset/tough-dingos-deliver.md new file mode 100644 index 0000000000..5c0faf703c --- /dev/null +++ b/.changeset/tough-dingos-deliver.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: treeshake `$inspect.trace` code if unused diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 1b3df47bdf..6e6075c5db 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -56,6 +56,9 @@ "./internal/flags/legacy": { "default": "./src/internal/flags/legacy.js" }, + "./internal/flags/tracing": { + "default": "./src/internal/flags/tracing.js" + }, "./internal/server": { "default": "./src/internal/server/index.js" }, diff --git a/packages/svelte/scripts/check-treeshakeability.js b/packages/svelte/scripts/check-treeshakeability.js index 1b50111b87..1501ee6954 100644 --- a/packages/svelte/scripts/check-treeshakeability.js +++ b/packages/svelte/scripts/check-treeshakeability.js @@ -58,6 +58,7 @@ for (const key in pkg.exports) { if (key === './internal') continue; if (key === './internal/disclose-version') continue; if (key === './internal/flags/legacy') continue; + if (key === './internal/flags/tracing') continue; for (const type of ['browser', 'default']) { if (!pkg.exports[key][type]) continue; @@ -91,6 +92,7 @@ const bundle = await bundle_code( hi + a a @@ -134,6 +136,15 @@ if (!bundle.includes('component_context.l')) { console.error(`❌ Legacy code not treeshakeable`); } +if (!bundle.includes(`'CreatedAt'`)) { + // eslint-disable-next-line no-console + console.error(`✅ $inspect.trace code treeshakeable`); +} else { + failed = true; + // eslint-disable-next-line no-console + console.error(`❌ $inspect.trace code not treeshakeable`); +} + if (failed) { // eslint-disable-next-line no-console console.error(bundle); diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 042e88fa2f..a4167be3a1 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -408,6 +408,7 @@ export function analyze_component(root, source, options) { template, elements: [], runes, + tracing: false, immutable: runes || options.immutable, exports: [], uses_props: false, diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index 6a301726b1..9f51cd61de 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -171,6 +171,8 @@ export function CallExpression(node, context) { context.state.scope.tracing = b.thunk(b.literal(label + ' ' + loc)); } + + context.state.analysis.tracing = true; } break; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index ccbdcea4cc..8e05b44a83 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -539,6 +539,10 @@ export function client_component(analysis, options) { body.unshift(b.imports([], 'svelte/internal/flags/legacy')); } + if (analysis.tracing) { + body.unshift(b.imports([], 'svelte/internal/flags/tracing')); + } + if (options.discloseVersion) { body.unshift(b.imports([], 'svelte/internal/disclose-version')); } diff --git a/packages/svelte/src/compiler/phases/types.d.ts b/packages/svelte/src/compiler/phases/types.d.ts index 6a2ea27993..b4dcc270cd 100644 --- a/packages/svelte/src/compiler/phases/types.d.ts +++ b/packages/svelte/src/compiler/phases/types.d.ts @@ -39,6 +39,7 @@ export interface ComponentAnalysis extends Analysis { /** Used for CSS pruning and scoping */ elements: Array; runes: boolean; + tracing: boolean; exports: Array<{ name: string; alias: string | null }>; /** Whether the component uses `$$props` */ uses_props: boolean; diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index d74f55866f..6cbd6394df 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -14,6 +14,7 @@ import { STATE_SYMBOL, STATE_SYMBOL_METADATA } from './constants.js'; import { UNINITIALIZED } from '../../constants.js'; import * as e from './errors.js'; import { get_stack } from './dev/tracing.js'; +import { tracing_mode_flag } from '../flags/index.js'; /** * @template T @@ -25,7 +26,7 @@ import { get_stack } from './dev/tracing.js'; export function proxy(value, parent = null, prev) { /** @type {Error | null} */ var stack = null; - if (DEV) { + if (DEV && tracing_mode_flag) { stack = get_stack('CreatedAt'); } // if non-proxyable, or is already a proxy, return `value` diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 8c1a3a652b..5e045920bf 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -25,6 +25,7 @@ import * as e from '../errors.js'; import { destroy_effect } from './effects.js'; import { inspect_effects, set_inspect_effects } from './sources.js'; import { get_stack } from '../dev/tracing.js'; +import { tracing_mode_flag } from '../../flags/index.js'; /** * @template V @@ -62,7 +63,7 @@ export function derived(fn) { parent: parent_derived ?? active_effect }; - if (DEV) { + if (DEV && tracing_mode_flag) { signal.created = get_stack('CreatedAt'); } diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 24dd837772..3e8c4a00c8 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -32,7 +32,7 @@ import { BLOCK_EFFECT } from '../constants.js'; import * as e from '../errors.js'; -import { legacy_mode_flag } from '../../flags/index.js'; +import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js'; import { get_stack } from '../dev/tracing.js'; export let inspect_effects = new Set(); @@ -60,7 +60,7 @@ export function source(v, stack) { version: 0 }; - if (DEV) { + if (DEV && tracing_mode_flag) { signal.created = stack ?? get_stack('CreatedAt'); signal.debug = null; } @@ -170,7 +170,7 @@ export function internal_set(source, value) { source.v = value; source.version = increment_version(); - if (DEV) { + if (DEV && tracing_mode_flag) { source.updated = get_stack('UpdatedAt'); } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 4aa3d17a15..4a90a21971 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -34,7 +34,7 @@ import { destroy_derived, execute_derived, update_derived } from './reactivity/d import * as e from './errors.js'; import { lifecycle_outside_component } from '../shared/errors.js'; import { FILENAME } from '../../constants.js'; -import { legacy_mode_flag } from '../flags/index.js'; +import { legacy_mode_flag, tracing_mode_flag } from '../flags/index.js'; import { tracing_expressions, get_stack } from './dev/tracing.js'; const FLUSH_MICROTASK = 0; @@ -917,6 +917,7 @@ export function get(signal) { if ( DEV && + tracing_mode_flag && tracing_expressions !== null && active_reaction !== null && tracing_expressions.reaction === active_reaction diff --git a/packages/svelte/src/internal/flags/index.js b/packages/svelte/src/internal/flags/index.js index 767a40a765..017840f2d9 100644 --- a/packages/svelte/src/internal/flags/index.js +++ b/packages/svelte/src/internal/flags/index.js @@ -1,5 +1,10 @@ export let legacy_mode_flag = false; +export let tracing_mode_flag = false; export function enable_legacy_mode_flag() { legacy_mode_flag = true; } + +export function enable_tracing_mode_flag() { + tracing_mode_flag = true; +} diff --git a/packages/svelte/src/internal/flags/tracing.js b/packages/svelte/src/internal/flags/tracing.js new file mode 100644 index 0000000000..b2de46dcd1 --- /dev/null +++ b/packages/svelte/src/internal/flags/tracing.js @@ -0,0 +1,3 @@ +import { enable_tracing_mode_flag } from './index.js'; + +enable_tracing_mode_flag(); From 7c6379a260799fdbecc43d9cb736ec9b81d21dec Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 19 Dec 2024 09:58:53 -0500 Subject: [PATCH 0017/1429] fix: bump esrap dependency (#14765) * fix: bump esrap * changeset * ugh --- .changeset/cool-ducks-carry.md | 5 +++++ packages/svelte/package.json | 2 +- packages/svelte/scripts/process-messages/index.js | 1 - .../svelte/src/compiler/phases/3-transform/index.js | 2 -- pnpm-lock.yaml | 13 ++++++------- 5 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 .changeset/cool-ducks-carry.md diff --git a/.changeset/cool-ducks-carry.md b/.changeset/cool-ducks-carry.md new file mode 100644 index 0000000000..507bb55d13 --- /dev/null +++ b/.changeset/cool-ducks-carry.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: bump esrap dependency diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 6e6075c5db..6c919a7308 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -150,7 +150,7 @@ "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "esm-env": "^1.2.1", - "esrap": "^1.3.1", + "esrap": "^1.3.2", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", diff --git a/packages/svelte/scripts/process-messages/index.js b/packages/svelte/scripts/process-messages/index.js index bbe257498e..80619acfa7 100644 --- a/packages/svelte/scripts/process-messages/index.js +++ b/packages/svelte/scripts/process-messages/index.js @@ -389,7 +389,6 @@ function transform(name, dest) { ast.body.push(clone); } - // @ts-expect-error const module = esrap.print(ast); fs.writeFileSync( diff --git a/packages/svelte/src/compiler/phases/3-transform/index.js b/packages/svelte/src/compiler/phases/3-transform/index.js index d0739d6cc4..f96fd64ec7 100644 --- a/packages/svelte/src/compiler/phases/3-transform/index.js +++ b/packages/svelte/src/compiler/phases/3-transform/index.js @@ -34,7 +34,6 @@ export function transform_component(analysis, source, options) { const js_source_name = get_source_name(options.filename, options.outputFilename, 'input.svelte'); - // @ts-expect-error const js = print(program, { // include source content; makes it easier/more robust looking up the source map code // (else esrap does return null for source and sourceMapContent which may trip up tooling) @@ -95,7 +94,6 @@ export function transform_module(analysis, source, options) { } return { - // @ts-expect-error js: print(program, { // include source content; makes it easier/more robust looking up the source map code // (else esrap does return null for source and sourceMapContent which may trip up tooling) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1de6883a48..f7f38700e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,8 +84,8 @@ importers: specifier: ^1.2.1 version: 1.2.1 esrap: - specifier: ^1.3.1 - version: 1.3.1 + specifier: ^1.3.2 + version: 1.3.2 is-reference: specifier: ^3.0.3 version: 3.0.3 @@ -908,7 +908,7 @@ packages: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} concat-map@0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} cross-spawn@5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} @@ -1111,8 +1111,8 @@ packages: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} - esrap@1.3.1: - resolution: {integrity: sha512-KpAH3+QsDmtOP1KOW04CbD1PgzWsIHjB8tOCk3PCb8xzNGn8XkjI8zl80i09fmXdzQyaS8tcsKCCDzHF7AcowA==} + esrap@1.3.2: + resolution: {integrity: sha512-C4PXusxYhFT98GjLSmb20k9PREuUdporer50dhzGuJu9IJXktbMddVCMLAERl5dAHyAi73GWWCE4FVHGP1794g==} esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} @@ -3315,10 +3315,9 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@1.3.1: + esrap@1.3.2: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - '@typescript-eslint/types': 8.2.0 esrecurse@4.3.0: dependencies: From 69d198eb44f0a2026a3f76c7b70c847983a8529a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 15:58:02 +0000 Subject: [PATCH 0018/1429] Version Packages (#14764) Co-authored-by: github-actions[bot] --- .changeset/cool-ducks-carry.md | 5 ----- .changeset/nine-buses-own.md | 5 ----- .changeset/tough-dingos-deliver.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 .changeset/cool-ducks-carry.md delete mode 100644 .changeset/nine-buses-own.md delete mode 100644 .changeset/tough-dingos-deliver.md diff --git a/.changeset/cool-ducks-carry.md b/.changeset/cool-ducks-carry.md deleted file mode 100644 index 507bb55d13..0000000000 --- a/.changeset/cool-ducks-carry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: bump esrap dependency diff --git a/.changeset/nine-buses-own.md b/.changeset/nine-buses-own.md deleted file mode 100644 index 039f7b2001..0000000000 --- a/.changeset/nine-buses-own.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure svg namespace for `` elements is correct diff --git a/.changeset/tough-dingos-deliver.md b/.changeset/tough-dingos-deliver.md deleted file mode 100644 index 5c0faf703c..0000000000 --- a/.changeset/tough-dingos-deliver.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: treeshake `$inspect.trace` code if unused diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 47a67006c1..5d571ff104 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.14.5 + +### Patch Changes + +- fix: bump esrap dependency ([#14765](https://github.com/sveltejs/svelte/pull/14765)) + +- fix: ensure svg namespace for `` elements is correct ([#14756](https://github.com/sveltejs/svelte/pull/14756)) + +- fix: treeshake `$inspect.trace` code if unused ([#14770](https://github.com/sveltejs/svelte/pull/14770)) + ## 5.14.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 6c919a7308..b69cd565cc 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.14.4", + "version": "5.14.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index e6f7bd2716..1082ce5cc5 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.14.4'; +export const VERSION = '5.14.5'; export const PUBLIC_VERSION = '5'; From 5bc403342177db2ec0b419d93e1e0722388e085c Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Thu, 19 Dec 2024 17:06:22 +0000 Subject: [PATCH 0019/1429] fix: treeshake `$inspect.trace` code if unused in modules (#14774) --- .changeset/modern-colts-drive.md | 5 +++++ packages/svelte/src/compiler/phases/2-analyze/index.js | 7 +++++-- .../phases/3-transform/client/transform-client.js | 8 +++++++- packages/svelte/src/compiler/phases/types.d.ts | 1 + 4 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 .changeset/modern-colts-drive.md diff --git a/.changeset/modern-colts-drive.md b/.changeset/modern-colts-drive.md new file mode 100644 index 0000000000..6c727d220e --- /dev/null +++ b/.changeset/modern-colts-drive.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: treeshake `$inspect.trace` code if unused in modules diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index a4167be3a1..c0e4a65571 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -243,13 +243,15 @@ export function analyze_module(ast, options) { } } + const analysis = { runes: true, tracing: false }; + walk( /** @type {Node} */ (ast), { scope, scopes, // @ts-expect-error TODO - analysis: { runes: true } + analysis }, visitors ); @@ -259,7 +261,8 @@ export function analyze_module(ast, options) { name: options.filename, accessors: false, runes: true, - immutable: true + immutable: true, + tracing: analysis.tracing }; } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 8e05b44a83..90901d29ce 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -671,9 +671,15 @@ export function client_module(analysis, options) { walk(/** @type {AST.SvelteNode} */ (analysis.module.ast), state, visitors) ); + const body = [b.import_all('$', 'svelte/internal/client')]; + + if (analysis.tracing) { + body.push(b.imports([], 'svelte/internal/flags/tracing')); + } + return { type: 'Program', sourceType: 'module', - body: [b.import_all('$', 'svelte/internal/client'), ...module.body] + body: [...body, ...module.body] }; } diff --git a/packages/svelte/src/compiler/phases/types.d.ts b/packages/svelte/src/compiler/phases/types.d.ts index b4dcc270cd..fe32dbba3e 100644 --- a/packages/svelte/src/compiler/phases/types.d.ts +++ b/packages/svelte/src/compiler/phases/types.d.ts @@ -27,6 +27,7 @@ export interface Analysis { name: string; // TODO should this be filename? it's used in `compileModule` as well as `compile` runes: boolean; immutable: boolean; + tracing: boolean; // TODO figure out if we can move this to ComponentAnalysis accessors: boolean; From 999b92d134186bba7e0eeb1de38626f07dcf7260 Mon Sep 17 00:00:00 2001 From: ottomated <31470743+ottomated@users.noreply.github.com> Date: Thu, 19 Dec 2024 10:12:59 -0800 Subject: [PATCH 0020/1429] fix: intellisense DX for $props, $inspect, $bindable, and $host (#14777) * remove default function types for $props, $inspect, $bindable, and $host * changeset * regenerate types * add helper message for type regeneration * append a newline to generated types * prettier --------- Co-authored-by: Rich Harris --- .changeset/three-suits-pay.md | 5 ++ .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- packages/svelte/scripts/generate-types.js | 2 + packages/svelte/src/ambient.d.ts | 90 ++++++++++++++++++++++ packages/svelte/types/index.d.ts | 92 ++++++++++++++++++++++- 6 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 .changeset/three-suits-pay.md diff --git a/.changeset/three-suits-pay.md b/.changeset/three-suits-pay.md new file mode 100644 index 0000000000..51d46395e8 --- /dev/null +++ b/.changeset/three-suits-pay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: Improve typescript DX for $inspect, $props, $bindable, and $host diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fbdd1e420c..b2bcb08848 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: run: pnpm lint - name: build and check generated types if: (${{ success() }} || ${{ failure() }}) # ensures this step runs even if previous steps fail - run: pnpm build && { [ "`git status --porcelain=v1`" == "" ] || (echo "Generated types have changed — please regenerate types locally and commit the changes after you have reviewed them"; git diff; exit 1); } + run: pnpm build && { [ "`git status --porcelain=v1`" == "" ] || (echo "Generated types have changed — please regenerate types locally with `cd packages/svelte && pnpm generate:types` and commit the changes after you have reviewed them"; git diff; exit 1); } Benchmarks: runs-on: ubuntu-latest timeout-minutes: 15 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a17f49bbeb..1daef0b89c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,7 +33,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Build - run: pnpm build && { [ "`git status --porcelain=v1`" == "" ] || (echo "Generated types have changed — please regenerate types locally and commit the changes after you have reviewed them"; git diff; exit 1); } + run: pnpm build && { [ "`git status --porcelain=v1`" == "" ] || (echo "Generated types have changed — please regenerate types locally with `cd packages/svelte && pnpm generate:types` and commit the changes after you have reviewed them"; git diff; exit 1); } - name: Create Release Pull Request or Publish to npm id: changesets diff --git a/packages/svelte/scripts/generate-types.js b/packages/svelte/scripts/generate-types.js index 16bbf52a2e..d44afe8205 100644 --- a/packages/svelte/scripts/generate-types.js +++ b/packages/svelte/scripts/generate-types.js @@ -46,6 +46,8 @@ await createBundle({ } }); +fs.appendFileSync(`${dir}/types/index.d.ts`, '\n'); + const types = fs.readFileSync(`${dir}/types/index.d.ts`, 'utf-8'); const bad_links = [...types.matchAll(/\]\((\/[^)]+)\)/g)]; diff --git a/packages/svelte/src/ambient.d.ts b/packages/svelte/src/ambient.d.ts index 4e98eb82eb..9dbc61c7cb 100644 --- a/packages/svelte/src/ambient.d.ts +++ b/packages/svelte/src/ambient.d.ts @@ -338,6 +338,29 @@ declare namespace $effect { */ declare function $props(): any; +declare namespace $props { + // prevent intellisense from being unhelpful + /** @deprecated */ + export const apply: never; + /** @deprecated */ + // @ts-ignore + export const arguments: never; + /** @deprecated */ + export const bind: never; + /** @deprecated */ + export const call: never; + /** @deprecated */ + export const caller: never; + /** @deprecated */ + export const length: never; + /** @deprecated */ + export const name: never; + /** @deprecated */ + export const prototype: never; + /** @deprecated */ + export const toString: never; +} + /** * Declares a prop as bindable, meaning the parent component can use `bind:propName={value}` to bind to it. * @@ -349,6 +372,29 @@ declare function $props(): any; */ declare function $bindable(fallback?: T): T; +declare namespace $bindable { + // prevent intellisense from being unhelpful + /** @deprecated */ + export const apply: never; + /** @deprecated */ + // @ts-ignore + export const arguments: never; + /** @deprecated */ + export const bind: never; + /** @deprecated */ + export const call: never; + /** @deprecated */ + export const caller: never; + /** @deprecated */ + export const length: never; + /** @deprecated */ + export const name: never; + /** @deprecated */ + export const prototype: never; + /** @deprecated */ + export const toString: never; +} + /** * Inspects one or more values whenever they, or the properties they contain, change. Example: * @@ -388,6 +434,27 @@ declare namespace $inspect { * */ export function trace(name: string): void; + + // prevent intellisense from being unhelpful + /** @deprecated */ + export const apply: never; + /** @deprecated */ + // @ts-ignore + export const arguments: never; + /** @deprecated */ + export const bind: never; + /** @deprecated */ + export const call: never; + /** @deprecated */ + export const caller: never; + /** @deprecated */ + export const length: never; + /** @deprecated */ + export const name: never; + /** @deprecated */ + export const prototype: never; + /** @deprecated */ + export const toString: never; } /** @@ -410,3 +477,26 @@ declare namespace $inspect { * https://svelte.dev/docs/svelte/$host */ declare function $host(): El; + +declare namespace $host { + // prevent intellisense from being unhelpful + /** @deprecated */ + export const apply: never; + /** @deprecated */ + // @ts-ignore + export const arguments: never; + /** @deprecated */ + export const bind: never; + /** @deprecated */ + export const call: never; + /** @deprecated */ + export const caller: never; + /** @deprecated */ + export const length: never; + /** @deprecated */ + export const name: never; + /** @deprecated */ + export const prototype: never; + /** @deprecated */ + export const toString: never; +} diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 671f68bff7..d422abebbc 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -2996,6 +2996,29 @@ declare namespace $effect { */ declare function $props(): any; +declare namespace $props { + // prevent intellisense from being unhelpful + /** @deprecated */ + export const apply: never; + /** @deprecated */ + // @ts-ignore + export const arguments: never; + /** @deprecated */ + export const bind: never; + /** @deprecated */ + export const call: never; + /** @deprecated */ + export const caller: never; + /** @deprecated */ + export const length: never; + /** @deprecated */ + export const name: never; + /** @deprecated */ + export const prototype: never; + /** @deprecated */ + export const toString: never; +} + /** * Declares a prop as bindable, meaning the parent component can use `bind:propName={value}` to bind to it. * @@ -3007,6 +3030,29 @@ declare function $props(): any; */ declare function $bindable(fallback?: T): T; +declare namespace $bindable { + // prevent intellisense from being unhelpful + /** @deprecated */ + export const apply: never; + /** @deprecated */ + // @ts-ignore + export const arguments: never; + /** @deprecated */ + export const bind: never; + /** @deprecated */ + export const call: never; + /** @deprecated */ + export const caller: never; + /** @deprecated */ + export const length: never; + /** @deprecated */ + export const name: never; + /** @deprecated */ + export const prototype: never; + /** @deprecated */ + export const toString: never; +} + /** * Inspects one or more values whenever they, or the properties they contain, change. Example: * @@ -3046,6 +3092,27 @@ declare namespace $inspect { * */ export function trace(name: string): void; + + // prevent intellisense from being unhelpful + /** @deprecated */ + export const apply: never; + /** @deprecated */ + // @ts-ignore + export const arguments: never; + /** @deprecated */ + export const bind: never; + /** @deprecated */ + export const call: never; + /** @deprecated */ + export const caller: never; + /** @deprecated */ + export const length: never; + /** @deprecated */ + export const name: never; + /** @deprecated */ + export const prototype: never; + /** @deprecated */ + export const toString: never; } /** @@ -3069,4 +3136,27 @@ declare namespace $inspect { */ declare function $host(): El; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file +declare namespace $host { + // prevent intellisense from being unhelpful + /** @deprecated */ + export const apply: never; + /** @deprecated */ + // @ts-ignore + export const arguments: never; + /** @deprecated */ + export const bind: never; + /** @deprecated */ + export const call: never; + /** @deprecated */ + export const caller: never; + /** @deprecated */ + export const length: never; + /** @deprecated */ + export const name: never; + /** @deprecated */ + export const prototype: never; + /** @deprecated */ + export const toString: never; +} + +//# sourceMappingURL=index.d.ts.map From 2086c199be592a9200a1f994be6650341ce8ffd6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:01:21 -0500 Subject: [PATCH 0021/1429] Version Packages (#14776) Co-authored-by: github-actions[bot] --- .changeset/modern-colts-drive.md | 5 ----- .changeset/three-suits-pay.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/modern-colts-drive.md delete mode 100644 .changeset/three-suits-pay.md diff --git a/.changeset/modern-colts-drive.md b/.changeset/modern-colts-drive.md deleted file mode 100644 index 6c727d220e..0000000000 --- a/.changeset/modern-colts-drive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: treeshake `$inspect.trace` code if unused in modules diff --git a/.changeset/three-suits-pay.md b/.changeset/three-suits-pay.md deleted file mode 100644 index 51d46395e8..0000000000 --- a/.changeset/three-suits-pay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: Improve typescript DX for $inspect, $props, $bindable, and $host diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 5d571ff104..387162e969 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.14.6 + +### Patch Changes + +- fix: treeshake `$inspect.trace` code if unused in modules ([#14774](https://github.com/sveltejs/svelte/pull/14774)) + +- fix: Improve typescript DX for $inspect, $props, $bindable, and $host ([#14777](https://github.com/sveltejs/svelte/pull/14777)) + ## 5.14.5 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index b69cd565cc..1838e7c673 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.14.5", + "version": "5.14.6", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 1082ce5cc5..5859e82860 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.14.5'; +export const VERSION = '5.14.6'; export const PUBLIC_VERSION = '5'; From 72b7bc7af83b52ef2a0e28f1a0011d7ffcab9df0 Mon Sep 17 00:00:00 2001 From: Bjorn Lu Date: Thu, 19 Dec 2024 23:07:51 +0100 Subject: [PATCH 0022/1429] feat: add "worker" exports condition (#14779) --- .changeset/calm-shrimps-remain.md | 5 +++++ packages/svelte/package.json | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 .changeset/calm-shrimps-remain.md diff --git a/.changeset/calm-shrimps-remain.md b/.changeset/calm-shrimps-remain.md new file mode 100644 index 0000000000..280ebb3444 --- /dev/null +++ b/.changeset/calm-shrimps-remain.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: add "worker" exports condition to better support bundling for worker-based environments diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 1838e7c673..36eb6fa0bc 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -21,6 +21,7 @@ "exports": { ".": { "types": "./types/index.d.ts", + "worker": "./src/index-server.js", "browser": "./src/index-client.js", "default": "./src/index-server.js" }, @@ -64,6 +65,7 @@ }, "./legacy": { "types": "./types/index.d.ts", + "worker": "./src/legacy/legacy-server.js", "browser": "./src/legacy/legacy-client.js", "default": "./src/legacy/legacy-server.js" }, @@ -73,6 +75,7 @@ }, "./reactivity": { "types": "./types/index.d.ts", + "worker": "./src/reactivity/index-server.js", "browser": "./src/reactivity/index-client.js", "default": "./src/reactivity/index-server.js" }, @@ -86,6 +89,7 @@ }, "./store": { "types": "./types/index.d.ts", + "worker": "./src/store/index-server.js", "browser": "./src/store/index-client.js", "default": "./src/store/index-server.js" }, From 1d773ef3a471adb36eb3c992168b21fbaf349562 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:34:10 -0500 Subject: [PATCH 0023/1429] Version Packages (#14782) Co-authored-by: github-actions[bot] --- .changeset/calm-shrimps-remain.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/calm-shrimps-remain.md diff --git a/.changeset/calm-shrimps-remain.md b/.changeset/calm-shrimps-remain.md deleted file mode 100644 index 280ebb3444..0000000000 --- a/.changeset/calm-shrimps-remain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: add "worker" exports condition to better support bundling for worker-based environments diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 387162e969..1e9c3451f3 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.15.0 + +### Minor Changes + +- feat: add "worker" exports condition to better support bundling for worker-based environments ([#14779](https://github.com/sveltejs/svelte/pull/14779)) + ## 5.14.6 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 36eb6fa0bc..05169a7bc2 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.14.6", + "version": "5.15.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 5859e82860..00070ffab0 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.14.6'; +export const VERSION = '5.15.0'; export const PUBLIC_VERSION = '5'; From 38a3ae321ffceccd7da838e09f092af86d1b6dc4 Mon Sep 17 00:00:00 2001 From: Andrey Sitnik Date: Tue, 24 Dec 2024 13:16:03 +0100 Subject: [PATCH 0024/1429] fix: Allow to disable animation prefix (#14822) * fix: Allow to disable animation prefix * Apply suggestions from code review --------- Co-authored-by: Paolo Ricciuti --- .changeset/twenty-shoes-peel.md | 5 +++++ .../src/compiler/phases/2-analyze/css/css-analyze.js | 10 +++++++++- .../svelte/tests/css/samples/global-block/expected.css | 4 ++++ .../svelte/tests/css/samples/global-block/input.svelte | 4 ++++ 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 .changeset/twenty-shoes-peel.md diff --git a/.changeset/twenty-shoes-peel.md b/.changeset/twenty-shoes-peel.md new file mode 100644 index 0000000000..e0a8e0701d --- /dev/null +++ b/.changeset/twenty-shoes-peel.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't include keyframes in global scope in the keyframes to rename diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js index b8c88a1023..ed22838582 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js @@ -28,11 +28,19 @@ function is_global_block_selector(simple_selector) { ); } +/** + * + * @param {Array} path + */ +function is_in_global_block(path) { + return path.some((node) => node.type === 'Rule' && node.metadata.is_global_block); +} + /** @type {CssVisitors} */ const css_visitors = { Atrule(node, context) { if (is_keyframes_node(node)) { - if (!node.prelude.startsWith('-global-')) { + if (!node.prelude.startsWith('-global-') && !is_in_global_block(context.path)) { context.state.keyframes.push(node.prelude); } } diff --git a/packages/svelte/tests/css/samples/global-block/expected.css b/packages/svelte/tests/css/samples/global-block/expected.css index 8a7c493d7c..438749224b 100644 --- a/packages/svelte/tests/css/samples/global-block/expected.css +++ b/packages/svelte/tests/css/samples/global-block/expected.css @@ -74,6 +74,10 @@ animation: svelte-xyz-test 1s; } + .y{ + animation: test-in 1s; + } + @keyframes test-in{ to{ opacity: 1; diff --git a/packages/svelte/tests/css/samples/global-block/input.svelte b/packages/svelte/tests/css/samples/global-block/input.svelte index dc1a754010..a1833636a1 100644 --- a/packages/svelte/tests/css/samples/global-block/input.svelte +++ b/packages/svelte/tests/css/samples/global-block/input.svelte @@ -76,6 +76,10 @@ animation: test 1s; } + .y{ + animation: test-in 1s; + } + @keyframes test-in{ to{ opacity: 1; From 015210a1a8a230383303a2c3aa044916a49d2681 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 24 Dec 2024 14:58:49 +0100 Subject: [PATCH 0025/1429] feat: allow objects/arrays for class attribute (#14714) * WIP * missed * fix * fix * rename, smooth over incompatibilities * spread support + test * docs * types * implement CSS pruning for array/object expressions * beefier static analysis * lint * rename doc * move class after all directive docs * tweak docs - clarify top-level falsy values, stagger examples, demonstrate composition, discourage class: more strongly * changeset * fix * Update documentation/docs/03-template-syntax/18-class.md Co-authored-by: Conduitry * Apply suggestions from code review --------- Co-authored-by: Rich Harris Co-authored-by: Conduitry --- .changeset/thin-panthers-sing.md | 5 ++ .../docs/03-template-syntax/16-class.md | 23 ----- .../docs/03-template-syntax/18-class.md | 90 +++++++++++++++++++ packages/svelte/elements.d.ts | 4 +- packages/svelte/package.json | 1 + .../phases/2-analyze/css/css-prune.js | 4 +- .../compiler/phases/2-analyze/css/utils.js | 75 ++++++++++++++-- .../src/compiler/phases/2-analyze/index.js | 2 + .../phases/2-analyze/visitors/Attribute.js | 13 +++ .../client/visitors/RegularElement.js | 8 +- .../server/visitors/shared/element.js | 31 ++++++- packages/svelte/src/compiler/phases/nodes.js | 3 +- .../svelte/src/compiler/types/template.d.ts | 2 + .../client/dom/elements/attributes.js | 5 ++ .../src/internal/client/dom/elements/class.js | 22 +++-- packages/svelte/src/internal/client/index.js | 2 +- packages/svelte/src/internal/server/index.js | 8 +- .../svelte/src/internal/shared/attributes.js | 14 +++ .../css/samples/clsx-can-prune/_config.js | 20 +++++ .../css/samples/clsx-can-prune/expected.css | 12 +++ .../css/samples/clsx-can-prune/input.svelte | 25 ++++++ .../samples/clsx-cannot-prune-1/expected.css | 2 + .../samples/clsx-cannot-prune-1/input.svelte | 5 ++ .../samples/clsx-cannot-prune-2/expected.css | 2 + .../samples/clsx-cannot-prune-2/input.svelte | 5 ++ .../samples/clsx-cannot-prune-3/expected.css | 2 + .../samples/clsx-cannot-prune-3/input.svelte | 5 ++ .../_config.js | 2 +- .../_config.js | 2 +- .../_config.js | 2 +- .../_config.js | 2 +- .../runtime-runes/samples/clsx/_config.js | 43 +++++++++ .../runtime-runes/samples/clsx/child.svelte | 5 ++ .../runtime-runes/samples/clsx/main.svelte | 33 +++++++ pnpm-lock.yaml | 9 ++ 35 files changed, 433 insertions(+), 55 deletions(-) create mode 100644 .changeset/thin-panthers-sing.md delete mode 100644 documentation/docs/03-template-syntax/16-class.md create mode 100644 documentation/docs/03-template-syntax/18-class.md create mode 100644 packages/svelte/tests/css/samples/clsx-can-prune/_config.js create mode 100644 packages/svelte/tests/css/samples/clsx-can-prune/expected.css create mode 100644 packages/svelte/tests/css/samples/clsx-can-prune/input.svelte create mode 100644 packages/svelte/tests/css/samples/clsx-cannot-prune-1/expected.css create mode 100644 packages/svelte/tests/css/samples/clsx-cannot-prune-1/input.svelte create mode 100644 packages/svelte/tests/css/samples/clsx-cannot-prune-2/expected.css create mode 100644 packages/svelte/tests/css/samples/clsx-cannot-prune-2/input.svelte create mode 100644 packages/svelte/tests/css/samples/clsx-cannot-prune-3/expected.css create mode 100644 packages/svelte/tests/css/samples/clsx-cannot-prune-3/input.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/clsx/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/clsx/child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/clsx/main.svelte diff --git a/.changeset/thin-panthers-sing.md b/.changeset/thin-panthers-sing.md new file mode 100644 index 0000000000..223de002c6 --- /dev/null +++ b/.changeset/thin-panthers-sing.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: allow `class` attribute to be an object or array, using `clsx` diff --git a/documentation/docs/03-template-syntax/16-class.md b/documentation/docs/03-template-syntax/16-class.md deleted file mode 100644 index cecbc7cf20..0000000000 --- a/documentation/docs/03-template-syntax/16-class.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: class: ---- - -The `class:` directive is a convenient way to conditionally set classes on elements, as an alternative to using conditional expressions inside `class` attributes: - -```svelte - -
...
-
...
-``` - -As with other directives, we can use a shorthand when the name of the class coincides with the value: - -```svelte -
...
-``` - -Multiple `class:` directives can be added to a single element: - -```svelte -
...
-``` diff --git a/documentation/docs/03-template-syntax/18-class.md b/documentation/docs/03-template-syntax/18-class.md new file mode 100644 index 0000000000..880a34e9ec --- /dev/null +++ b/documentation/docs/03-template-syntax/18-class.md @@ -0,0 +1,90 @@ +--- +title: class +--- + +There are two ways to set classes on elements: the `class` attribute, and the `class:` directive. + +## Attributes + +Primitive values are treated like any other attribute: + +```svelte +
...
+``` + +> [!NOTE] +> For historical reasons, falsy values (like `false` and `NaN`) are stringified (`class="false"`), though `class={undefined}` (or `null`) cause the attribute to be omitted altogether. In a future version of Svelte, all falsy values will cause `class` to be omitted. + +### Objects and arrays + +Since Svelte 5.16, `class` can be an object or array, and is converted to a string using [clsx](https://github.com/lukeed/clsx). + +If the value is an object, the truthy keys are added: + +```svelte + + + +
...
+``` + +If the value is an array, the truthy values are combined: + +```svelte + +
...
+``` + +Note that whether we're using the array or object form, we can set multiple classes simultaneously with a single condition, which is particularly useful if you're using things like Tailwind. + +Arrays can contain arrays and objects, and clsx will flatten them. This is useful for combining local classes with props, for example: + +```svelte + + + + +``` + +The user of this component has the same flexibility to use a mixture of objects, arrays and strings: + +```svelte + + + + +``` + +## The `class:` directive + +Prior to Svelte 5.16, the `class:` directive was the most convenient way to set classes on elements conditionally. + +```svelte + +
...
+
...
+``` + +As with other directives, we can use a shorthand when the name of the class coincides with the value: + +```svelte +
...
+``` + +> [!NOTE] Unless you're using an older version of Svelte, consider avoiding `class:`, since the attribute is more powerful and composable. diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts index 8800b65172..604403f0a2 100644 --- a/packages/svelte/elements.d.ts +++ b/packages/svelte/elements.d.ts @@ -741,7 +741,7 @@ export interface HTMLAttributes extends AriaAttributes, D accesskey?: string | undefined | null; autocapitalize?: 'characters' | 'off' | 'on' | 'none' | 'sentences' | 'words' | undefined | null; autofocus?: boolean | undefined | null; - class?: string | undefined | null; + class?: string | import('clsx').ClassArray | import('clsx').ClassDictionary | undefined | null; contenteditable?: Booleanish | 'inherit' | 'plaintext-only' | undefined | null; contextmenu?: string | undefined | null; dir?: 'ltr' | 'rtl' | 'auto' | undefined | null; @@ -1522,7 +1522,7 @@ export interface SvelteWindowAttributes extends HTMLAttributes { export interface SVGAttributes extends AriaAttributes, DOMAttributes { // Attributes which also defined in HTMLAttributes className?: string | undefined | null; - class?: string | undefined | null; + class?: string | import('clsx').ClassArray | import('clsx').ClassDictionary | undefined | null; color?: string | undefined | null; height?: number | string | undefined | null; id?: string | undefined | null; diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 05169a7bc2..b9f4d3de09 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -153,6 +153,7 @@ "acorn-typescript": "^1.4.13", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", + "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^1.3.2", "is-reference": "^3.0.3", diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index 35bc675166..ca7476ef7f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -731,7 +731,7 @@ function attribute_matches(node, name, expected_value, operator, case_insensitiv /** @type {string[]} */ let prev_values = []; for (const chunk of chunks) { - const current_possible_values = get_possible_values(chunk); + const current_possible_values = get_possible_values(chunk, name === 'class'); // impossible to find out all combinations if (!current_possible_values) return true; @@ -784,7 +784,7 @@ function attribute_matches(node, name, expected_value, operator, case_insensitiv prev_values.push(current_possible_value); } }); - if (prev_values.length < current_possible_values.size) { + if (prev_values.length < current_possible_values.length) { prev_values.push(' '); } if (prev_values.length > 20) { diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/utils.js b/packages/svelte/src/compiler/phases/2-analyze/css/utils.js index d3fd71ec39..d7544b55c0 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/utils.js @@ -4,14 +4,74 @@ const UNKNOWN = {}; /** * @param {Node} node + * @param {boolean} is_class * @param {Set} set + * @param {boolean} is_nested */ -function gather_possible_values(node, set) { +function gather_possible_values(node, is_class, set, is_nested = false) { + if (set.has(UNKNOWN)) { + // no point traversing any further + return; + } + if (node.type === 'Literal') { set.add(String(node.value)); } else if (node.type === 'ConditionalExpression') { - gather_possible_values(node.consequent, set); - gather_possible_values(node.alternate, set); + gather_possible_values(node.consequent, is_class, set, is_nested); + gather_possible_values(node.alternate, is_class, set, is_nested); + } else if (node.type === 'LogicalExpression') { + if (node.operator === '&&') { + // && is a special case, because the only way the left + // hand value can be included is if it's falsy. this is + // a bit of extra work but it's worth it because + // `class={[condition && 'blah']}` is common, + // and we don't want to deopt on `condition` + const left = new Set(); + gather_possible_values(node.left, is_class, left, is_nested); + + if (left.has(UNKNOWN)) { + // add all non-nullish falsy values, unless this is a `class` attribute that + // will be processed by cslx, in which case falsy values are removed, unless + // they're not inside an array/object (TODO 6.0 remove that last part) + if (!is_class || !is_nested) { + set.add(''); + set.add(false); + set.add(NaN); + set.add(0); // -0 and 0n are also falsy, but stringify to '0' + } + } else { + for (const value of left) { + if (!value && value != undefined && (!is_class || !is_nested)) { + set.add(value); + } + } + } + + gather_possible_values(node.right, is_class, set, is_nested); + } else { + gather_possible_values(node.left, is_class, set, is_nested); + gather_possible_values(node.right, is_class, set, is_nested); + } + } else if (is_class && node.type === 'ArrayExpression') { + for (const entry of node.elements) { + if (entry) { + gather_possible_values(entry, is_class, set, true); + } + } + } else if (is_class && node.type === 'ObjectExpression') { + for (const property of node.properties) { + if ( + property.type === 'Property' && + !property.computed && + (property.key.type === 'Identifier' || property.key.type === 'Literal') + ) { + set.add( + property.key.type === 'Identifier' ? property.key.name : String(property.key.value) + ); + } else { + set.add(UNKNOWN); + } + } } else { set.add(UNKNOWN); } @@ -19,19 +79,20 @@ function gather_possible_values(node, set) { /** * @param {AST.Text | AST.ExpressionTag} chunk - * @returns {Set | null} + * @param {boolean} is_class + * @returns {string[] | null} */ -export function get_possible_values(chunk) { +export function get_possible_values(chunk, is_class) { const values = new Set(); if (chunk.type === 'Text') { values.add(chunk.data); } else { - gather_possible_values(chunk.expression, values); + gather_possible_values(chunk.expression, is_class, values); } if (values.has(UNKNOWN)) return null; - return values; + return [...values].map((value) => String(value)); } /** diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index c0e4a65571..76c1e94277 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -773,6 +773,8 @@ export function analyze_component(root, source, options) { if (attribute.type !== 'Attribute') continue; if (attribute.name.toLowerCase() !== 'class') continue; + // The dynamic class method appends the hash to the end of the class attribute on its own + if (attribute.metadata.needs_clsx) continue outer; class_attribute = attribute; } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js index 6eb9faca6d..9d801e095e 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -38,6 +38,19 @@ export function Attribute(node, context) { mark_subtree_dynamic(context.path); } + // class={[...]} or class={{...}} or `class={x}` need clsx to resolve the classes + if ( + node.name === 'class' && + !Array.isArray(node.value) && + node.value !== true && + node.value.expression.type !== 'Literal' && + node.value.expression.type !== 'TemplateLiteral' && + node.value.expression.type !== 'BinaryExpression' + ) { + mark_subtree_dynamic(context.path); + node.metadata.needs_clsx = true; + } + if (node.value !== true) { for (const chunk of get_attribute_chunks(node.value)) { if (chunk.type !== 'ExpressionTag') continue; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 2c2c287f12..59a6fafbc5 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -553,6 +553,10 @@ function build_element_attribute_update_assignment( let update; if (name === 'class') { + if (attribute.metadata.needs_clsx) { + value = b.call('$.clsx', value); + } + if (attribute.metadata.expression.has_state && has_call) { // ensure we're not creating a separate template effect for this so that // potential class directives are added to the same effect and therefore always apply @@ -561,11 +565,13 @@ function build_element_attribute_update_assignment( value = b.call('$.get', id); has_call = false; } + update = b.stmt( b.call( is_svg ? '$.set_svg_class' : is_mathml ? '$.set_mathml_class' : '$.set_class', node_id, - value + value, + attribute.metadata.needs_clsx ? b.literal(context.state.analysis.css.hash) : undefined ) ); } else if (name === 'value') { diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js index 5ebc647571..d0d800d3cb 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js @@ -86,10 +86,35 @@ export function build_element_attributes(node, context) { } else if (attribute.name !== 'defaultValue' && attribute.name !== 'defaultChecked') { if (attribute.name === 'class') { class_index = attributes.length; - } else if (attribute.name === 'style') { - style_index = attributes.length; + + if (attribute.metadata.needs_clsx) { + const clsx_value = b.call( + '$.clsx', + /** @type {AST.ExpressionTag} */ (attribute.value).expression + ); + attributes.push({ + ...attribute, + value: { + .../** @type {AST.ExpressionTag} */ (attribute.value), + expression: context.state.analysis.css.hash + ? b.binary( + '+', + b.binary('+', clsx_value, b.literal(' ')), + b.literal(context.state.analysis.css.hash) + ) + : clsx_value + } + }); + } else { + attributes.push(attribute); + } + } else { + if (attribute.name === 'style') { + style_index = attributes.length; + } + + attributes.push(attribute); } - attributes.push(attribute); } } else if (attribute.type === 'BindDirective') { if (attribute.name === 'value' && node.name === 'select') continue; diff --git a/packages/svelte/src/compiler/phases/nodes.js b/packages/svelte/src/compiler/phases/nodes.js index 7aa9fecd3a..5066833feb 100644 --- a/packages/svelte/src/compiler/phases/nodes.js +++ b/packages/svelte/src/compiler/phases/nodes.js @@ -45,7 +45,8 @@ export function create_attribute(name, start, end, value) { value, metadata: { expression: create_expression_metadata(), - delegated: null + delegated: null, + needs_clsx: false } }; } diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index 97a25df4a7..8be9aed177 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -482,6 +482,8 @@ export namespace AST { expression: ExpressionMetadata; /** May be set if this is an event attribute */ delegated: null | DelegatedEvent; + /** May be `true` if this is a `class` attribute that needs `clsx` */ + needs_clsx: boolean; }; } diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 9c62d684c1..6656532986 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -13,6 +13,7 @@ import { set_active_effect, set_active_reaction } from '../../runtime.js'; +import { clsx } from '../../../shared/attributes.js'; /** * The value/checked attribute in the template actually corresponds to the defaultValue property, so we need @@ -267,6 +268,10 @@ export function set_attributes( } } + if (next.class) { + next.class = clsx(next.class); + } + if (css_hash !== undefined) { next.class = next.class ? next.class + ' ' + css_hash : css_hash; } diff --git a/packages/svelte/src/internal/client/dom/elements/class.js b/packages/svelte/src/internal/client/dom/elements/class.js index 22f3da0f44..62ffb6d14b 100644 --- a/packages/svelte/src/internal/client/dom/elements/class.js +++ b/packages/svelte/src/internal/client/dom/elements/class.js @@ -3,12 +3,13 @@ import { hydrating } from '../hydration.js'; /** * @param {SVGElement} dom * @param {string} value + * @param {string} [hash] * @returns {void} */ -export function set_svg_class(dom, value) { +export function set_svg_class(dom, value, hash) { // @ts-expect-error need to add __className to patched prototype var prev_class_name = dom.__className; - var next_class_name = to_class(value); + var next_class_name = to_class(value, hash); if (hydrating && dom.getAttribute('class') === next_class_name) { // In case of hydration don't reset the class as it's already correct. @@ -32,12 +33,13 @@ export function set_svg_class(dom, value) { /** * @param {MathMLElement} dom * @param {string} value + * @param {string} [hash] * @returns {void} */ -export function set_mathml_class(dom, value) { +export function set_mathml_class(dom, value, hash) { // @ts-expect-error need to add __className to patched prototype var prev_class_name = dom.__className; - var next_class_name = to_class(value); + var next_class_name = to_class(value, hash); if (hydrating && dom.getAttribute('class') === next_class_name) { // In case of hydration don't reset the class as it's already correct. @@ -61,12 +63,13 @@ export function set_mathml_class(dom, value) { /** * @param {HTMLElement} dom * @param {string} value + * @param {string} [hash] * @returns {void} */ -export function set_class(dom, value) { +export function set_class(dom, value, hash) { // @ts-expect-error need to add __className to patched prototype var prev_class_name = dom.__className; - var next_class_name = to_class(value); + var next_class_name = to_class(value, hash); if (hydrating && dom.className === next_class_name) { // In case of hydration don't reset the class as it's already correct. @@ -79,7 +82,7 @@ export function set_class(dom, value) { // Removing the attribute when the value is only an empty string causes // peformance issues vs simply making the className an empty string. So // we should only remove the class if the the value is nullish. - if (value == null) { + if (value == null && !hash) { dom.removeAttribute('class'); } else { dom.className = next_class_name; @@ -93,10 +96,11 @@ export function set_class(dom, value) { /** * @template V * @param {V} value + * @param {string} [hash] * @returns {string | V} */ -function to_class(value) { - return value == null ? '' : value; +function to_class(value, hash) { + return (value == null ? '' : value) + (hash ? ' ' + hash : ''); } /** diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index f22c33babc..3b85ae1816 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -161,7 +161,7 @@ export { $window as window, $document as document } from './dom/operations.js'; -export { attr } from '../shared/attributes.js'; +export { attr, clsx } from '../shared/attributes.js'; export { snapshot } from '../shared/clone.js'; export { noop, fallback } from '../shared/utils.js'; export { diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index b8371b7e00..89b3c33df8 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -2,7 +2,7 @@ /** @import { Component, Payload, RenderOutput } from '#server' */ /** @import { Store } from '#shared' */ export { FILENAME, HMR } from '../../constants.js'; -import { attr } from '../shared/attributes.js'; +import { attr, clsx } from '../shared/attributes.js'; import { is_promise, noop } from '../shared/utils.js'; import { subscribe_to_store } from '../../store/utils.js'; import { @@ -195,6 +195,10 @@ export function spread_attributes(attrs, classes, styles, flags = 0) { : style_object_to_string(styles); } + if (attrs.class) { + attrs.class = clsx(attrs.class); + } + if (classes) { const classlist = attrs.class ? [attrs.class] : []; @@ -522,7 +526,7 @@ export function once(get_value) { }; } -export { attr }; +export { attr, clsx }; export { html } from './blocks/html.js'; diff --git a/packages/svelte/src/internal/shared/attributes.js b/packages/svelte/src/internal/shared/attributes.js index 867d6ba5d3..a561501bf4 100644 --- a/packages/svelte/src/internal/shared/attributes.js +++ b/packages/svelte/src/internal/shared/attributes.js @@ -1,4 +1,5 @@ import { escape_html } from '../../escaping.js'; +import { clsx as _clsx } from 'clsx'; /** * `
` should be rendered as `
` and _not_ @@ -26,3 +27,16 @@ export function attr(name, value, is_boolean = false) { const assignment = is_boolean ? '' : `="${escape_html(normalized, true)}"`; return ` ${name}${assignment}`; } + +/** + * Small wrapper around clsx to preserve Svelte's (weird) handling of falsy values. + * TODO Svelte 6 revisit this, and likely turn all falsy values into the empty string (what clsx also does) + * @param {any} value + */ +export function clsx(value) { + if (typeof value === 'object') { + return _clsx(value); + } else { + return value ?? ''; + } +} diff --git a/packages/svelte/tests/css/samples/clsx-can-prune/_config.js b/packages/svelte/tests/css/samples/clsx-can-prune/_config.js new file mode 100644 index 0000000000..43c80a318d --- /dev/null +++ b/packages/svelte/tests/css/samples/clsx-can-prune/_config.js @@ -0,0 +1,20 @@ +import { test } from '../../test'; + +export default test({ + warnings: [ + { + code: 'css_unused_selector', + message: 'Unused CSS selector ".unused"\nhttps://svelte.dev/e/css_unused_selector', + start: { + line: 24, + column: 1, + character: 548 + }, + end: { + line: 24, + column: 8, + character: 555 + } + } + ] +}); diff --git a/packages/svelte/tests/css/samples/clsx-can-prune/expected.css b/packages/svelte/tests/css/samples/clsx-can-prune/expected.css new file mode 100644 index 0000000000..620db0f022 --- /dev/null +++ b/packages/svelte/tests/css/samples/clsx-can-prune/expected.css @@ -0,0 +1,12 @@ + + .used1.svelte-xyz { color: green; } + .used2.svelte-xyz { color: green; } + .used3.svelte-xyz { color: green; } + .used4.svelte-xyz { color: green; } + .used5.svelte-xyz { color: green; } + .used6.svelte-xyz { color: green; } + .used7.svelte-xyz { color: green; } + .used8.svelte-xyz { color: green; } + .used9.svelte-xyz { color: green; } + + /* (unused) .unused { color: red; }*/ diff --git a/packages/svelte/tests/css/samples/clsx-can-prune/input.svelte b/packages/svelte/tests/css/samples/clsx-can-prune/input.svelte new file mode 100644 index 0000000000..2c75a39f99 --- /dev/null +++ b/packages/svelte/tests/css/samples/clsx-can-prune/input.svelte @@ -0,0 +1,25 @@ + + +

+

+

+

+

+

+

+ + diff --git a/packages/svelte/tests/css/samples/clsx-cannot-prune-1/expected.css b/packages/svelte/tests/css/samples/clsx-cannot-prune-1/expected.css new file mode 100644 index 0000000000..243bdb2b82 --- /dev/null +++ b/packages/svelte/tests/css/samples/clsx-cannot-prune-1/expected.css @@ -0,0 +1,2 @@ + + .x.svelte-xyz { color: green; } diff --git a/packages/svelte/tests/css/samples/clsx-cannot-prune-1/input.svelte b/packages/svelte/tests/css/samples/clsx-cannot-prune-1/input.svelte new file mode 100644 index 0000000000..d9de0bdfd4 --- /dev/null +++ b/packages/svelte/tests/css/samples/clsx-cannot-prune-1/input.svelte @@ -0,0 +1,5 @@ +

hello world

+ + diff --git a/packages/svelte/tests/css/samples/clsx-cannot-prune-2/expected.css b/packages/svelte/tests/css/samples/clsx-cannot-prune-2/expected.css new file mode 100644 index 0000000000..243bdb2b82 --- /dev/null +++ b/packages/svelte/tests/css/samples/clsx-cannot-prune-2/expected.css @@ -0,0 +1,2 @@ + + .x.svelte-xyz { color: green; } diff --git a/packages/svelte/tests/css/samples/clsx-cannot-prune-2/input.svelte b/packages/svelte/tests/css/samples/clsx-cannot-prune-2/input.svelte new file mode 100644 index 0000000000..212deab4f0 --- /dev/null +++ b/packages/svelte/tests/css/samples/clsx-cannot-prune-2/input.svelte @@ -0,0 +1,5 @@ +

hello world

+ + diff --git a/packages/svelte/tests/css/samples/clsx-cannot-prune-3/expected.css b/packages/svelte/tests/css/samples/clsx-cannot-prune-3/expected.css new file mode 100644 index 0000000000..243bdb2b82 --- /dev/null +++ b/packages/svelte/tests/css/samples/clsx-cannot-prune-3/expected.css @@ -0,0 +1,2 @@ + + .x.svelte-xyz { color: green; } diff --git a/packages/svelte/tests/css/samples/clsx-cannot-prune-3/input.svelte b/packages/svelte/tests/css/samples/clsx-cannot-prune-3/input.svelte new file mode 100644 index 0000000000..2dfa1afd18 --- /dev/null +++ b/packages/svelte/tests/css/samples/clsx-cannot-prune-3/input.svelte @@ -0,0 +1,5 @@ +

hello world

+ + diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-no-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-no-style/_config.js index a7518f7e6c..b2f0396181 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-no-style/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-no-style/_config.js @@ -40,7 +40,7 @@ export default test({ assert.equal(div.className, 'true'); component.testName = {}; - assert.equal(div.className, '[object Object]'); + assert.equal(div.className, ''); component.testName = ''; assert.equal(div.className, ''); diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js index dbab9fd42b..c8710f9038 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js @@ -32,7 +32,7 @@ export default test({ assert.equal(div.className, 'true svelte-x1o6ra'); component.testName = {}; - assert.equal(div.className, '[object Object] svelte-x1o6ra'); + assert.equal(div.className, ' svelte-x1o6ra'); component.testName = ''; assert.equal(div.className, ' svelte-x1o6ra'); diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-no-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-no-style/_config.js index a7518f7e6c..b2f0396181 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-no-style/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-no-style/_config.js @@ -40,7 +40,7 @@ export default test({ assert.equal(div.className, 'true'); component.testName = {}; - assert.equal(div.className, '[object Object]'); + assert.equal(div.className, ''); component.testName = ''; assert.equal(div.className, ''); diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js index 20426dcf4b..8d0f411b8f 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js @@ -40,7 +40,7 @@ export default test({ assert.equal(div.className, 'true svelte-x1o6ra'); component.testName = {}; - assert.equal(div.className, '[object Object] svelte-x1o6ra'); + assert.equal(div.className, ' svelte-x1o6ra'); component.testName = ''; assert.equal(div.className, ' svelte-x1o6ra'); diff --git a/packages/svelte/tests/runtime-runes/samples/clsx/_config.js b/packages/svelte/tests/runtime-runes/samples/clsx/_config.js new file mode 100644 index 0000000000..e0813d0e6c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/clsx/_config.js @@ -0,0 +1,43 @@ +import { test } from '../../test'; + +export default test({ + html: ` +
+
+
+
+
+ +
child
+
child
+
child
+
child
+
child
+ + + `, + test({ assert, target }) { + const button = target.querySelector('button'); + + button?.click(); + + assert.htmlEqual( + target.innerHTML, + ` +
+
+
+
+
+ +
child
+
child
+
child
+
child
+
child
+ + + ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/clsx/child.svelte b/packages/svelte/tests/runtime-runes/samples/clsx/child.svelte new file mode 100644 index 0000000000..1b8be697c0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/clsx/child.svelte @@ -0,0 +1,5 @@ + + +
child
diff --git a/packages/svelte/tests/runtime-runes/samples/clsx/main.svelte b/packages/svelte/tests/runtime-runes/samples/clsx/main.svelte new file mode 100644 index 0000000000..bf68b42e11 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/clsx/main.svelte @@ -0,0 +1,33 @@ + + +
+
+
+
+
+ + + + + + + + + + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f7f38700e4..8bf7bb4224 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,6 +80,9 @@ importers: axobject-query: specifier: ^4.1.0 version: 4.1.0 + clsx: + specifier: ^2.1.1 + version: 2.1.1 esm-env: specifier: ^1.2.1 version: 1.2.1 @@ -890,6 +893,10 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -3055,6 +3062,8 @@ snapshots: ci-info@3.9.0: {} + clsx@2.1.1: {} + color-convert@2.0.1: dependencies: color-name: 1.1.4 From 97f3aa91517cabb05ecf553b5be18bf38dcef1ae Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 24 Dec 2024 09:14:56 -0500 Subject: [PATCH 0026/1429] docs: fix links in some errors/warnings (#14825) * fix links in some errors/warnings * fix --- .../98-reference/.generated/client-errors.md | 8 ++++++-- .../98-reference/.generated/client-warnings.md | 2 +- .../98-reference/.generated/compile-warnings.md | 2 +- packages/svelte/messages/client-errors/errors.md | 8 ++++++-- .../svelte/messages/client-warnings/warnings.md | 2 +- .../svelte/messages/compile-warnings/a11y.md | 2 +- packages/svelte/src/compiler/warnings.js | 4 ++-- packages/svelte/src/internal/client/errors.js | 8 ++++---- .../warnings.json | 16 ++++++++-------- .../validator/samples/slot-warning/warnings.json | 2 +- .../samples/slot-warning2/warnings.json | 2 +- 11 files changed, 32 insertions(+), 24 deletions(-) diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index 90c9c1f9d1..2c2e0707ea 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -21,15 +21,19 @@ A component is attempting to bind to a non-bindable property `%key%` belonging t ### component_api_changed ``` -%parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information +%parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5 ``` +See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more information. + ### component_api_invalid_new ``` -Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `compatibility.componentApi` compiler option to `4` to keep it working. See https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes for more information +Attempted to instantiate %component% with `new %name%`, which is no longer valid in Svelte 5. If this component is not under your control, set the `compatibility.componentApi` compiler option to `4` to keep it working. ``` +See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more information. + ### derived_references_self ``` diff --git a/documentation/docs/98-reference/.generated/client-warnings.md b/documentation/docs/98-reference/.generated/client-warnings.md index 06ca578b76..5218ec4cb1 100644 --- a/documentation/docs/98-reference/.generated/client-warnings.md +++ b/documentation/docs/98-reference/.generated/client-warnings.md @@ -52,7 +52,7 @@ Your `console.%method%` contained `$state` proxies. Consider using `$inspect(... When logging a [proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy), browser devtools will log the proxy itself rather than the value it represents. In the case of Svelte, the 'target' of a `$state` proxy might not resemble its current value, which can be confusing. -The easiest way to log a value as it changes over time is to use the [`$inspect`](https://svelte.dev/docs/svelte/$inspect) rune. Alternatively, to log things on a one-off basis (for example, inside an event handler) you can use [`$state.snapshot`](https://svelte.dev/docs/svelte/$state#$state.snapshot) to take a snapshot of the current value. +The easiest way to log a value as it changes over time is to use the [`$inspect`](/docs/svelte/$inspect) rune. Alternatively, to log things on a one-off basis (for example, inside an event handler) you can use [`$state.snapshot`](/docs/svelte/$state#$state.snapshot) to take a snapshot of the current value. ### event_handler_invalid diff --git a/documentation/docs/98-reference/.generated/compile-warnings.md b/documentation/docs/98-reference/.generated/compile-warnings.md index 3d515305fe..57396bd7fd 100644 --- a/documentation/docs/98-reference/.generated/compile-warnings.md +++ b/documentation/docs/98-reference/.generated/compile-warnings.md @@ -62,7 +62,7 @@ Enforce that `autofocus` is not used on elements. Autofocusing elements can caus ### a11y_click_events_have_key_events ``` -Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as `
diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-trace-null/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-trace-null/_config.js new file mode 100644 index 0000000000..e779a835c2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inspect-trace-null/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + test() {} +}); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-trace-null/main.svelte b/packages/svelte/tests/runtime-runes/samples/inspect-trace-null/main.svelte new file mode 100644 index 0000000000..30eb95f124 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inspect-trace-null/main.svelte @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-trace-reassignment/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-trace-reassignment/_config.js new file mode 100644 index 0000000000..c9a66289a1 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inspect-trace-reassignment/_config.js @@ -0,0 +1,72 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +/** + * @param {any[]} logs + */ +function normalise_trace_logs(logs) { + let normalised = []; + for (let i = 0; i < logs.length; i++) { + const log = logs[i]; + + if (typeof log === 'string' && log.includes('%c')) { + const split = log.split('%c'); + normalised.push({ + log: (split[0].length !== 0 ? split[0] : split[1]).trim(), + highlighted: logs[i + 1] === 'color: CornflowerBlue; font-weight: bold' + }); + i++; + } else if (log instanceof Error) { + continue; + } else { + normalised.push({ log }); + } + } + return normalised; +} + +export default test({ + compileOptions: { + dev: true + }, + + test({ assert, target, logs }) { + // initial log, everything is highlighted + + assert.deepEqual(normalise_trace_logs(logs), [ + { log: 'effect', highlighted: false }, + { log: '$state', highlighted: true }, + { log: false } + ]); + + logs.length = 0; + + const button = target.querySelector('button'); + button?.click(); + flushSync(); + + const input = target.querySelector('input'); + input?.click(); + flushSync(); + + // checked changed, effect reassign state, values should be correct and be correctly highlighted + + assert.deepEqual(normalise_trace_logs(logs), [ + { log: 'effect', highlighted: false }, + { log: '$state', highlighted: true }, + { log: true }, + { log: '$state', highlighted: true }, + { log: 1 }, + { log: 'effect', highlighted: false }, + { log: '$state', highlighted: false }, + { log: true }, + { log: '$state', highlighted: true }, + { log: 2 }, + { log: 'effect', highlighted: false }, + { log: '$state', highlighted: false }, + { log: true }, + { log: '$state', highlighted: true }, + { log: 3 } + ]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-trace-reassignment/main.svelte b/packages/svelte/tests/runtime-runes/samples/inspect-trace-reassignment/main.svelte new file mode 100644 index 0000000000..5028c0f251 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/inspect-trace-reassignment/main.svelte @@ -0,0 +1,18 @@ + + + \ No newline at end of file diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index d422abebbc..b6b76b1cb7 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -3091,7 +3091,7 @@ declare namespace $inspect { * }); * */ - export function trace(name: string): void; + export function trace(name?: string): void; // prevent intellisense from being unhelpful /** @deprecated */ From 82db92f8707f593a0d14c2dab4089f6f36b3ada3 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Sun, 5 Jan 2025 20:25:27 +0000 Subject: [PATCH 0033/1429] fix: ensure disconnected deriveds correctly connect again (#14899) * fix: ensure disconnected deriveds correctly connect again * fix: ensure disconnected deriveds correctly connect again --- .changeset/happy-plums-greet.md | 5 + .../svelte/src/internal/client/runtime.js | 4 +- .../samples/derived-disconnect/_config.js | 96 +++++++++++++++++++ .../samples/derived-disconnect/main.svelte | 23 +++++ 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 .changeset/happy-plums-greet.md create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-disconnect/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-disconnect/main.svelte diff --git a/.changeset/happy-plums-greet.md b/.changeset/happy-plums-greet.md new file mode 100644 index 0000000000..695fbff8fd --- /dev/null +++ b/.changeset/happy-plums-greet.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure disconnected deriveds correctly connect again diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index c5223a6ae4..0be91d78ba 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -207,7 +207,9 @@ export function check_dirtiness(reaction) { for (i = 0; i < length; i++) { dependency = dependencies[i]; - if (!dependency?.reactions?.includes(reaction)) { + // We always re-add all reactions (even duplicates) if the derived was + // previously disconnected + if (is_disconnected || !dependency?.reactions?.includes(reaction)) { (dependency.reactions ??= []).push(reaction); } } diff --git a/packages/svelte/tests/runtime-runes/samples/derived-disconnect/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-disconnect/_config.js new file mode 100644 index 0000000000..76e60b7402 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-disconnect/_config.js @@ -0,0 +1,96 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + let [b1, b2, b3, b4, b5] = target.querySelectorAll('button'); + + b1?.click(); + flushSync(); + + assert.htmlEqual( + target.innerHTML, + `
Current ID: 1
+
Name: a

+
` + ); + + b2?.click(); + flushSync(); + + assert.htmlEqual( + target.innerHTML, + `
Current ID: 2
+
Name: b

+
` + ); + + b3?.click(); + flushSync(); + + assert.htmlEqual( + target.innerHTML, + `
Current ID: 3
+
Name: c

+
` + ); + + b4?.click(); + flushSync(); + + assert.htmlEqual( + target.innerHTML, + `
Current ID: 4
+
Name: d

+
` + ); + + b5?.click(); + flushSync(); + + b5?.click(); + flushSync(); + + [b1, b2, b3, b4, b5] = target.querySelectorAll('button'); + + b1?.click(); + flushSync(); + + assert.htmlEqual( + target.innerHTML, + `
Current ID: 1
+
Name: a

+
` + ); + + b2?.click(); + flushSync(); + + assert.htmlEqual( + target.innerHTML, + `
Current ID: 2
+
Name: b

+
` + ); + + b3?.click(); + flushSync(); + + assert.htmlEqual( + target.innerHTML, + `
Current ID: 3
+
Name: c

+
` + ); + + b4?.click(); + flushSync(); + + assert.htmlEqual( + target.innerHTML, + `
Current ID: 4
+
Name: d

+
` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/derived-disconnect/main.svelte b/packages/svelte/tests/runtime-runes/samples/derived-disconnect/main.svelte new file mode 100644 index 0000000000..8cb6ed2afd --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-disconnect/main.svelte @@ -0,0 +1,23 @@ + + +
+ {#if visible} +
Current ID: {currentId}
+
Name: {currentItem.name}
+ {#each items as item} +
+ {/each} + {/if} +
+
+
From ed26c3f658acb0292f06e6a437d42bb64e804447 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 5 Jan 2025 20:38:17 +0000 Subject: [PATCH 0034/1429] Version Packages (#14888) Co-authored-by: github-actions[bot] --- .changeset/happy-plums-greet.md | 5 ----- .changeset/silver-shoes-rule.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/happy-plums-greet.md delete mode 100644 .changeset/silver-shoes-rule.md diff --git a/.changeset/happy-plums-greet.md b/.changeset/happy-plums-greet.md deleted file mode 100644 index 695fbff8fd..0000000000 --- a/.changeset/happy-plums-greet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure disconnected deriveds correctly connect again diff --git a/.changeset/silver-shoes-rule.md b/.changeset/silver-shoes-rule.md deleted file mode 100644 index b1c44c9007..0000000000 --- a/.changeset/silver-shoes-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly highlight sources reassigned inside `trace` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 277d1631cb..97aad73746 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.16.2 + +### Patch Changes + +- fix: ensure disconnected deriveds correctly connect again ([#14899](https://github.com/sveltejs/svelte/pull/14899)) + +- fix: correctly highlight sources reassigned inside `trace` ([#14811](https://github.com/sveltejs/svelte/pull/14811)) + ## 5.16.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index a054f6c6d9..f4b9a3faff 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.16.1", + "version": "5.16.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 8a57832c96..add463faef 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -6,5 +6,5 @@ * https://svelte.dev/docs/svelte-compiler#svelte-version * @type {string} */ -export const VERSION = '5.16.1'; +export const VERSION = '5.16.2'; export const PUBLIC_VERSION = '5'; From c4e9faad52c09e143148a69c3643d36462175499 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Mon, 6 Jan 2025 11:08:07 +0100 Subject: [PATCH 0035/1429] fix: correctly parse `each` with loose parser (#14887) * fix: correctly parse `each` with loose parser * chore: fix lint * chore: remove unused imports * Apply suggestions from code review * chore: fix lint --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- .changeset/famous-scissors-begin.md | 5 + .../phases/1-parse/read/expression.js | 43 ++- .../src/compiler/phases/1-parse/state/tag.js | 22 +- .../samples/loose-valid-each-as/input.svelte | 7 + .../samples/loose-valid-each-as/output.json | 308 ++++++++++++++++++ 5 files changed, 364 insertions(+), 21 deletions(-) create mode 100644 .changeset/famous-scissors-begin.md create mode 100644 packages/svelte/tests/parser-modern/samples/loose-valid-each-as/input.svelte create mode 100644 packages/svelte/tests/parser-modern/samples/loose-valid-each-as/output.json diff --git a/.changeset/famous-scissors-begin.md b/.changeset/famous-scissors-begin.md new file mode 100644 index 0000000000..96075d0dac --- /dev/null +++ b/.changeset/famous-scissors-begin.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly parse `each` with loose parser diff --git a/packages/svelte/src/compiler/phases/1-parse/read/expression.js b/packages/svelte/src/compiler/phases/1-parse/read/expression.js index 907608b11b..82a667d38c 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/expression.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/expression.js @@ -8,9 +8,31 @@ import { find_matching_bracket } from '../utils/bracket.js'; /** * @param {Parser} parser * @param {string} [opening_token] + * @returns {Expression | undefined} + */ +export function get_loose_identifier(parser, opening_token) { + // Find the next } and treat it as the end of the expression + const end = find_matching_bracket(parser.template, parser.index, opening_token ?? '{'); + if (end) { + const start = parser.index; + parser.index = end; + // We don't know what the expression is and signal this by returning an empty identifier + return { + type: 'Identifier', + start, + end, + name: '' + }; + } +} + +/** + * @param {Parser} parser + * @param {string} [opening_token] + * @param {boolean} [disallow_loose] * @returns {Expression} */ -export default function read_expression(parser, opening_token) { +export default function read_expression(parser, opening_token, disallow_loose) { try { const node = parse_expression_at(parser.template, parser.ts, parser.index); @@ -41,19 +63,12 @@ export default function read_expression(parser, opening_token) { return /** @type {Expression} */ (node); } catch (err) { - if (parser.loose) { - // Find the next } and treat it as the end of the expression - const end = find_matching_bracket(parser.template, parser.index, opening_token ?? '{'); - if (end) { - const start = parser.index; - parser.index = end; - // We don't know what the expression is and signal this by returning an empty identifier - return { - type: 'Identifier', - start, - end, - name: '' - }; + // If we are in an each loop we need the error to be thrown in cases like + // `as { y = z }` so we still throw and handle the error there + if (parser.loose && !disallow_loose) { + const expression = get_loose_identifier(parser, opening_token); + if (expression) { + return expression; } } diff --git a/packages/svelte/src/compiler/phases/1-parse/state/tag.js b/packages/svelte/src/compiler/phases/1-parse/state/tag.js index 7996d64ded..95d7d00677 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -1,13 +1,13 @@ /** @import { ArrowFunctionExpression, Expression, Identifier, Pattern } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { Parser } from '../index.js' */ -import read_pattern from '../read/context.js'; -import read_expression from '../read/expression.js'; -import * as e from '../../../errors.js'; -import { create_fragment } from '../utils/create.js'; import { walk } from 'zimmerframe'; -import { parse_expression_at } from '../acorn.js'; +import * as e from '../../../errors.js'; import { create_expression_metadata } from '../../nodes.js'; +import { parse_expression_at } from '../acorn.js'; +import read_pattern from '../read/context.js'; +import read_expression, { get_loose_identifier } from '../read/expression.js'; +import { create_fragment } from '../utils/create.js'; const regex_whitespace_with_closing_curly_brace = /^\s*}/; @@ -87,7 +87,7 @@ function open(parser) { // we get a valid expression while (!expression) { try { - expression = read_expression(parser); + expression = read_expression(parser, undefined, true); } catch (err) { end = /** @type {any} */ (err).position[0] - 2; @@ -95,7 +95,15 @@ function open(parser) { end -= 1; } - if (end <= start) throw err; + if (end <= start) { + if (parser.loose) { + expression = get_loose_identifier(parser); + if (expression) { + break; + } + } + throw err; + } // @ts-expect-error parser.template is meant to be readonly, this is a special case parser.template = template.slice(0, end); diff --git a/packages/svelte/tests/parser-modern/samples/loose-valid-each-as/input.svelte b/packages/svelte/tests/parser-modern/samples/loose-valid-each-as/input.svelte new file mode 100644 index 0000000000..f2cc7f5e79 --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/loose-valid-each-as/input.svelte @@ -0,0 +1,7 @@ + + +{#each arr as [key, value = 'default']} +
{key}: {value}
+{/each} diff --git a/packages/svelte/tests/parser-modern/samples/loose-valid-each-as/output.json b/packages/svelte/tests/parser-modern/samples/loose-valid-each-as/output.json new file mode 100644 index 0000000000..181f1ba250 --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/loose-valid-each-as/output.json @@ -0,0 +1,308 @@ +{ + "css": null, + "js": [], + "start": 45, + "end": 119, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 43, + "end": 45, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 45, + "end": 119, + "expression": { + "type": "Identifier", + "start": 52, + "end": 55, + "loc": { + "start": { + "line": 5, + "column": 7 + }, + "end": { + "line": 5, + "column": 10 + } + }, + "name": "arr" + }, + "body": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 84, + "end": 86, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 86, + "end": 111, + "name": "div", + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "ExpressionTag", + "start": 91, + "end": 96, + "expression": { + "type": "Identifier", + "start": 92, + "end": 95, + "loc": { + "start": { + "line": 6, + "column": 7 + }, + "end": { + "line": 6, + "column": 10 + } + }, + "name": "key" + } + }, + { + "type": "Text", + "start": 96, + "end": 98, + "raw": ": ", + "data": ": " + }, + { + "type": "ExpressionTag", + "start": 98, + "end": 105, + "expression": { + "type": "Identifier", + "start": 99, + "end": 104, + "loc": { + "start": { + "line": 6, + "column": 14 + }, + "end": { + "line": 6, + "column": 19 + } + }, + "name": "value" + } + } + ] + } + }, + { + "type": "Text", + "start": 111, + "end": 112, + "raw": "\n", + "data": "\n" + } + ] + }, + "context": { + "type": "ArrayPattern", + "start": 59, + "end": 83, + "loc": { + "start": { + "line": 5, + "column": 15 + }, + "end": { + "line": 5, + "column": 39 + } + }, + "elements": [ + { + "type": "Identifier", + "start": 60, + "end": 63, + "loc": { + "start": { + "line": 5, + "column": 16 + }, + "end": { + "line": 5, + "column": 19 + } + }, + "name": "key" + }, + { + "type": "AssignmentPattern", + "start": 65, + "end": 82, + "loc": { + "start": { + "line": 5, + "column": 21 + }, + "end": { + "line": 5, + "column": 38 + } + }, + "left": { + "type": "Identifier", + "start": 65, + "end": 70, + "loc": { + "start": { + "line": 5, + "column": 21 + }, + "end": { + "line": 5, + "column": 26 + } + }, + "name": "value" + }, + "right": { + "type": "Literal", + "start": 73, + "end": 82, + "loc": { + "start": { + "line": 5, + "column": 29 + }, + "end": { + "line": 5, + "column": 38 + } + }, + "value": "default", + "raw": "'default'" + } + } + ] + } + } + ] + }, + "options": null, + "instance": { + "type": "Script", + "start": 0, + "end": 43, + "context": "default", + "content": { + "type": "Program", + "start": 18, + "end": 34, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 0 + } + }, + "body": [ + { + "type": "VariableDeclaration", + "start": 20, + "end": 33, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 14 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 24, + "end": 32, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 13 + } + }, + "id": { + "type": "Identifier", + "start": 24, + "end": 27, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 8 + } + }, + "name": "arr" + }, + "init": { + "type": "ArrayExpression", + "start": 30, + "end": 32, + "loc": { + "start": { + "line": 2, + "column": 11 + }, + "end": { + "line": 2, + "column": 13 + } + }, + "elements": [] + } + } + ], + "kind": "let" + } + ], + "sourceType": "module" + }, + "attributes": [ + { + "type": "Attribute", + "start": 8, + "end": 17, + "name": "lang", + "value": [ + { + "start": 14, + "end": 16, + "type": "Text", + "raw": "ts", + "data": "ts" + } + ] + } + ] + } +} From 2e2b440954a783a836e803f7b2babc054a1a33ad Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:51:32 +0100 Subject: [PATCH 0036/1429] fix: apply `clsx` logic to custom element `class` attributes (#14907) Fixes #14902 --- .changeset/happy-candles-clean.md | 5 +++++ .../3-transform/client/visitors/RegularElement.js | 12 +++++++++++- .../tests/runtime-runes/samples/clsx/_config.js | 14 ++++++++++---- .../tests/runtime-runes/samples/clsx/main.svelte | 2 ++ 4 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 .changeset/happy-candles-clean.md diff --git a/.changeset/happy-candles-clean.md b/.changeset/happy-candles-clean.md new file mode 100644 index 0000000000..d08f97a247 --- /dev/null +++ b/.changeset/happy-candles-clean.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: apply `clsx` logic to custom element `class` attributes diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 59a6fafbc5..ffd06dfd86 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -571,7 +571,9 @@ function build_element_attribute_update_assignment( is_svg ? '$.set_svg_class' : is_mathml ? '$.set_mathml_class' : '$.set_class', node_id, value, - attribute.metadata.needs_clsx ? b.literal(context.state.analysis.css.hash) : undefined + attribute.metadata.needs_clsx && context.state.analysis.css.hash + ? b.literal(context.state.analysis.css.hash) + : undefined ) ); } else if (name === 'value') { @@ -648,6 +650,14 @@ function build_custom_element_attribute_update_assignment(node_id, attribute, co const name = attribute.name; // don't lowercase, as we set the element's property, which might be case sensitive let { has_call, value } = build_attribute_value(attribute.value, context); + // We assume that noone's going to redefine the semantics of the class attribute on custom elements, i.e. it's still used for CSS classes + if (name === 'class' && attribute.metadata.needs_clsx) { + if (context.state.analysis.css.hash) { + value = b.array([value, b.literal(context.state.analysis.css.hash)]); + } + value = b.call('$.clsx', value); + } + const update = b.stmt(b.call('$.set_custom_element_data', node_id, b.literal(name), value)); if (attribute.metadata.expression.has_state) { diff --git a/packages/svelte/tests/runtime-runes/samples/clsx/_config.js b/packages/svelte/tests/runtime-runes/samples/clsx/_config.js index e0813d0e6c..202a13b1cb 100644 --- a/packages/svelte/tests/runtime-runes/samples/clsx/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/clsx/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -14,27 +15,32 @@ export default test({
child
child
+ + `, test({ assert, target }) { const button = target.querySelector('button'); button?.click(); + flushSync(); assert.htmlEqual( target.innerHTML, `
-
-
+
+
child
child
+
child
+
child
child
-
child
-
child
+ + ` diff --git a/packages/svelte/tests/runtime-runes/samples/clsx/main.svelte b/packages/svelte/tests/runtime-runes/samples/clsx/main.svelte index bf68b42e11..ebc0697f97 100644 --- a/packages/svelte/tests/runtime-runes/samples/clsx/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/clsx/main.svelte @@ -18,6 +18,8 @@ + + \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/bindable-prop-and-export/_config.js b/packages/svelte/tests/runtime-runes/samples/bindable-prop-and-export/_config.js new file mode 100644 index 0000000000..45cd58f55c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/bindable-prop-and-export/_config.js @@ -0,0 +1,35 @@ +import { ok, test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + compileOptions: { + dev: true + }, + html: ``, + ssrHtml: ``, + + async test({ assert, target, instance }) { + const [btn1, btn2] = target.querySelectorAll('button'); + const input = target.querySelector('input'); + flushSync(() => { + btn1.click(); + }); + assert.equal(btn1.innerHTML, 'false'); + assert.equal(btn2.innerHTML, 'false'); + assert.equal(input?.checked, false); + + flushSync(() => { + btn2.click(); + }); + assert.equal(btn1.innerHTML, 'true'); + assert.equal(btn2.innerHTML, 'true'); + assert.equal(input?.checked, true); + + flushSync(() => { + input?.click(); + }); + assert.equal(btn1.innerHTML, 'false'); + assert.equal(btn2.innerHTML, 'false'); + assert.equal(input?.checked, false); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/bindable-prop-and-export/main.svelte b/packages/svelte/tests/runtime-runes/samples/bindable-prop-and-export/main.svelte new file mode 100644 index 0000000000..3b47eef156 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/bindable-prop-and-export/main.svelte @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file From fb67bad5b04a96e416b7896f031353e18b660acb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 18:28:50 -0500 Subject: [PATCH 0046/1429] Version Packages (#14921) Co-authored-by: github-actions[bot] --- .changeset/real-onions-confess.md | 5 ----- .changeset/tidy-dragons-thank.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/real-onions-confess.md delete mode 100644 .changeset/tidy-dragons-thank.md diff --git a/.changeset/real-onions-confess.md b/.changeset/real-onions-confess.md deleted file mode 100644 index 5f11b225ad..0000000000 --- a/.changeset/real-onions-confess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: inherit correct namespace for `` elements diff --git a/.changeset/tidy-dragons-thank.md b/.changeset/tidy-dragons-thank.md deleted file mode 100644 index 1257ec0210..0000000000 --- a/.changeset/tidy-dragons-thank.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: don't throw `bind_invalid_export` if there's also a bindable prop with the same name diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 43898eb107..cc834a7ca3 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.16.5 + +### Patch Changes + +- fix: inherit correct namespace for `<title>` elements ([#14817](https://github.com/sveltejs/svelte/pull/14817)) + +- fix: don't throw `bind_invalid_export` if there's also a bindable prop with the same name ([#14813](https://github.com/sveltejs/svelte/pull/14813)) + ## 5.16.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 59bc861560..682cf30fb3 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.16.4", + "version": "5.16.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b51c659b31..d4fd729b38 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.16.4'; +export const VERSION = '5.16.5'; export const PUBLIC_VERSION = '5'; From 8201d7ad8b4e90f420aefbfe5f0f94fe860a0578 Mon Sep 17 00:00:00 2001 From: Felipe <felipe.santoz2013@gmail.com> Date: Tue, 7 Jan 2025 00:41:06 +0000 Subject: [PATCH 0047/1429] docs: Switch typing of Action<Parameter> from null to undefined in 12-use.md (#14798) * Update 12-use.md - * Remove unecessary line in Action typing example * drive-by formatting fix --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- documentation/docs/03-template-syntax/12-use.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/documentation/docs/03-template-syntax/12-use.md b/documentation/docs/03-template-syntax/12-use.md index f3db72a772..45de023578 100644 --- a/documentation/docs/03-template-syntax/12-use.md +++ b/documentation/docs/03-template-syntax/12-use.md @@ -50,17 +50,16 @@ The `Action` interface receives three optional type arguments — a node type (w ```svelte <!--- file: App.svelte ---> <script> - import { on } from 'svelte/events'; - /** * @type {import('svelte/action').Action< * HTMLDivElement, - * null, + * undefined, * { * onswiperight: (e: CustomEvent) => void; * onswipeleft: (e: CustomEvent) => void; * // ... - * }>} + * } + * >} */ function gestures(node) { $effect(() => { From 08a9d123e81725189e727b18b1f99662ec2a48c8 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Tue, 7 Jan 2025 09:01:44 +0000 Subject: [PATCH 0048/1429] chore: use codePointAt instead of implementing it ourselves (#14923) seems to be some sort of codePointAt polyfill from ancient times before that was available on String.prototype --- .../src/compiler/phases/1-parse/index.js | 5 +- .../compiler/phases/1-parse/read/context.js | 44 ++++++++-------- .../compiler/phases/1-parse/utils/bracket.js | 50 +++++++------------ .../phases/1-parse/utils/full_char_code_at.js | 15 ------ 4 files changed, 41 insertions(+), 73 deletions(-) delete mode 100644 packages/svelte/src/compiler/phases/1-parse/utils/full_char_code_at.js diff --git a/packages/svelte/src/compiler/phases/1-parse/index.js b/packages/svelte/src/compiler/phases/1-parse/index.js index 390919f5cd..6cc5b58aa6 100644 --- a/packages/svelte/src/compiler/phases/1-parse/index.js +++ b/packages/svelte/src/compiler/phases/1-parse/index.js @@ -3,7 +3,6 @@ import { isIdentifierStart, isIdentifierChar } from 'acorn'; import fragment from './state/fragment.js'; import { regex_whitespace } from '../patterns.js'; -import full_char_code_at from './utils/full_char_code_at.js'; import * as e from '../../errors.js'; import { create_fragment } from './utils/create.js'; import read_options from './read/options.js'; @@ -230,13 +229,13 @@ export class Parser { let i = this.index; - const code = full_char_code_at(this.template, i); + const code = /** @type {number} */ (this.template.codePointAt(i)); if (!isIdentifierStart(code, true)) return null; i += code <= 0xffff ? 1 : 2; while (i < this.template.length) { - const code = full_char_code_at(this.template, i); + const code = /** @type {number} */ (this.template.codePointAt(i)); if (!isIdentifierChar(code, true)) break; i += code <= 0xffff ? 1 : 2; diff --git a/packages/svelte/src/compiler/phases/1-parse/read/context.js b/packages/svelte/src/compiler/phases/1-parse/read/context.js index e408b5024f..e206701500 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/context.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/context.js @@ -1,15 +1,7 @@ /** @import { Location } from 'locate-character' */ /** @import { Pattern } from 'estree' */ /** @import { Parser } from '../index.js' */ -// @ts-expect-error acorn type definitions are borked in the release we use -import { isIdentifierStart } from 'acorn'; -import full_char_code_at from '../utils/full_char_code_at.js'; -import { - is_bracket_open, - is_bracket_close, - is_bracket_pair, - get_bracket_close -} from '../utils/bracket.js'; +import { is_bracket_open, is_bracket_close, get_bracket_close } from '../utils/bracket.js'; import { parse_expression_at } from '../acorn.js'; import { regex_not_newline_characters } from '../../patterns.js'; import * as e from '../../../errors.js'; @@ -23,9 +15,9 @@ export default function read_pattern(parser) { const start = parser.index; let i = parser.index; - const code = full_char_code_at(parser.template, i); - if (isIdentifierStart(code, true)) { - const name = /** @type {string} */ (parser.read_identifier()); + const name = parser.read_identifier(); + + if (name !== null) { const annotation = read_type_annotation(parser); return { @@ -41,28 +33,32 @@ export default function read_pattern(parser) { }; } - if (!is_bracket_open(code)) { + if (!is_bracket_open(parser.template[i])) { e.expected_pattern(i); } - const bracket_stack = [code]; - i += code <= 0xffff ? 1 : 2; + /** @type {string[]} */ + const bracket_stack = []; while (i < parser.template.length) { - const code = full_char_code_at(parser.template, i); - if (is_bracket_open(code)) { - bracket_stack.push(code); - } else if (is_bracket_close(code)) { - const popped = /** @type {number} */ (bracket_stack.pop()); - if (!is_bracket_pair(popped, code)) { - e.expected_token(i, String.fromCharCode(/** @type {number} */ (get_bracket_close(popped)))); + const char = parser.template[i]; + + if (is_bracket_open(char)) { + bracket_stack.push(char); + } else if (is_bracket_close(char)) { + const popped = /** @type {string} */ (bracket_stack.pop()); + const expected = /** @type {string} */ (get_bracket_close(popped)); + + if (char !== expected) { + e.expected_token(i, expected); } + if (bracket_stack.length === 0) { - i += code <= 0xffff ? 1 : 2; + i += 1; break; } } - i += code <= 0xffff ? 1 : 2; + i += 1; } parser.index = i; diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js b/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js index e7576af955..b7c8cb43cd 100644 --- a/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js +++ b/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js @@ -1,41 +1,30 @@ -import full_char_code_at from './full_char_code_at.js'; +const SQUARE_BRACKET_OPEN = '['; +const SQUARE_BRACKET_CLOSE = ']'; +const CURLY_BRACKET_OPEN = '{'; +const CURLY_BRACKET_CLOSE = '}'; +const PARENTHESES_OPEN = '('; +const PARENTHESES_CLOSE = ')'; -const SQUARE_BRACKET_OPEN = '['.charCodeAt(0); -const SQUARE_BRACKET_CLOSE = ']'.charCodeAt(0); -const CURLY_BRACKET_OPEN = '{'.charCodeAt(0); -const CURLY_BRACKET_CLOSE = '}'.charCodeAt(0); -const PARENTHESES_OPEN = '('.charCodeAt(0); -const PARENTHESES_CLOSE = ')'.charCodeAt(0); - -/** @param {number} code */ -export function is_bracket_open(code) { - return code === SQUARE_BRACKET_OPEN || code === CURLY_BRACKET_OPEN; +/** @param {string} char */ +export function is_bracket_open(char) { + return char === SQUARE_BRACKET_OPEN || char === CURLY_BRACKET_OPEN; } -/** @param {number} code */ -export function is_bracket_close(code) { - return code === SQUARE_BRACKET_CLOSE || code === CURLY_BRACKET_CLOSE; +/** @param {string} char */ +export function is_bracket_close(char) { + return char === SQUARE_BRACKET_CLOSE || char === CURLY_BRACKET_CLOSE; } -/** - * @param {number} open - * @param {number} close - */ -export function is_bracket_pair(open, close) { - return ( - (open === SQUARE_BRACKET_OPEN && close === SQUARE_BRACKET_CLOSE) || - (open === CURLY_BRACKET_OPEN && close === CURLY_BRACKET_CLOSE) - ); -} - -/** @param {number} open */ +/** @param {string} open */ export function get_bracket_close(open) { if (open === SQUARE_BRACKET_OPEN) { return SQUARE_BRACKET_CLOSE; } + if (open === CURLY_BRACKET_OPEN) { return CURLY_BRACKET_CLOSE; } + if (open === PARENTHESES_OPEN) { return PARENTHESES_CLOSE; } @@ -132,8 +121,7 @@ function count_leading_backslashes(string, search_start_index) { * @returns {number | undefined} The index of the closing bracket, or undefined if not found. */ export function find_matching_bracket(template, index, open) { - const open_code = full_char_code_at(open, 0); - const close_code = get_bracket_close(open_code); + const close = get_bracket_close(open); let brackets = 1; let i = index; while (brackets > 0 && i < template.length) { @@ -159,10 +147,10 @@ export function find_matching_bracket(template, index, open) { continue; } default: { - const code = full_char_code_at(template, i); - if (code === open_code) { + const char = template[i]; + if (char === open) { brackets++; - } else if (code === close_code) { + } else if (char === close) { brackets--; } if (brackets === 0) { diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/full_char_code_at.js b/packages/svelte/src/compiler/phases/1-parse/utils/full_char_code_at.js deleted file mode 100644 index 90e6b8def3..0000000000 --- a/packages/svelte/src/compiler/phases/1-parse/utils/full_char_code_at.js +++ /dev/null @@ -1,15 +0,0 @@ -// Adapted from https://github.com/acornjs/acorn/blob/6584815dca7440e00de841d1dad152302fdd7ca5/src/tokenize.js -// Reproduced under MIT License https://github.com/acornjs/acorn/blob/master/LICENSE - -/** - * @param {string} str - * @param {number} i - * @returns {number} - */ -export default function full_char_code_at(str, i) { - const code = str.charCodeAt(i); - if (code <= 0xd7ff || code >= 0xe000) return code; - - const next = str.charCodeAt(i + 1); - return (code << 10) + next - 0x35fdc00; -} From 77378688b9b3cb5047e0b33bf6ab630e2d62a85d Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Tue, 7 Jan 2025 10:06:11 +0000 Subject: [PATCH 0049/1429] fix: remove leading newline from `<pre>` contents (#14922) ... if it's not followed by another newline, according to the spec Fixes #14767 --------- Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com> --- .changeset/tricky-radios-vanish.md | 5 ++++ .../src/compiler/phases/3-transform/utils.js | 17 ++++++++++++++ .../samples/pre-first-node-newline/_config.js | 5 ++++ .../pre-first-node-newline/_expected.html | 7 ++++++ .../pre-first-node-newline/main.svelte | 23 +++++++++++++++++++ .../runtime-legacy/samples/pre-tag/_config.js | 18 ++++----------- 6 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 .changeset/tricky-radios-vanish.md create mode 100644 packages/svelte/tests/hydration/samples/pre-first-node-newline/_config.js create mode 100644 packages/svelte/tests/hydration/samples/pre-first-node-newline/_expected.html create mode 100644 packages/svelte/tests/hydration/samples/pre-first-node-newline/main.svelte diff --git a/.changeset/tricky-radios-vanish.md b/.changeset/tricky-radios-vanish.md new file mode 100644 index 0000000000..0935148fba --- /dev/null +++ b/.changeset/tricky-radios-vanish.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: remove leading newline from `<pre>` contents diff --git a/packages/svelte/src/compiler/phases/3-transform/utils.js b/packages/svelte/src/compiler/phases/3-transform/utils.js index b53a3110bd..62a635de35 100644 --- a/packages/svelte/src/compiler/phases/3-transform/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/utils.js @@ -5,6 +5,7 @@ import { regex_ends_with_whitespaces, regex_not_whitespace, + regex_starts_with_newline, regex_starts_with_whitespaces } from '../patterns.js'; import * as b from '../../utils/builders.js'; @@ -270,6 +271,22 @@ export function clean_nodes( var first = trimmed[0]; + // initial newline inside a `<pre>` is disregarded, if not followed by another newline + if (parent.type === 'RegularElement' && parent.name === 'pre' && first.type === 'Text') { + const text = first.data.replace(regex_starts_with_newline, ''); + if (text !== first.data) { + const tmp = text.replace(regex_starts_with_newline, ''); + if (text === tmp) { + first.data = text; + first.raw = first.raw.replace(regex_starts_with_newline, ''); + if (first.data === '') { + trimmed.shift(); + first = trimmed[0]; + } + } + } + } + // Special case: Add a comment if this is a lone script tag. This ensures that our run_scripts logic in template.js // will always be able to call node.replaceWith() on the script tag in order to make it run. If we don't add this // and would still call node.replaceWith() on the script tag, it would be a no-op because the script tag has no parent. diff --git a/packages/svelte/tests/hydration/samples/pre-first-node-newline/_config.js b/packages/svelte/tests/hydration/samples/pre-first-node-newline/_config.js new file mode 100644 index 0000000000..c838aab749 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/pre-first-node-newline/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +// A note about _expected.html: It is different from body.html because we're +// testing against target.innerHTML which already removed the redundant first newline +export default test({}); diff --git a/packages/svelte/tests/hydration/samples/pre-first-node-newline/_expected.html b/packages/svelte/tests/hydration/samples/pre-first-node-newline/_expected.html new file mode 100644 index 0000000000..26de34d1ed --- /dev/null +++ b/packages/svelte/tests/hydration/samples/pre-first-node-newline/_expected.html @@ -0,0 +1,7 @@ +<!--[--><pre>static content no line</pre> <pre> static content ignored line +</pre> <pre> + static content relevant line +</pre> <pre><div><span></span></div> +</pre> <pre> +<div><span></span></div> +</pre><!--]--> diff --git a/packages/svelte/tests/hydration/samples/pre-first-node-newline/main.svelte b/packages/svelte/tests/hydration/samples/pre-first-node-newline/main.svelte new file mode 100644 index 0000000000..9764b49e99 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/pre-first-node-newline/main.svelte @@ -0,0 +1,23 @@ +<script> + let name = $state(''); +</script> + +<pre>static content no line</pre> + +<pre> + static content ignored line +</pre> + +<pre> + + static content relevant line +</pre> + +<pre> +<div><span>{name}</span></div> +</pre> + +<pre> + +<div><span>{name}</span></div> +</pre> diff --git a/packages/svelte/tests/runtime-legacy/samples/pre-tag/_config.js b/packages/svelte/tests/runtime-legacy/samples/pre-tag/_config.js index f1a42e996a..d7e1fe9d43 100644 --- a/packages/svelte/tests/runtime-legacy/samples/pre-tag/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/pre-tag/_config.js @@ -2,17 +2,9 @@ import { test } from '../../test'; export default test({ mode: ['client', 'server'], // output is correct, but test suite chokes on the extra ssr comment which is harmless - withoutNormalizeHtml: true, - html: get_html(false), - ssrHtml: get_html(true) -}); - -/** @param {boolean} ssr */ -function get_html(ssr) { - // ssr rendered HTML has an extra newline prefixed within `<pre>` tag, - // if the <pre> tag starts with `\n` - // because when browser parses the SSR rendered HTML, it will ignore the 1st '\n' character - return `${ssr ? '<!--[-->' : ''}<pre id="pre"> A + withoutNormalizeHtml: 'only-strip-comments', // because whitespace inside pre tags is significant + // Note how we're testing against target.innerHTML which already removed the redundant first newline + html: `<pre id="pre"> A B <span> C @@ -35,5 +27,5 @@ function get_html(ssr) { leading newlines</pre></div> <div id="pre-without-leading-newline"><pre>without spaces</pre> <pre> with spaces </pre> <pre>${' '} newline after leading space</pre></div> <pre id="pre-with-multiple-leading-newlines"> -multiple leading newlines</pre>${ssr ? '<!--]-->' : ''}`; -} +multiple leading newlines</pre>` +}); From a1f371e78656f951ad75945493a798716ecc92c4 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 7 Jan 2025 18:06:12 +0100 Subject: [PATCH 0050/1429] docs: add code of files being tested (#14925) * docs: add code of files being tested closes #14900 * fix * Apply suggestions from code review Co-authored-by: Rich Harris <rich.harris@vercel.com> * Update documentation/docs/07-misc/02-testing.md Co-authored-by: Rich Harris <rich.harris@vercel.com> * Apply suggestions from code review Co-authored-by: Rich Harris <rich.harris@vercel.com> * rename import * from https://github.com/sveltejs/svelte.dev/pull/1094 --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- documentation/docs/02-runes/02-$state.md | 7 +-- documentation/docs/07-misc/02-testing.md | 61 +++++++++++++++++++++++- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/documentation/docs/02-runes/02-$state.md b/documentation/docs/02-runes/02-$state.md index 77140dc690..49e17cd08f 100644 --- a/documentation/docs/02-runes/02-$state.md +++ b/documentation/docs/02-runes/02-$state.md @@ -44,12 +44,7 @@ todos[0].done = !todos[0].done; If you push a new object to the array, it will also be proxified: ```js -// @filename: ambient.d.ts -declare global { - const todos: Array<{ done: boolean, text: string }> -} - -// @filename: index.js +let todos = [{ done: false, text: 'add more todos' }]; // ---cut--- todos.push({ done: false, diff --git a/documentation/docs/07-misc/02-testing.md b/documentation/docs/07-misc/02-testing.md index c8774e341f..0842019039 100644 --- a/documentation/docs/07-misc/02-testing.md +++ b/documentation/docs/07-misc/02-testing.md @@ -40,7 +40,7 @@ You can now write unit tests for code inside your `.js/.ts` files: /// file: multiplier.svelte.test.js import { flushSync } from 'svelte'; import { expect, test } from 'vitest'; -import { multiplier } from './multiplier.js'; +import { multiplier } from './multiplier.svelte.js'; test('Multiplier', () => { let double = multiplier(0, 2); @@ -53,9 +53,30 @@ test('Multiplier', () => { }); ``` +```js +/// file: multiplier.svelte.js +/** + * @param {number} initial + * @param {number} k + */ +export function multiplier(initial, k) { + let count = $state(initial); + + return { + get value() { + return count * k; + }, + /** @param {number} c */ + set: (c) => { + count = c; + } + }; +} +``` + ### Using runes inside your test files -It is possible to use runes inside your test files. First ensure your bundler knows to route the file through the Svelte compiler before running the test by adding `.svelte` to the filename (e.g `multiplier.svelte.test.js`). After that, you can use runes inside your tests. +Since Vitest processes your test files the same way as your source files, you can use runes inside your tests as long as the filename includes `.svelte`: ```js /// file: multiplier.svelte.test.js @@ -75,6 +96,21 @@ test('Multiplier', () => { }); ``` +```js +/// file: multiplier.svelte.js +/** + * @param {() => number} getCount + * @param {number} k + */ +export function multiplier(getCount, k) { + return { + get value() { + return getCount() * k; + } + }; +} +``` + If the code being tested uses effects, you need to wrap the test inside `$effect.root`: ```js @@ -105,6 +141,27 @@ test('Effect', () => { }); ``` +```js +/// file: logger.svelte.js +/** + * @param {() => any} getValue + */ +export function logger(getValue) { + /** @type {any[]} */ + let log = $state([]); + + $effect(() => { + log.push(getValue()); + }); + + return { + get value() { + return log; + } + }; +} +``` + ### Component testing It is possible to test your components in isolation using Vitest. From c8865bb4a70ac026b1e85c4f8e8491f2e354ce7b Mon Sep 17 00:00:00 2001 From: Matthijs <42407879+TheSpeedM@users.noreply.github.com> Date: Tue, 7 Jan 2025 20:24:26 +0100 Subject: [PATCH 0051/1429] fix: make Tween with duration 0 set current to target immediately (#14937) * fix: Make Tween duration 0 set current to target immediately * Run prettier on test file * tweak --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/cold-cups-act.md | 5 +++++ packages/svelte/src/motion/tweened.js | 11 ++++++++--- packages/svelte/tests/motion/test.ts | 18 +++++++++++++++++- 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 .changeset/cold-cups-act.md diff --git a/.changeset/cold-cups-act.md b/.changeset/cold-cups-act.md new file mode 100644 index 0000000000..6776d4eef3 --- /dev/null +++ b/.changeset/cold-cups-act.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: Make Tween duration 0 set current to target immediately diff --git a/packages/svelte/src/motion/tweened.js b/packages/svelte/src/motion/tweened.js index 8aaefe5a2c..36d32d1f72 100644 --- a/packages/svelte/src/motion/tweened.js +++ b/packages/svelte/src/motion/tweened.js @@ -230,9 +230,6 @@ export class Tween { set(value, options) { set(this.#target, value); - let previous_task = this.#task; - - let started = false; let { delay = 0, duration = 400, @@ -240,10 +237,18 @@ export class Tween { interpolate = get_interpolator } = { ...this.#defaults, ...options }; + if (duration === 0) { + this.#task?.abort(); + set(this.#current, value); + return Promise.resolve(); + } + const start = raf.now() + delay; /** @type {(t: number) => T} */ let fn; + let started = false; + let previous_task = this.#task; this.#task = loop((now) => { if (now < start) { diff --git a/packages/svelte/tests/motion/test.ts b/packages/svelte/tests/motion/test.ts index b6554e5e56..7d845bac3d 100644 --- a/packages/svelte/tests/motion/test.ts +++ b/packages/svelte/tests/motion/test.ts @@ -2,7 +2,7 @@ import '../helpers.js'; // for the matchMedia polyfill import { describe, it, assert } from 'vitest'; import { get } from 'svelte/store'; -import { spring, tweened } from 'svelte/motion'; +import { spring, tweened, Tween } from 'svelte/motion'; describe('motion', () => { describe('spring', () => { @@ -39,4 +39,20 @@ describe('motion', () => { assert.equal(get(size), 20); }); }); + + describe('Tween', () => { + it('sets immediately when duration is 0', () => { + const size = new Tween(0); + + size.set(100, { duration: 0 }); + assert.equal(size.current, 100); + }); + }); + + it('updates correctly when initialized with a `null`-ish value', () => { + const size = new Tween(undefined as unknown as number, { duration: 0 }); + + size.set(10); + assert.equal(size.current, 10); + }); }); From 8241096b06d5dfb2fc5374ce704e6b30c01e26dd Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 7 Jan 2025 20:24:53 +0100 Subject: [PATCH 0052/1429] fix: treat `inert` as a boolean attribute (#14935) * fix: treat `inert` as a boolean attribute fixes #14731 * remove solo: true --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/quiet-planets-carry.md | 5 +++++ packages/svelte/src/utils.js | 2 +- .../samples/attribute-boolean-inert/_config.js | 17 ++++++++++++----- .../samples/attribute-boolean-inert/main.svelte | 1 + 4 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 .changeset/quiet-planets-carry.md diff --git a/.changeset/quiet-planets-carry.md b/.changeset/quiet-planets-carry.md new file mode 100644 index 0000000000..0abeffa2e8 --- /dev/null +++ b/.changeset/quiet-planets-carry.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: treat `inert` as a boolean attribute diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index e42721b4f4..76486d32ac 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -156,6 +156,7 @@ const DOM_BOOLEAN_ATTRIBUTES = [ 'formnovalidate', 'hidden', 'indeterminate', + 'inert', 'ismap', 'loop', 'multiple', @@ -214,7 +215,6 @@ const DOM_PROPERTIES = [ 'playsInline', 'readOnly', 'value', - 'inert', 'volume', 'defaultValue', 'defaultChecked', diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-boolean-inert/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-boolean-inert/_config.js index 1322d63b7a..26fbbc955b 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-boolean-inert/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-boolean-inert/_config.js @@ -1,14 +1,21 @@ -import { ok, test } from '../../test'; +import { test } from '../../test'; export default test({ + ssrHtml: ` + <div></div> + <div inert="">some div <button>click</button></div> + `, + get props() { return { inert: true }; }, + test({ assert, target, component }) { - const div = target.querySelector('div'); - ok(div); - assert.ok(div.inert); + const [div1, div2] = target.querySelectorAll('div'); + assert.ok(!div1.inert); + assert.ok(div2.inert); + component.inert = false; - assert.ok(!div.inert); + assert.ok(!div2.inert); } }); diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-boolean-inert/main.svelte b/packages/svelte/tests/runtime-legacy/samples/attribute-boolean-inert/main.svelte index 6c3df7e31e..3b97923713 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-boolean-inert/main.svelte +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-boolean-inert/main.svelte @@ -2,4 +2,5 @@ export let inert; </script> +<div inert={false}></div> <div {inert}>some div <button>click</button></div> From 34628b9c90170cf84be64bfcc18adf88623a8aa4 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 7 Jan 2025 20:25:29 +0100 Subject: [PATCH 0053/1429] docs: note custom elements on caveat (#14934) closes #14727 --- documentation/docs/07-misc/04-custom-elements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/docs/07-misc/04-custom-elements.md b/documentation/docs/07-misc/04-custom-elements.md index 71c66f7edc..a8e0c81763 100644 --- a/documentation/docs/07-misc/04-custom-elements.md +++ b/documentation/docs/07-misc/04-custom-elements.md @@ -125,3 +125,4 @@ Custom elements can be a useful way to package components for consumption in a n - The deprecated `let:` directive has no effect, because custom elements do not have a way to pass data to the parent component that fills the slot - Polyfills are required to support older browsers - You can use Svelte's context feature between regular Svelte components within a custom element, but you can't use them across custom elements. In other words, you can't use `setContext` on a parent custom element and read that with `getContext` in a child custom element. +- Don't declare properties or attributes starting with `on`, as their usage will be interpreted as an event listener. In other words, Svelte treats `<custom-element oneworld={true}></custom-element>` as `customElement.addEventListener('eworld', true)` (and not as `customElement.oneworld = true`) From 08061c85da6c2c2d5d11d6cdad0a9d387042a784 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 7 Jan 2025 20:26:13 +0100 Subject: [PATCH 0054/1429] fix: guard against `customElements` being unavailable in browser extension contexts (#14933) fixes #14739 --- .changeset/forty-books-taste.md | 5 +++++ .../svelte/src/internal/client/dom/elements/attributes.js | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 .changeset/forty-books-taste.md diff --git a/.changeset/forty-books-taste.md b/.changeset/forty-books-taste.md new file mode 100644 index 0000000000..e2474c635f --- /dev/null +++ b/.changeset/forty-books-taste.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: guard against `customElements` being unavailable in browser extension contexts diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 6656532986..a2fffe8696 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -221,7 +221,10 @@ export function set_custom_element_data(node, prop, value) { // Don't compute setters for custom elements while they aren't registered yet, // because during their upgrade/instantiation they might add more setters. // Instead, fall back to a simple "an object, then set as property" heuristic. - setters_cache.has(node.nodeName) || customElements.get(node.tagName.toLowerCase()) + setters_cache.has(node.nodeName) || + // customElements may not be available in browser extension contexts + !customElements || + customElements.get(node.tagName.toLowerCase()) ? get_setters(node).includes(prop) : value && typeof value === 'object' ) { From 19038d19cb89cb100a6820069447e6d6eb22b612 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Tue, 7 Jan 2025 20:27:03 +0000 Subject: [PATCH 0055/1429] docs: add section on update propagation (#14938) --- documentation/docs/02-runes/03-$derived.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/documentation/docs/02-runes/03-$derived.md b/documentation/docs/02-runes/03-$derived.md index 6b38f99746..24ab643b68 100644 --- a/documentation/docs/02-runes/03-$derived.md +++ b/documentation/docs/02-runes/03-$derived.md @@ -51,3 +51,20 @@ In essence, `$derived(expression)` is equivalent to `$derived.by(() => expressio Anything read synchronously inside the `$derived` expression (or `$derived.by` function body) is considered a _dependency_ of the derived state. When the state changes, the derived will be marked as _dirty_ and recalculated when it is next read. To exempt a piece of state from being treated as a dependency, use [`untrack`](svelte#untrack). + +## Update propagation + +Svelte uses something called _push-pull reactivity_ — when state is updated, everything that depends on the state (whether directly or indirectly) is immediately notified of the change (the 'push'), but derived values are not re-evaluated until they are actually read (the 'pull'). + +If the new value of a derived is referentially identical to its previous value, downstream updates will be skipped. In other words, Svelte will only update the text inside the button when `large` changes, not when `count` changes, even though `large` depends on `count`: + +```svelte +<script> + let count = $state(0); + let large = $derived(count > 10); +</script> + +<button onclick={() => count++}> + {large} +</button> +``` From 4aadb34c02dc6afce56bb0f66bbac117ac56c4e8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 15:30:56 -0500 Subject: [PATCH 0056/1429] Version Packages (#14924) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/cold-cups-act.md | 5 ----- .changeset/forty-books-taste.md | 5 ----- .changeset/quiet-planets-carry.md | 5 ----- .changeset/tricky-radios-vanish.md | 5 ----- packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 7 files changed, 14 insertions(+), 22 deletions(-) delete mode 100644 .changeset/cold-cups-act.md delete mode 100644 .changeset/forty-books-taste.md delete mode 100644 .changeset/quiet-planets-carry.md delete mode 100644 .changeset/tricky-radios-vanish.md diff --git a/.changeset/cold-cups-act.md b/.changeset/cold-cups-act.md deleted file mode 100644 index 6776d4eef3..0000000000 --- a/.changeset/cold-cups-act.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: Make Tween duration 0 set current to target immediately diff --git a/.changeset/forty-books-taste.md b/.changeset/forty-books-taste.md deleted file mode 100644 index e2474c635f..0000000000 --- a/.changeset/forty-books-taste.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: guard against `customElements` being unavailable in browser extension contexts diff --git a/.changeset/quiet-planets-carry.md b/.changeset/quiet-planets-carry.md deleted file mode 100644 index 0abeffa2e8..0000000000 --- a/.changeset/quiet-planets-carry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: treat `inert` as a boolean attribute diff --git a/.changeset/tricky-radios-vanish.md b/.changeset/tricky-radios-vanish.md deleted file mode 100644 index 0935148fba..0000000000 --- a/.changeset/tricky-radios-vanish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: remove leading newline from `<pre>` contents diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index cc834a7ca3..4a2cca8280 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.16.6 + +### Patch Changes + +- fix: Make Tween duration 0 set current to target immediately ([#14937](https://github.com/sveltejs/svelte/pull/14937)) + +- fix: guard against `customElements` being unavailable in browser extension contexts ([#14933](https://github.com/sveltejs/svelte/pull/14933)) + +- fix: treat `inert` as a boolean attribute ([#14935](https://github.com/sveltejs/svelte/pull/14935)) + +- fix: remove leading newline from `<pre>` contents ([#14922](https://github.com/sveltejs/svelte/pull/14922)) + ## 5.16.5 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 682cf30fb3..26907c2a5b 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.16.5", + "version": "5.16.6", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index d4fd729b38..c7ad6fcf63 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.16.5'; +export const VERSION = '5.16.6'; export const PUBLIC_VERSION = '5'; From 48e3db21c187d1f4eb2aa10b417628e9ce6d17c3 Mon Sep 17 00:00:00 2001 From: Caique Torres <56696506+caiquetorres@users.noreply.github.com> Date: Tue, 7 Jan 2025 20:31:41 -0300 Subject: [PATCH 0057/1429] fix: default values in object destructuring (#14554) * fix: enhance string/expression length check and fix closing character issues * docs: add documentation for unterminated_string_constant error * test: add tests for object destructuring with default values in "each" blocks * Update .changeset/clean-planets-rush.md * refactor: clean up unnecessary comments and whitespace * fix: resolve formatting issues * simplify * tweak * regenerate --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/clean-planets-rush.md | 5 + .../98-reference/.generated/compile-errors.md | 6 + .../messages/compile-errors/template.md | 4 + packages/svelte/src/compiler/errors.js | 9 + .../compiler/phases/1-parse/read/context.js | 95 +- .../input.svelte | 9 + .../output.json | 826 +++++++++++++++++ .../each-block-object-pattern/input.svelte | 8 + .../each-block-object-pattern/output.json | 869 +++++++++++++++++- 9 files changed, 1806 insertions(+), 25 deletions(-) create mode 100644 .changeset/clean-planets-rush.md create mode 100644 packages/svelte/tests/parser-modern/samples/each-block-object-pattern-special-characters/input.svelte create mode 100644 packages/svelte/tests/parser-modern/samples/each-block-object-pattern-special-characters/output.json diff --git a/.changeset/clean-planets-rush.md b/.changeset/clean-planets-rush.md new file mode 100644 index 0000000000..d8500d4315 --- /dev/null +++ b/.changeset/clean-planets-rush.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: handle default values in object destructuring within "each" blocks when using characters like "}" and "]" diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index 298363f78d..c498920007 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -1012,6 +1012,12 @@ Unexpected end of input '%word%' is a reserved word in JavaScript and cannot be used here ``` +### unterminated_string_constant + +``` +Unterminated string constant +``` + ### void_element_invalid_content ``` diff --git a/packages/svelte/messages/compile-errors/template.md b/packages/svelte/messages/compile-errors/template.md index 02961b61fc..287178ef87 100644 --- a/packages/svelte/messages/compile-errors/template.md +++ b/packages/svelte/messages/compile-errors/template.md @@ -418,6 +418,10 @@ See https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-ele > '%word%' is a reserved word in JavaScript and cannot be used here +## unterminated_string_constant + +> Unterminated string constant + ## void_element_invalid_content > Void elements cannot have children or closing tags diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index 9ea13e811e..870cd9ac09 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -1574,6 +1574,15 @@ export function unexpected_reserved_word(node, word) { e(node, "unexpected_reserved_word", `'${word}' is a reserved word in JavaScript and cannot be used here\nhttps://svelte.dev/e/unexpected_reserved_word`); } +/** + * Unterminated string constant + * @param {null | number | NodeLike} node + * @returns {never} + */ +export function unterminated_string_constant(node) { + e(node, "unterminated_string_constant", `Unterminated string constant\nhttps://svelte.dev/e/unterminated_string_constant`); +} + /** * Void elements cannot have children or closing tags * @param {null | number | NodeLike} node diff --git a/packages/svelte/src/compiler/phases/1-parse/read/context.js b/packages/svelte/src/compiler/phases/1-parse/read/context.js index e206701500..f4c73dcf40 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/context.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/context.js @@ -37,33 +37,11 @@ export default function read_pattern(parser) { e.expected_pattern(i); } - /** @type {string[]} */ - const bracket_stack = []; - - while (i < parser.template.length) { - const char = parser.template[i]; - - if (is_bracket_open(char)) { - bracket_stack.push(char); - } else if (is_bracket_close(char)) { - const popped = /** @type {string} */ (bracket_stack.pop()); - const expected = /** @type {string} */ (get_bracket_close(popped)); - - if (char !== expected) { - e.expected_token(i, expected); - } - - if (bracket_stack.length === 0) { - i += 1; - break; - } - } - i += 1; - } - + i = match_bracket(parser, start); parser.index = i; const pattern_string = parser.template.slice(start, i); + try { // the length of the `space_with_newline` has to be start - 1 // because we added a `(` in front of the pattern_string, @@ -93,6 +71,75 @@ export default function read_pattern(parser) { } } +/** + * @param {Parser} parser + * @param {number} start + */ +function match_bracket(parser, start) { + const bracket_stack = []; + + let i = start; + + while (i < parser.template.length) { + let char = parser.template[i++]; + + if (char === "'" || char === '"' || char === '`') { + i = match_quote(parser, i, char); + continue; + } + + if (is_bracket_open(char)) { + bracket_stack.push(char); + } else if (is_bracket_close(char)) { + const popped = /** @type {string} */ (bracket_stack.pop()); + const expected = /** @type {string} */ (get_bracket_close(popped)); + + if (char !== expected) { + e.expected_token(i - 1, expected); + } + + if (bracket_stack.length === 0) { + return i; + } + } + } + + e.unexpected_eof(parser.template.length); +} + +/** + * @param {Parser} parser + * @param {number} start + * @param {string} quote + */ +function match_quote(parser, start, quote) { + let is_escaped = false; + let i = start; + + while (i < parser.template.length) { + const char = parser.template[i++]; + + if (is_escaped) { + is_escaped = false; + continue; + } + + if (char === quote) { + return i; + } + + if (char === '\\') { + is_escaped = true; + } + + if (quote === '`' && char === '$' && parser.template[i] === '{') { + i = match_bracket(parser, i); + } + } + + e.unterminated_string_constant(start); +} + /** * @param {Parser} parser * @returns {any} diff --git a/packages/svelte/tests/parser-modern/samples/each-block-object-pattern-special-characters/input.svelte b/packages/svelte/tests/parser-modern/samples/each-block-object-pattern-special-characters/input.svelte new file mode 100644 index 0000000000..bc83f8e57f --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/each-block-object-pattern-special-characters/input.svelte @@ -0,0 +1,9 @@ +{#each x as { y = 'z' }}{/each} + +{#each x as { y = '{' }}{/each} + +{#each x as { y = ']' }}{/each} + +{#each x as { y = `${`"`}` }}{/each} + +{#each x as { y = `${`John`}` }}{/each} diff --git a/packages/svelte/tests/parser-modern/samples/each-block-object-pattern-special-characters/output.json b/packages/svelte/tests/parser-modern/samples/each-block-object-pattern-special-characters/output.json new file mode 100644 index 0000000000..b962db3226 --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/each-block-object-pattern-special-characters/output.json @@ -0,0 +1,826 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 176, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "EachBlock", + "start": 0, + "end": 31, + "expression": { + "type": "Identifier", + "start": 7, + "end": 8, + "loc": { + "start": { + "line": 1, + "column": 7 + }, + "end": { + "line": 1, + "column": 8 + } + }, + "name": "x" + }, + "body": { + "type": "Fragment", + "nodes": [] + }, + "context": { + "type": "ObjectPattern", + "start": 12, + "end": 23, + "loc": { + "start": { + "line": 1, + "column": 12 + }, + "end": { + "line": 1, + "column": 23 + } + }, + "properties": [ + { + "type": "Property", + "start": 14, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "type": "Identifier", + "start": 14, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 15 + } + }, + "name": "y" + }, + "kind": "init", + "value": { + "type": "AssignmentPattern", + "start": 14, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "left": { + "type": "Identifier", + "start": 14, + "end": 15, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 15 + } + }, + "name": "y" + }, + "right": { + "type": "Literal", + "start": 18, + "end": 21, + "loc": { + "start": { + "line": 1, + "column": 18 + }, + "end": { + "line": 1, + "column": 21 + } + }, + "value": "z", + "raw": "'z'" + } + } + } + ] + } + }, + { + "type": "Text", + "start": 31, + "end": 33, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 33, + "end": 64, + "expression": { + "type": "Identifier", + "start": 40, + "end": 41, + "loc": { + "start": { + "line": 3, + "column": 7 + }, + "end": { + "line": 3, + "column": 8 + } + }, + "name": "x" + }, + "body": { + "type": "Fragment", + "nodes": [] + }, + "context": { + "type": "ObjectPattern", + "start": 45, + "end": 56, + "loc": { + "start": { + "line": 3, + "column": 13 + }, + "end": { + "line": 3, + "column": 24 + } + }, + "properties": [ + { + "type": "Property", + "start": 47, + "end": 54, + "loc": { + "start": { + "line": 3, + "column": 15 + }, + "end": { + "line": 3, + "column": 22 + } + }, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "type": "Identifier", + "start": 47, + "end": 48, + "loc": { + "start": { + "line": 3, + "column": 15 + }, + "end": { + "line": 3, + "column": 16 + } + }, + "name": "y" + }, + "kind": "init", + "value": { + "type": "AssignmentPattern", + "start": 47, + "end": 54, + "loc": { + "start": { + "line": 3, + "column": 15 + }, + "end": { + "line": 3, + "column": 22 + } + }, + "left": { + "type": "Identifier", + "start": 47, + "end": 48, + "loc": { + "start": { + "line": 3, + "column": 15 + }, + "end": { + "line": 3, + "column": 16 + } + }, + "name": "y" + }, + "right": { + "type": "Literal", + "start": 51, + "end": 54, + "loc": { + "start": { + "line": 3, + "column": 19 + }, + "end": { + "line": 3, + "column": 22 + } + }, + "value": "{", + "raw": "'{'" + } + } + } + ] + } + }, + { + "type": "Text", + "start": 64, + "end": 66, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 66, + "end": 97, + "expression": { + "type": "Identifier", + "start": 73, + "end": 74, + "loc": { + "start": { + "line": 5, + "column": 7 + }, + "end": { + "line": 5, + "column": 8 + } + }, + "name": "x" + }, + "body": { + "type": "Fragment", + "nodes": [] + }, + "context": { + "type": "ObjectPattern", + "start": 78, + "end": 89, + "loc": { + "start": { + "line": 5, + "column": 13 + }, + "end": { + "line": 5, + "column": 24 + } + }, + "properties": [ + { + "type": "Property", + "start": 80, + "end": 87, + "loc": { + "start": { + "line": 5, + "column": 15 + }, + "end": { + "line": 5, + "column": 22 + } + }, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "type": "Identifier", + "start": 80, + "end": 81, + "loc": { + "start": { + "line": 5, + "column": 15 + }, + "end": { + "line": 5, + "column": 16 + } + }, + "name": "y" + }, + "kind": "init", + "value": { + "type": "AssignmentPattern", + "start": 80, + "end": 87, + "loc": { + "start": { + "line": 5, + "column": 15 + }, + "end": { + "line": 5, + "column": 22 + } + }, + "left": { + "type": "Identifier", + "start": 80, + "end": 81, + "loc": { + "start": { + "line": 5, + "column": 15 + }, + "end": { + "line": 5, + "column": 16 + } + }, + "name": "y" + }, + "right": { + "type": "Literal", + "start": 84, + "end": 87, + "loc": { + "start": { + "line": 5, + "column": 19 + }, + "end": { + "line": 5, + "column": 22 + } + }, + "value": "]", + "raw": "']'" + } + } + } + ] + } + }, + { + "type": "Text", + "start": 97, + "end": 99, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 99, + "end": 135, + "expression": { + "type": "Identifier", + "start": 106, + "end": 107, + "loc": { + "start": { + "line": 7, + "column": 7 + }, + "end": { + "line": 7, + "column": 8 + } + }, + "name": "x" + }, + "body": { + "type": "Fragment", + "nodes": [] + }, + "context": { + "type": "ObjectPattern", + "start": 111, + "end": 127, + "loc": { + "start": { + "line": 7, + "column": 13 + }, + "end": { + "line": 7, + "column": 29 + } + }, + "properties": [ + { + "type": "Property", + "start": 113, + "end": 125, + "loc": { + "start": { + "line": 7, + "column": 15 + }, + "end": { + "line": 7, + "column": 27 + } + }, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "type": "Identifier", + "start": 113, + "end": 114, + "loc": { + "start": { + "line": 7, + "column": 15 + }, + "end": { + "line": 7, + "column": 16 + } + }, + "name": "y" + }, + "kind": "init", + "value": { + "type": "AssignmentPattern", + "start": 113, + "end": 125, + "loc": { + "start": { + "line": 7, + "column": 15 + }, + "end": { + "line": 7, + "column": 27 + } + }, + "left": { + "type": "Identifier", + "start": 113, + "end": 114, + "loc": { + "start": { + "line": 7, + "column": 15 + }, + "end": { + "line": 7, + "column": 16 + } + }, + "name": "y" + }, + "right": { + "type": "TemplateLiteral", + "start": 117, + "end": 125, + "loc": { + "start": { + "line": 7, + "column": 19 + }, + "end": { + "line": 7, + "column": 27 + } + }, + "expressions": [ + { + "type": "TemplateLiteral", + "start": 120, + "end": 123, + "loc": { + "start": { + "line": 7, + "column": 22 + }, + "end": { + "line": 7, + "column": 25 + } + }, + "expressions": [], + "quasis": [ + { + "type": "TemplateElement", + "start": 121, + "end": 122, + "loc": { + "start": { + "line": 7, + "column": 23 + }, + "end": { + "line": 7, + "column": 24 + } + }, + "value": { + "raw": "\"", + "cooked": "\"" + }, + "tail": true + } + ] + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 118, + "end": 118, + "loc": { + "start": { + "line": 7, + "column": 20 + }, + "end": { + "line": 7, + "column": 20 + } + }, + "value": { + "raw": "", + "cooked": "" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 124, + "end": 124, + "loc": { + "start": { + "line": 7, + "column": 26 + }, + "end": { + "line": 7, + "column": 26 + } + }, + "value": { + "raw": "", + "cooked": "" + }, + "tail": true + } + ] + } + } + } + ] + } + }, + { + "type": "Text", + "start": 135, + "end": 137, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 137, + "end": 176, + "expression": { + "type": "Identifier", + "start": 144, + "end": 145, + "loc": { + "start": { + "line": 9, + "column": 7 + }, + "end": { + "line": 9, + "column": 8 + } + }, + "name": "x" + }, + "body": { + "type": "Fragment", + "nodes": [] + }, + "context": { + "type": "ObjectPattern", + "start": 149, + "end": 168, + "loc": { + "start": { + "line": 9, + "column": 13 + }, + "end": { + "line": 9, + "column": 32 + } + }, + "properties": [ + { + "type": "Property", + "start": 151, + "end": 166, + "loc": { + "start": { + "line": 9, + "column": 15 + }, + "end": { + "line": 9, + "column": 30 + } + }, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "type": "Identifier", + "start": 151, + "end": 152, + "loc": { + "start": { + "line": 9, + "column": 15 + }, + "end": { + "line": 9, + "column": 16 + } + }, + "name": "y" + }, + "kind": "init", + "value": { + "type": "AssignmentPattern", + "start": 151, + "end": 166, + "loc": { + "start": { + "line": 9, + "column": 15 + }, + "end": { + "line": 9, + "column": 30 + } + }, + "left": { + "type": "Identifier", + "start": 151, + "end": 152, + "loc": { + "start": { + "line": 9, + "column": 15 + }, + "end": { + "line": 9, + "column": 16 + } + }, + "name": "y" + }, + "right": { + "type": "TemplateLiteral", + "start": 155, + "end": 166, + "loc": { + "start": { + "line": 9, + "column": 19 + }, + "end": { + "line": 9, + "column": 30 + } + }, + "expressions": [ + { + "type": "TemplateLiteral", + "start": 158, + "end": 164, + "loc": { + "start": { + "line": 9, + "column": 22 + }, + "end": { + "line": 9, + "column": 28 + } + }, + "expressions": [], + "quasis": [ + { + "type": "TemplateElement", + "start": 159, + "end": 163, + "loc": { + "start": { + "line": 9, + "column": 23 + }, + "end": { + "line": 9, + "column": 27 + } + }, + "value": { + "raw": "John", + "cooked": "John" + }, + "tail": true + } + ] + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 156, + "end": 156, + "loc": { + "start": { + "line": 9, + "column": 20 + }, + "end": { + "line": 9, + "column": 20 + } + }, + "value": { + "raw": "", + "cooked": "" + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 165, + "end": 165, + "loc": { + "start": { + "line": 9, + "column": 29 + }, + "end": { + "line": 9, + "column": 29 + } + }, + "value": { + "raw": "", + "cooked": "" + }, + "tail": true + } + ] + } + } + } + ] + } + } + ] + }, + "options": null +} diff --git a/packages/svelte/tests/parser-modern/samples/each-block-object-pattern/input.svelte b/packages/svelte/tests/parser-modern/samples/each-block-object-pattern/input.svelte index 8ffe8a7287..c3bfdb46a6 100644 --- a/packages/svelte/tests/parser-modern/samples/each-block-object-pattern/input.svelte +++ b/packages/svelte/tests/parser-modern/samples/each-block-object-pattern/input.svelte @@ -1,3 +1,11 @@ {#each people as { name, cool = true }} <p>{name} is {cool ? 'cool' : 'not cool'}</p> {/each} + +{#each people as { name = `Jane ${"Doe"}`, cool = true }} + <p>{name} is {cool ? 'cool' : 'not cool'}</p> +{/each} + +{#each people as { name = (() => { return `Jane ${"Doe"}`; })(), cool = true }} + <p>{name} is {cool ? 'cool' : 'not cool'}</p> +{/each} diff --git a/packages/svelte/tests/parser-modern/samples/each-block-object-pattern/output.json b/packages/svelte/tests/parser-modern/samples/each-block-object-pattern/output.json index 8fc3feb916..144016417b 100644 --- a/packages/svelte/tests/parser-modern/samples/each-block-object-pattern/output.json +++ b/packages/svelte/tests/parser-modern/samples/each-block-object-pattern/output.json @@ -2,7 +2,7 @@ "css": null, "js": [], "start": 0, - "end": 94, + "end": 344, "type": "Root", "fragment": { "type": "Fragment", @@ -307,6 +307,873 @@ } ] } + }, + { + "type": "Text", + "start": 94, + "end": 96, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 96, + "end": 208, + "expression": { + "type": "Identifier", + "start": 103, + "end": 109, + "loc": { + "start": { + "line": 5, + "column": 7 + }, + "end": { + "line": 5, + "column": 13 + } + }, + "name": "people" + }, + "body": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 153, + "end": 155, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 155, + "end": 200, + "name": "p", + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "ExpressionTag", + "start": 158, + "end": 164, + "expression": { + "type": "Identifier", + "start": 159, + "end": 163, + "loc": { + "start": { + "line": 6, + "column": 5 + }, + "end": { + "line": 6, + "column": 9 + } + }, + "name": "name" + } + }, + { + "type": "Text", + "start": 164, + "end": 168, + "raw": " is ", + "data": " is " + }, + { + "type": "ExpressionTag", + "start": 168, + "end": 196, + "expression": { + "type": "ConditionalExpression", + "start": 169, + "end": 195, + "loc": { + "start": { + "line": 6, + "column": 15 + }, + "end": { + "line": 6, + "column": 41 + } + }, + "test": { + "type": "Identifier", + "start": 169, + "end": 173, + "loc": { + "start": { + "line": 6, + "column": 15 + }, + "end": { + "line": 6, + "column": 19 + } + }, + "name": "cool" + }, + "consequent": { + "type": "Literal", + "start": 176, + "end": 182, + "loc": { + "start": { + "line": 6, + "column": 22 + }, + "end": { + "line": 6, + "column": 28 + } + }, + "value": "cool", + "raw": "'cool'" + }, + "alternate": { + "type": "Literal", + "start": 185, + "end": 195, + "loc": { + "start": { + "line": 6, + "column": 31 + }, + "end": { + "line": 6, + "column": 41 + } + }, + "value": "not cool", + "raw": "'not cool'" + } + } + } + ] + } + }, + { + "type": "Text", + "start": 200, + "end": 201, + "raw": "\n", + "data": "\n" + } + ] + }, + "context": { + "type": "ObjectPattern", + "start": 113, + "end": 152, + "loc": { + "start": { + "line": 5, + "column": 18 + }, + "end": { + "line": 5, + "column": 57 + } + }, + "properties": [ + { + "type": "Property", + "start": 115, + "end": 137, + "loc": { + "start": { + "line": 5, + "column": 20 + }, + "end": { + "line": 5, + "column": 42 + } + }, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "type": "Identifier", + "start": 115, + "end": 119, + "loc": { + "start": { + "line": 5, + "column": 20 + }, + "end": { + "line": 5, + "column": 24 + } + }, + "name": "name" + }, + "kind": "init", + "value": { + "type": "AssignmentPattern", + "start": 115, + "end": 137, + "loc": { + "start": { + "line": 5, + "column": 20 + }, + "end": { + "line": 5, + "column": 42 + } + }, + "left": { + "type": "Identifier", + "start": 115, + "end": 119, + "loc": { + "start": { + "line": 5, + "column": 20 + }, + "end": { + "line": 5, + "column": 24 + } + }, + "name": "name" + }, + "right": { + "type": "TemplateLiteral", + "start": 122, + "end": 137, + "loc": { + "start": { + "line": 5, + "column": 27 + }, + "end": { + "line": 5, + "column": 42 + } + }, + "expressions": [ + { + "type": "Literal", + "start": 130, + "end": 135, + "loc": { + "start": { + "line": 5, + "column": 35 + }, + "end": { + "line": 5, + "column": 40 + } + }, + "value": "Doe", + "raw": "\"Doe\"" + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 123, + "end": 128, + "loc": { + "start": { + "line": 5, + "column": 28 + }, + "end": { + "line": 5, + "column": 33 + } + }, + "value": { + "raw": "Jane ", + "cooked": "Jane " + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 136, + "end": 136, + "loc": { + "start": { + "line": 5, + "column": 41 + }, + "end": { + "line": 5, + "column": 41 + } + }, + "value": { + "raw": "", + "cooked": "" + }, + "tail": true + } + ] + } + } + }, + { + "type": "Property", + "start": 139, + "end": 150, + "loc": { + "start": { + "line": 5, + "column": 44 + }, + "end": { + "line": 5, + "column": 55 + } + }, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "type": "Identifier", + "start": 139, + "end": 143, + "loc": { + "start": { + "line": 5, + "column": 44 + }, + "end": { + "line": 5, + "column": 48 + } + }, + "name": "cool" + }, + "kind": "init", + "value": { + "type": "AssignmentPattern", + "start": 139, + "end": 150, + "loc": { + "start": { + "line": 5, + "column": 44 + }, + "end": { + "line": 5, + "column": 55 + } + }, + "left": { + "type": "Identifier", + "start": 139, + "end": 143, + "loc": { + "start": { + "line": 5, + "column": 44 + }, + "end": { + "line": 5, + "column": 48 + } + }, + "name": "cool" + }, + "right": { + "type": "Literal", + "start": 146, + "end": 150, + "loc": { + "start": { + "line": 5, + "column": 51 + }, + "end": { + "line": 5, + "column": 55 + } + }, + "value": true, + "raw": "true" + } + } + } + ] + } + }, + { + "type": "Text", + "start": 208, + "end": 210, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "EachBlock", + "start": 210, + "end": 344, + "expression": { + "type": "Identifier", + "start": 217, + "end": 223, + "loc": { + "start": { + "line": 9, + "column": 7 + }, + "end": { + "line": 9, + "column": 13 + } + }, + "name": "people" + }, + "body": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 289, + "end": 291, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "RegularElement", + "start": 291, + "end": 336, + "name": "p", + "attributes": [], + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "ExpressionTag", + "start": 294, + "end": 300, + "expression": { + "type": "Identifier", + "start": 295, + "end": 299, + "loc": { + "start": { + "line": 10, + "column": 5 + }, + "end": { + "line": 10, + "column": 9 + } + }, + "name": "name" + } + }, + { + "type": "Text", + "start": 300, + "end": 304, + "raw": " is ", + "data": " is " + }, + { + "type": "ExpressionTag", + "start": 304, + "end": 332, + "expression": { + "type": "ConditionalExpression", + "start": 305, + "end": 331, + "loc": { + "start": { + "line": 10, + "column": 15 + }, + "end": { + "line": 10, + "column": 41 + } + }, + "test": { + "type": "Identifier", + "start": 305, + "end": 309, + "loc": { + "start": { + "line": 10, + "column": 15 + }, + "end": { + "line": 10, + "column": 19 + } + }, + "name": "cool" + }, + "consequent": { + "type": "Literal", + "start": 312, + "end": 318, + "loc": { + "start": { + "line": 10, + "column": 22 + }, + "end": { + "line": 10, + "column": 28 + } + }, + "value": "cool", + "raw": "'cool'" + }, + "alternate": { + "type": "Literal", + "start": 321, + "end": 331, + "loc": { + "start": { + "line": 10, + "column": 31 + }, + "end": { + "line": 10, + "column": 41 + } + }, + "value": "not cool", + "raw": "'not cool'" + } + } + } + ] + } + }, + { + "type": "Text", + "start": 336, + "end": 337, + "raw": "\n", + "data": "\n" + } + ] + }, + "context": { + "type": "ObjectPattern", + "start": 227, + "end": 288, + "loc": { + "start": { + "line": 9, + "column": 18 + }, + "end": { + "line": 9, + "column": 79 + } + }, + "properties": [ + { + "type": "Property", + "start": 229, + "end": 273, + "loc": { + "start": { + "line": 9, + "column": 20 + }, + "end": { + "line": 9, + "column": 64 + } + }, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "type": "Identifier", + "start": 229, + "end": 233, + "loc": { + "start": { + "line": 9, + "column": 20 + }, + "end": { + "line": 9, + "column": 24 + } + }, + "name": "name" + }, + "kind": "init", + "value": { + "type": "AssignmentPattern", + "start": 229, + "end": 273, + "loc": { + "start": { + "line": 9, + "column": 20 + }, + "end": { + "line": 9, + "column": 64 + } + }, + "left": { + "type": "Identifier", + "start": 229, + "end": 233, + "loc": { + "start": { + "line": 9, + "column": 20 + }, + "end": { + "line": 9, + "column": 24 + } + }, + "name": "name" + }, + "right": { + "type": "CallExpression", + "start": 236, + "end": 273, + "loc": { + "start": { + "line": 9, + "column": 27 + }, + "end": { + "line": 9, + "column": 64 + } + }, + "callee": { + "type": "ArrowFunctionExpression", + "start": 237, + "end": 270, + "loc": { + "start": { + "line": 9, + "column": 28 + }, + "end": { + "line": 9, + "column": 61 + } + }, + "id": null, + "expression": false, + "generator": false, + "async": false, + "params": [], + "body": { + "type": "BlockStatement", + "start": 243, + "end": 270, + "loc": { + "start": { + "line": 9, + "column": 34 + }, + "end": { + "line": 9, + "column": 61 + } + }, + "body": [ + { + "type": "ReturnStatement", + "start": 245, + "end": 268, + "loc": { + "start": { + "line": 9, + "column": 36 + }, + "end": { + "line": 9, + "column": 59 + } + }, + "argument": { + "type": "TemplateLiteral", + "start": 252, + "end": 267, + "loc": { + "start": { + "line": 9, + "column": 43 + }, + "end": { + "line": 9, + "column": 58 + } + }, + "expressions": [ + { + "type": "Literal", + "start": 260, + "end": 265, + "loc": { + "start": { + "line": 9, + "column": 51 + }, + "end": { + "line": 9, + "column": 56 + } + }, + "value": "Doe", + "raw": "\"Doe\"" + } + ], + "quasis": [ + { + "type": "TemplateElement", + "start": 253, + "end": 258, + "loc": { + "start": { + "line": 9, + "column": 44 + }, + "end": { + "line": 9, + "column": 49 + } + }, + "value": { + "raw": "Jane ", + "cooked": "Jane " + }, + "tail": false + }, + { + "type": "TemplateElement", + "start": 266, + "end": 266, + "loc": { + "start": { + "line": 9, + "column": 57 + }, + "end": { + "line": 9, + "column": 57 + } + }, + "value": { + "raw": "", + "cooked": "" + }, + "tail": true + } + ] + } + } + ] + } + }, + "arguments": [], + "optional": false + } + } + }, + { + "type": "Property", + "start": 275, + "end": 286, + "loc": { + "start": { + "line": 9, + "column": 66 + }, + "end": { + "line": 9, + "column": 77 + } + }, + "method": false, + "shorthand": true, + "computed": false, + "key": { + "type": "Identifier", + "start": 275, + "end": 279, + "loc": { + "start": { + "line": 9, + "column": 66 + }, + "end": { + "line": 9, + "column": 70 + } + }, + "name": "cool" + }, + "kind": "init", + "value": { + "type": "AssignmentPattern", + "start": 275, + "end": 286, + "loc": { + "start": { + "line": 9, + "column": 66 + }, + "end": { + "line": 9, + "column": 77 + } + }, + "left": { + "type": "Identifier", + "start": 275, + "end": 279, + "loc": { + "start": { + "line": 9, + "column": 66 + }, + "end": { + "line": 9, + "column": 70 + } + }, + "name": "cool" + }, + "right": { + "type": "Literal", + "start": 282, + "end": 286, + "loc": { + "start": { + "line": 9, + "column": 73 + }, + "end": { + "line": 9, + "column": 77 + } + }, + "value": true, + "raw": "true" + } + } + } + ] + } } ] }, From deb362fe517c6fac3b77bfb4ec0b0dc9df16a971 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Wed, 8 Jan 2025 01:37:53 +0000 Subject: [PATCH 0058/1429] chore: expand benchmark iterations (#14944) --- benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js | 4 ++-- benchmarking/benchmarks/reactivity/kairo/kairo_broad.js | 4 ++-- benchmarking/benchmarks/reactivity/kairo/kairo_deep.js | 4 ++-- benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js | 4 ++-- benchmarking/benchmarks/reactivity/kairo/kairo_mux.js | 4 ++-- benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js | 4 ++-- benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js | 4 ++-- benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js b/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js index 1237547ebe..6b058cdc3c 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js @@ -45,7 +45,7 @@ export async function kairo_avoidable_unowned() { const { run, destroy } = setup(); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); @@ -74,7 +74,7 @@ export async function kairo_avoidable_owned() { }); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js b/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js index 8148a743ea..d1cde5958e 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js @@ -51,7 +51,7 @@ export async function kairo_broad_unowned() { const { run, destroy } = setup(); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); @@ -80,7 +80,7 @@ export async function kairo_broad_owned() { }); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js b/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js index 806042cc72..149457ede1 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js @@ -51,7 +51,7 @@ export async function kairo_deep_unowned() { const { run, destroy } = setup(); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); @@ -80,7 +80,7 @@ export async function kairo_deep_owned() { }); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js b/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js index deb9482de9..958a1bcd78 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js @@ -55,7 +55,7 @@ export async function kairo_diamond_unowned() { const { run, destroy } = setup(); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); @@ -84,7 +84,7 @@ export async function kairo_diamond_owned() { }); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js b/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js index 8eafacc9eb..b645051c09 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js @@ -48,7 +48,7 @@ export async function kairo_mux_unowned() { const { run, destroy } = setup(); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); @@ -77,7 +77,7 @@ export async function kairo_mux_owned() { }); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js b/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js index 2bddf879c9..53b85acd37 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js @@ -52,7 +52,7 @@ export async function kairo_repeated_unowned() { const { run, destroy } = setup(); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); @@ -81,7 +81,7 @@ export async function kairo_repeated_owned() { }); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js b/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js index 9d99b7815b..b9e2ad9fa4 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js @@ -65,7 +65,7 @@ export async function kairo_triangle_unowned() { const { run, destroy } = setup(); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); @@ -94,7 +94,7 @@ export async function kairo_triangle_owned() { }); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js b/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js index c30c007561..0e783732dc 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js @@ -51,7 +51,7 @@ export async function kairo_unstable_unowned() { const { run, destroy } = setup(); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); @@ -80,7 +80,7 @@ export async function kairo_unstable_owned() { }); const { timing } = await fastest_test(10, () => { - for (let i = 0; i < 100; i++) { + for (let i = 0; i < 1000; i++) { run(); } }); From 9c20eb4815bf86147746012453a2cfd8b407c61b Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Wed, 8 Jan 2025 09:46:26 +0000 Subject: [PATCH 0059/1429] chore: improve signal performance by reducing duplicate deps (#14945) * chore: expand benchmark iterations * chore: improve signal performance by reducing duplicate deps * feedback * oops --- .changeset/tame-students-retire.md | 5 ++ .../svelte/src/internal/client/dev/tracing.js | 7 +-- .../internal/client/reactivity/deriveds.js | 7 ++- .../src/internal/client/reactivity/effects.js | 2 +- .../src/internal/client/reactivity/sources.js | 7 ++- .../src/internal/client/reactivity/types.d.ts | 8 ++- .../svelte/src/internal/client/runtime.js | 63 +++++++++++-------- packages/svelte/tests/signals/test.ts | 3 +- 8 files changed, 59 insertions(+), 43 deletions(-) create mode 100644 .changeset/tame-students-retire.md diff --git a/.changeset/tame-students-retire.md b/.changeset/tame-students-retire.md new file mode 100644 index 0000000000..121c3690d0 --- /dev/null +++ b/.changeset/tame-students-retire.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: improve signal performance by reducing duplicate deps diff --git a/packages/svelte/src/internal/client/dev/tracing.js b/packages/svelte/src/internal/client/dev/tracing.js index 6e759ecd59..3881ef3442 100644 --- a/packages/svelte/src/internal/client/dev/tracing.js +++ b/packages/svelte/src/internal/client/dev/tracing.js @@ -42,15 +42,12 @@ function log_entry(signal, entry) { const type = (signal.f & DERIVED) !== 0 ? '$derived' : '$state'; const current_reaction = /** @type {Reaction} */ (active_reaction); - const status = - signal.version > current_reaction.version || current_reaction.version === 0 ? 'dirty' : 'clean'; + const dirty = signal.wv > current_reaction.wv || current_reaction.wv === 0; // eslint-disable-next-line no-console console.groupCollapsed( `%c${type}`, - status !== 'clean' - ? 'color: CornflowerBlue; font-weight: bold' - : 'color: grey; font-weight: bold', + dirty ? 'color: CornflowerBlue; font-weight: bold' : 'color: grey; font-weight: bold', typeof value === 'object' && value !== null && STATE_SYMBOL in value ? snapshot(value, true) : value diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 5e045920bf..7ec1ed30bd 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -16,7 +16,7 @@ import { set_signal_status, skip_reaction, update_reaction, - increment_version, + increment_write_version, set_active_effect, component_context } from '../runtime.js'; @@ -58,8 +58,9 @@ export function derived(fn) { f: flags, fn, reactions: null, + rv: 0, v: /** @type {V} */ (null), - version: 0, + wv: 0, parent: parent_derived ?? active_effect }; @@ -182,7 +183,7 @@ export function update_derived(derived) { if (!derived.equals(value)) { derived.v = value; - derived.version = increment_version(); + derived.wv = increment_write_version(); } } diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index bf890627f7..16f076edde 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -110,7 +110,7 @@ function create_effect(type, fn, sync, push = true) { prev: null, teardown: null, transitions: null, - version: 0 + wv: 0 }; if (DEV) { diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index fedf5b5070..e0facf3b55 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -12,7 +12,7 @@ import { set_untracked_writes, set_signal_status, untrack, - increment_version, + increment_write_version, update_effect, derived_sources, set_derived_sources, @@ -57,7 +57,8 @@ export function source(v, stack) { v, reactions: null, equals, - version: 0 + rv: 0, + wv: 0 }; if (DEV && tracing_mode_flag) { @@ -169,7 +170,7 @@ export function internal_set(source, value) { if (!source.equals(value)) { var old_value = source.v; source.v = value; - source.version = increment_version(); + source.wv = increment_write_version(); if (DEV && tracing_mode_flag) { source.updated = get_stack('UpdatedAt'); diff --git a/packages/svelte/src/internal/client/reactivity/types.d.ts b/packages/svelte/src/internal/client/reactivity/types.d.ts index 9a6e5d8bf0..3a76a3ff83 100644 --- a/packages/svelte/src/internal/client/reactivity/types.d.ts +++ b/packages/svelte/src/internal/client/reactivity/types.d.ts @@ -4,14 +4,16 @@ export interface Signal { /** Flags bitmask */ f: number; /** Write version */ - version: number; + wv: number; } export interface Value<V = unknown> extends Signal { - /** Signals that read from this signal */ - reactions: null | Reaction[]; /** Equality function */ equals: Equals; + /** Signals that read from this signal */ + reactions: null | Reaction[]; + /** Read version */ + rv: number; /** The latest value for this signal */ v: V; /** Dev only */ diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index a066f1c11a..202608ce06 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -127,8 +127,14 @@ export function set_untracked_writes(value) { untracked_writes = value; } -/** @type {number} Used by sources and deriveds for handling updates to unowned deriveds it starts from 1 to differentiate between a created effect and a run one for tracing */ -let current_version = 1; +/** + * @type {number} Used by sources and deriveds for handling updates. + * Version starts from 1 so that unowned deriveds differentiate between a created effect and a run one for tracing + **/ +let write_version = 1; + +/** @type {number} Used to version each read of a source of derived to avoid duplicating depedencies inside a reaction */ +let read_version = 0; // If we are working with a get() chain that has no active container, // to prevent memory leaks, we skip adding the reaction. @@ -168,8 +174,8 @@ export function set_dev_current_component_function(fn) { dev_current_component_function = fn; } -export function increment_version() { - return ++current_version; +export function increment_write_version() { + return ++write_version; } /** @returns {boolean} */ @@ -226,7 +232,7 @@ export function check_dirtiness(reaction) { update_derived(/** @type {Derived} */ (dependency)); } - if (dependency.version > reaction.version) { + if (dependency.wv > reaction.wv) { return true; } } @@ -398,6 +404,7 @@ export function update_reaction(reaction) { skip_reaction = !is_flushing_effect && (flags & UNOWNED) !== 0; derived_sources = null; component_context = reaction.ctx; + read_version++; try { var result = /** @type {Function} */ (0, reaction.fn)(); @@ -528,7 +535,7 @@ export function update_effect(effect) { execute_effect_teardown(effect); var teardown = update_reaction(effect); effect.teardown = typeof teardown === 'function' ? teardown : null; - effect.version = current_version; + effect.wv = write_version; var deps = effect.deps; @@ -540,7 +547,7 @@ export function update_effect(effect) { for (let i = 0; i < deps.length; i++) { var dep = deps[i]; if (dep.trace_need_increase) { - dep.version = increment_version(); + dep.wv = increment_write_version(); dep.trace_need_increase = undefined; dep.trace_v = undefined; } @@ -880,27 +887,29 @@ export function get(signal) { e.state_unsafe_local_read(); } var deps = active_reaction.deps; + if (signal.rv < read_version) { + signal.rv = read_version; + // If the signal is accessing the same dependencies in the same + // order as it did last time, increment `skipped_deps` + // rather than updating `new_deps`, which creates GC cost + if (new_deps === null && deps !== null && deps[skipped_deps] === signal) { + skipped_deps++; + } else if (new_deps === null) { + new_deps = [signal]; + } else { + new_deps.push(signal); + } - // If the signal is accessing the same dependencies in the same - // order as it did last time, increment `skipped_deps` - // rather than updating `new_deps`, which creates GC cost - if (new_deps === null && deps !== null && deps[skipped_deps] === signal) { - skipped_deps++; - } else if (new_deps === null) { - new_deps = [signal]; - } else { - new_deps.push(signal); - } - - if ( - untracked_writes !== null && - active_effect !== null && - (active_effect.f & CLEAN) !== 0 && - (active_effect.f & BRANCH_EFFECT) === 0 && - untracked_writes.includes(signal) - ) { - set_signal_status(active_effect, DIRTY); - schedule_effect(active_effect); + if ( + untracked_writes !== null && + active_effect !== null && + (active_effect.f & CLEAN) !== 0 && + (active_effect.f & BRANCH_EFFECT) === 0 && + untracked_writes.includes(signal) + ) { + set_signal_status(active_effect, DIRTY); + schedule_effect(active_effect); + } } } else if (is_derived && /** @type {Derived} */ (signal).deps === null) { var derived = /** @type {Derived} */ (signal); diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index 0a22e30286..e198a5a89d 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -296,6 +296,7 @@ describe('signals', () => { const destroy = effect_root(() => { user_effect(() => { log.push($.get(calc)); + $.get(calc); }); }); @@ -306,7 +307,7 @@ describe('signals', () => { flushSync(() => set(count, 4)); flushSync(() => set(count, 0)); // Ensure we're not leaking consumers - assert.deepEqual(count.reactions?.length, 2); + assert.deepEqual(count.reactions?.length, 1); assert.deepEqual(calc.reactions?.length, 1); assert.deepEqual(log, [0, 2, 'limit', 0]); destroy(); From ce2566d4c41c24546a697aa5feac07a617f806c0 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 8 Jan 2025 09:47:24 +0000 Subject: [PATCH 0060/1429] docs: simplify `$effect.tracking()` docs (#14939) --- documentation/docs/02-runes/04-$effect.md | 48 +---------------------- 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md index b338795220..c48a82f9a5 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -193,53 +193,7 @@ The `$effect.tracking` rune is an advanced feature that tells you whether or not <p>in template: {$effect.tracking()}</p> <!-- true --> ``` -This allows you to (for example) add things like subscriptions without causing memory leaks, by putting them in child effects. Here's a `readable` function that listens to changes from a callback function as long as it's inside a tracking context: - -```ts -import { tick } from 'svelte'; - -export default function readable<T>( - initial_value: T, - start: (callback: (update: (v: T) => T) => T) => () => void -) { - let value = $state(initial_value); - - let subscribers = 0; - let stop: null | (() => void) = null; - - return { - get value() { - // If in a tracking context ... - if ($effect.tracking()) { - $effect(() => { - // ...and there's no subscribers yet... - if (subscribers === 0) { - // ...invoke the function and listen to changes to update state - stop = start((fn) => (value = fn(value))); - } - - subscribers++; - - // The return callback is called once a listener unlistens - return () => { - tick().then(() => { - subscribers--; - // If it was the last subscriber... - if (subscribers === 0) { - // ...stop listening to changes - stop?.(); - stop = null; - } - }); - }; - }); - } - - return value; - } - }; -} -``` +It is used to implement abstractions like [`createSubscriber`](/docs/svelte/svelte-reactivity#createSubscriber), which will create listeners to update reactive values but _only_ if those values are being tracked (rather than, for example, read inside an event handler). ## `$effect.root` From adee13d98c09c5bddc0da41cd38c429498880a09 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 8 Jan 2025 09:49:04 +0000 Subject: [PATCH 0061/1429] fix: account for min-width/height in `slide` transition (#14942) --- .changeset/gold-olives-care.md | 5 +++++ packages/svelte/src/transition/index.js | 3 ++- .../samples/class-shortcut-with-transition/_config.js | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 .changeset/gold-olives-care.md diff --git a/.changeset/gold-olives-care.md b/.changeset/gold-olives-care.md new file mode 100644 index 0000000000..9f7357c6da --- /dev/null +++ b/.changeset/gold-olives-care.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: account for min-width/height in `slide` transition diff --git a/packages/svelte/src/transition/index.js b/packages/svelte/src/transition/index.js index 4123106211..aacc6b6cf3 100644 --- a/packages/svelte/src/transition/index.js +++ b/packages/svelte/src/transition/index.js @@ -131,7 +131,8 @@ export function slide(node, { delay = 0, duration = 400, easing = cubic_out, axi `margin-${secondary_properties[0]}: ${t * margin_start_value}px;` + `margin-${secondary_properties[1]}: ${t * margin_end_value}px;` + `border-${secondary_properties[0]}-width: ${t * border_width_start_value}px;` + - `border-${secondary_properties[1]}-width: ${t * border_width_end_value}px;` + `border-${secondary_properties[1]}-width: ${t * border_width_end_value}px;` + + `min-${primary_property}: 0` }; } diff --git a/packages/svelte/tests/runtime-legacy/samples/class-shortcut-with-transition/_config.js b/packages/svelte/tests/runtime-legacy/samples/class-shortcut-with-transition/_config.js index 6e3f7a3cb7..e58e8c53c5 100644 --- a/packages/svelte/tests/runtime-legacy/samples/class-shortcut-with-transition/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/class-shortcut-with-transition/_config.js @@ -18,7 +18,7 @@ export default test({ raf.tick(150); assert.htmlEqual( target.innerHTML, - '<p>foo</p><p class="red svelte-1yszte8 border" style="overflow: hidden; opacity: 0; border-top-width: 0.5px; border-bottom-width: 0.5px;">bar</p>' + '<p>foo</p><p class="red svelte-1yszte8 border" style="overflow: hidden; opacity: 0; border-top-width: 0.5px; border-bottom-width: 0.5px; min-height: 0;">bar</p>' ); component.open = true; raf.tick(250); From d245bab63a3fd4fbe3d0a03d51581db5b794564e Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 8 Jan 2025 09:49:49 +0000 Subject: [PATCH 0062/1429] feat: allow non-numeric values to be tweened by snapping immediately to new value (#14941) --- .changeset/cool-apples-report.md | 5 +++++ packages/svelte/src/motion/tweened.js | 3 ++- packages/svelte/tests/motion/test.ts | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 .changeset/cool-apples-report.md diff --git a/.changeset/cool-apples-report.md b/.changeset/cool-apples-report.md new file mode 100644 index 0000000000..fa76c036d1 --- /dev/null +++ b/.changeset/cool-apples-report.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: allow non-numeric values to be tweened by snapping immediately to new value diff --git a/packages/svelte/src/motion/tweened.js b/packages/svelte/src/motion/tweened.js index 36d32d1f72..31cddf8f30 100644 --- a/packages/svelte/src/motion/tweened.js +++ b/packages/svelte/src/motion/tweened.js @@ -72,7 +72,8 @@ function get_interpolator(a, b) { return (t) => a + t * delta; } - throw new Error(`Cannot interpolate ${type} values`); + // for non-numeric values, snap to the final value immediately + return () => b; } /** diff --git a/packages/svelte/tests/motion/test.ts b/packages/svelte/tests/motion/test.ts index 7d845bac3d..7d0403983a 100644 --- a/packages/svelte/tests/motion/test.ts +++ b/packages/svelte/tests/motion/test.ts @@ -3,6 +3,7 @@ import '../helpers.js'; // for the matchMedia polyfill import { describe, it, assert } from 'vitest'; import { get } from 'svelte/store'; import { spring, tweened, Tween } from 'svelte/motion'; +import { raf } from '../animation-helpers.js'; describe('motion', () => { describe('spring', () => { @@ -38,6 +39,16 @@ describe('motion', () => { size.update((v) => v + 10); assert.equal(get(size), 20); }); + + it('updates non-numeric values immediately', () => { + raf.reset(); + const boolean = tweened(false); + + boolean.set(true, { duration: 100 }); + + raf.tick(1); + assert.equal(get(boolean), true); + }); }); describe('Tween', () => { @@ -47,6 +58,16 @@ describe('motion', () => { size.set(100, { duration: 0 }); assert.equal(size.current, 100); }); + + it('updates non-numeric values immediately', () => { + raf.reset(); + const boolean = new Tween(false); + + boolean.set(true, { duration: 100 }); + + raf.tick(1); + assert.equal(boolean.current, true); + }); }); it('updates correctly when initialized with a `null`-ish value', () => { From 4d2cb2734b94902225a3b960985976a81b6aa584 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 8 Jan 2025 09:53:27 +0000 Subject: [PATCH 0063/1429] fix: prevent long delays causing erratic spring behaviour (#14940) --- .changeset/modern-wasps-punch.md | 5 +++++ packages/svelte/src/motion/spring.js | 13 +++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 .changeset/modern-wasps-punch.md diff --git a/.changeset/modern-wasps-punch.md b/.changeset/modern-wasps-punch.md new file mode 100644 index 0000000000..1fa0b41300 --- /dev/null +++ b/.changeset/modern-wasps-punch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent long delays causing erratic spring behaviour diff --git a/packages/svelte/src/motion/spring.js b/packages/svelte/src/motion/spring.js index bc30ce9578..1c8e5f15c0 100644 --- a/packages/svelte/src/motion/spring.js +++ b/packages/svelte/src/motion/spring.js @@ -108,12 +108,17 @@ export function spring(value, opts = {}) { return false; } inv_mass = Math.min(inv_mass + inv_mass_recovery_rate, 1); + + // clamp elapsed time to 1/30th of a second, so that longer pauses + // (blocked thread or inactive tab) don't cause the spring to go haywire + const elapsed = Math.min(now - last_time, 1000 / 30); + /** @type {TickContext} */ const ctx = { inv_mass, opts: spring, settled: true, - dt: ((now - last_time) * 60) / 1000 + dt: (elapsed * 60) / 1000 }; // @ts-ignore const next_value = tick_spring(ctx, last_value, value, target_value); @@ -236,6 +241,10 @@ export class Spring { this.#task ??= loop((now) => { this.#inverse_mass = Math.min(this.#inverse_mass + inv_mass_recovery_rate, 1); + // clamp elapsed time to 1/30th of a second, so that longer pauses + // (blocked thread or inactive tab) don't cause the spring to go haywire + const elapsed = Math.min(now - this.#last_time, 1000 / 30); + /** @type {import('./private').TickContext} */ const ctx = { inv_mass: this.#inverse_mass, @@ -245,7 +254,7 @@ export class Spring { precision: this.#precision.v }, settled: true, - dt: ((now - this.#last_time) * 60) / 1000 + dt: (elapsed * 60) / 1000 }; var next = tick_spring(ctx, this.#last_value, this.#current.v, this.#target.v); From d41801c75f2a232387229d2a45324affd0310808 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 8 Jan 2025 09:54:52 +0000 Subject: [PATCH 0064/1429] feat: warn on using `slide` transition with invalid `display` styles (#14936) * feat: warn on using `slide` transition with table elements * more generic * more generic --- .changeset/silver-humans-yawn.md | 5 +++++ .../docs/98-reference/.generated/client-warnings.md | 12 ++++++++++++ .../svelte/messages/client-warnings/warnings.md | 10 ++++++++++ packages/svelte/src/internal/client/warnings.js | 12 ++++++++++++ packages/svelte/src/transition/index.js | 13 +++++++++++++ 5 files changed, 52 insertions(+) create mode 100644 .changeset/silver-humans-yawn.md diff --git a/.changeset/silver-humans-yawn.md b/.changeset/silver-humans-yawn.md new file mode 100644 index 0000000000..e274341079 --- /dev/null +++ b/.changeset/silver-humans-yawn.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +feat: warn on using `slide` transition with table elements diff --git a/documentation/docs/98-reference/.generated/client-warnings.md b/documentation/docs/98-reference/.generated/client-warnings.md index 5218ec4cb1..284e9a7c3e 100644 --- a/documentation/docs/98-reference/.generated/client-warnings.md +++ b/documentation/docs/98-reference/.generated/client-warnings.md @@ -222,3 +222,15 @@ Reactive `$state(...)` proxies and the values they proxy have different identiti ``` To resolve this, ensure you're comparing values where both values were created with `$state(...)`, or neither were. Note that `$state.raw(...)` will _not_ create a state proxy. + +### transition_slide_display + +``` +The `slide` transition does not work correctly for elements with `display: %value%` +``` + +The [slide](/docs/svelte/svelte-transition#slide) transition works by animating the `height` of the element, which requires a `display` style like `block`, `flex` or `grid`. It does not work for: + +- `display: inline` (which is the default for elements like `<span>`), and its variants like `inline-block`, `inline-flex` and `inline-grid` +- `display: table` and `table-[name]`, which are the defaults for elements like `<table>` and `<tr>` +- `display: contents` diff --git a/packages/svelte/messages/client-warnings/warnings.md b/packages/svelte/messages/client-warnings/warnings.md index 21db506dc9..943cf6f01f 100644 --- a/packages/svelte/messages/client-warnings/warnings.md +++ b/packages/svelte/messages/client-warnings/warnings.md @@ -186,3 +186,13 @@ To fix it, either create callback props to communicate changes, or mark `person` ``` To resolve this, ensure you're comparing values where both values were created with `$state(...)`, or neither were. Note that `$state.raw(...)` will _not_ create a state proxy. + +## transition_slide_display + +> The `slide` transition does not work correctly for elements with `display: %value%` + +The [slide](/docs/svelte/svelte-transition#slide) transition works by animating the `height` of the element, which requires a `display` style like `block`, `flex` or `grid`. It does not work for: + +- `display: inline` (which is the default for elements like `<span>`), and its variants like `inline-block`, `inline-flex` and `inline-grid` +- `display: table` and `table-[name]`, which are the defaults for elements like `<table>` and `<tr>` +- `display: contents` diff --git a/packages/svelte/src/internal/client/warnings.js b/packages/svelte/src/internal/client/warnings.js index 24d86e2e0b..78d457f48f 100644 --- a/packages/svelte/src/internal/client/warnings.js +++ b/packages/svelte/src/internal/client/warnings.js @@ -165,4 +165,16 @@ export function state_proxy_equality_mismatch(operator) { } else { console.warn(`https://svelte.dev/e/state_proxy_equality_mismatch`); } +} + +/** + * The `slide` transition does not work correctly for elements with `display: %value%` + * @param {string} value + */ +export function transition_slide_display(value) { + if (DEV) { + console.warn(`%c[svelte] transition_slide_display\n%cThe \`slide\` transition does not work correctly for elements with \`display: ${value}\`\nhttps://svelte.dev/e/transition_slide_display`, bold, normal); + } else { + console.warn(`https://svelte.dev/e/transition_slide_display`); + } } \ No newline at end of file diff --git a/packages/svelte/src/transition/index.js b/packages/svelte/src/transition/index.js index aacc6b6cf3..898a929eb1 100644 --- a/packages/svelte/src/transition/index.js +++ b/packages/svelte/src/transition/index.js @@ -1,4 +1,8 @@ /** @import { BlurParams, CrossfadeParams, DrawParams, FadeParams, FlyParams, ScaleParams, SlideParams, TransitionConfig } from './public' */ + +import { DEV } from 'esm-env'; +import * as w from '../internal/client/warnings.js'; + /** @param {number} x */ const linear = (x) => x; @@ -92,6 +96,8 @@ export function fly( }; } +var slide_warning = false; + /** * Slides an element in and out. * @@ -101,6 +107,13 @@ export function fly( */ export function slide(node, { delay = 0, duration = 400, easing = cubic_out, axis = 'y' } = {}) { const style = getComputedStyle(node); + + if (DEV && !slide_warning && /(contents|inline|table)/.test(style.display)) { + slide_warning = true; + Promise.resolve().then(() => (slide_warning = false)); + w.transition_slide_display(style.display); + } + const opacity = +style.opacity; const primary_property = axis === 'y' ? 'height' : 'width'; const primary_property_value = parseFloat(style[primary_property]); From 9b71eea35d8e62e7ef008565bf23fe25c4f47ed9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 09:57:21 +0000 Subject: [PATCH 0065/1429] Version Packages (#14943) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/clean-planets-rush.md | 5 ----- .changeset/cool-apples-report.md | 5 ----- .changeset/gold-olives-care.md | 5 ----- .changeset/modern-wasps-punch.md | 5 ----- .changeset/silver-humans-yawn.md | 5 ----- .changeset/tame-students-retire.md | 5 ----- packages/svelte/CHANGELOG.md | 18 ++++++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 9 files changed, 20 insertions(+), 32 deletions(-) delete mode 100644 .changeset/clean-planets-rush.md delete mode 100644 .changeset/cool-apples-report.md delete mode 100644 .changeset/gold-olives-care.md delete mode 100644 .changeset/modern-wasps-punch.md delete mode 100644 .changeset/silver-humans-yawn.md delete mode 100644 .changeset/tame-students-retire.md diff --git a/.changeset/clean-planets-rush.md b/.changeset/clean-planets-rush.md deleted file mode 100644 index d8500d4315..0000000000 --- a/.changeset/clean-planets-rush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: handle default values in object destructuring within "each" blocks when using characters like "}" and "]" diff --git a/.changeset/cool-apples-report.md b/.changeset/cool-apples-report.md deleted file mode 100644 index fa76c036d1..0000000000 --- a/.changeset/cool-apples-report.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: allow non-numeric values to be tweened by snapping immediately to new value diff --git a/.changeset/gold-olives-care.md b/.changeset/gold-olives-care.md deleted file mode 100644 index 9f7357c6da..0000000000 --- a/.changeset/gold-olives-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: account for min-width/height in `slide` transition diff --git a/.changeset/modern-wasps-punch.md b/.changeset/modern-wasps-punch.md deleted file mode 100644 index 1fa0b41300..0000000000 --- a/.changeset/modern-wasps-punch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: prevent long delays causing erratic spring behaviour diff --git a/.changeset/silver-humans-yawn.md b/.changeset/silver-humans-yawn.md deleted file mode 100644 index e274341079..0000000000 --- a/.changeset/silver-humans-yawn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -feat: warn on using `slide` transition with table elements diff --git a/.changeset/tame-students-retire.md b/.changeset/tame-students-retire.md deleted file mode 100644 index 121c3690d0..0000000000 --- a/.changeset/tame-students-retire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: improve signal performance by reducing duplicate deps diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 4a2cca8280..daf8ed209a 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,23 @@ # svelte +## 5.17.0 + +### Minor Changes + +- feat: allow non-numeric values to be tweened by snapping immediately to new value ([#14941](https://github.com/sveltejs/svelte/pull/14941)) + +### Patch Changes + +- fix: handle default values in object destructuring within "each" blocks when using characters like "}" and "]" ([#14554](https://github.com/sveltejs/svelte/pull/14554)) + +- fix: account for min-width/height in `slide` transition ([#14942](https://github.com/sveltejs/svelte/pull/14942)) + +- fix: prevent long delays causing erratic spring behaviour ([#14940](https://github.com/sveltejs/svelte/pull/14940)) + +- feat: warn on using `slide` transition with table elements ([#14936](https://github.com/sveltejs/svelte/pull/14936)) + +- chore: improve signal performance by reducing duplicate deps ([#14945](https://github.com/sveltejs/svelte/pull/14945)) + ## 5.16.6 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 26907c2a5b..5cd078bf77 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.16.6", + "version": "5.17.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index c7ad6fcf63..396c2b07ef 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.16.6'; +export const VERSION = '5.17.0'; export const PUBLIC_VERSION = '5'; From ce4f9722dab8d99140d410a9d1f71ec19d16535e Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 8 Jan 2025 09:54:29 -0500 Subject: [PATCH 0066/1429] fix: remove bindable prop validation (#14946) --- .changeset/flat-otters-cheer.md | 5 ++++ .../3-transform/client/transform-client.js | 22 ---------------- packages/svelte/src/internal/client/index.js | 2 +- .../src/internal/client/reactivity/props.js | 6 +++-- .../svelte/src/internal/client/validate.js | 25 +------------------ .../samples/export-binding/_config.js | 10 -------- .../export-binding/counter/index.svelte | 8 ------ .../samples/export-binding/main.svelte | 7 ------ .../props-not-bindable-spread/Counter.svelte | 5 ---- .../props-not-bindable-spread/_config.js | 13 ---------- .../props-not-bindable-spread/main.svelte | 7 ------ .../samples/props-not-bindable/Counter.svelte | 5 ---- .../samples/props-not-bindable/_config.js | 13 ---------- .../samples/props-not-bindable/main.svelte | 7 ------ 14 files changed, 11 insertions(+), 124 deletions(-) create mode 100644 .changeset/flat-otters-cheer.md delete mode 100644 packages/svelte/tests/runtime-runes/samples/export-binding/_config.js delete mode 100644 packages/svelte/tests/runtime-runes/samples/export-binding/counter/index.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/export-binding/main.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/Counter.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js delete mode 100644 packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/main.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/props-not-bindable/Counter.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js delete mode 100644 packages/svelte/tests/runtime-runes/samples/props-not-bindable/main.svelte diff --git a/.changeset/flat-otters-cheer.md b/.changeset/flat-otters-cheer.md new file mode 100644 index 0000000000..3f84de8242 --- /dev/null +++ b/.changeset/flat-otters-cheer.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: remove bindable prop validation diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 90901d29ce..a969117ed3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -299,28 +299,6 @@ export function client_component(analysis, options) { (binding.kind === 'prop' || binding.kind === 'bindable_prop') && !name.startsWith('$$') ); - if (dev && analysis.runes) { - const exports = analysis.exports.map(({ name, alias }) => b.literal(alias ?? name)); - /** @type {ESTree.Literal[]} */ - const bindable = []; - for (const [name, binding] of properties) { - if (binding.kind === 'bindable_prop') { - bindable.push(b.literal(binding.prop_alias ?? name)); - } - } - instance.body.unshift( - b.stmt( - b.call( - '$.validate_prop_bindings', - b.id('$$props'), - b.array(bindable), - b.array(exports), - b.id(`${analysis.name}`) - ) - ) - ); - } - if (analysis.accessors) { for (const [name, binding] of properties) { const key = binding.prop_alias ?? name; diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 3b85ae1816..2bf58c51f7 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -150,7 +150,7 @@ export { setContext, hasContext } from './runtime.js'; -export { validate_binding, validate_each_keys, validate_prop_bindings } from './validate.js'; +export { validate_binding, validate_each_keys } from './validate.js'; export { raf } from './timing.js'; export { proxy } from './proxy.js'; export { create_custom_element } from './dom/elements/custom-element.js'; diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index 8fb13f7e2c..3e5a0258c7 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -297,8 +297,10 @@ export function prop(props, key, flags, fallback) { var is_entry_props = STATE_SYMBOL in props || LEGACY_PROPS in props; var setter = - get_descriptor(props, key)?.set ?? - (is_entry_props && bindable && key in props ? (v) => (props[key] = v) : undefined); + (bindable && + (get_descriptor(props, key)?.set ?? + (is_entry_props && key in props && ((v) => (props[key] = v))))) || + undefined; var fallback_value = /** @type {V} */ (fallback); var fallback_dirty = true; diff --git a/packages/svelte/src/internal/client/validate.js b/packages/svelte/src/internal/client/validate.js index 417c145eb2..24e280edf8 100644 --- a/packages/svelte/src/internal/client/validate.js +++ b/packages/svelte/src/internal/client/validate.js @@ -1,5 +1,5 @@ import { dev_current_component_function } from './runtime.js'; -import { get_descriptor, is_array } from '../shared/utils.js'; +import { is_array } from '../shared/utils.js'; import * as e from './errors.js'; import { FILENAME } from '../../constants.js'; import { render_effect } from './reactivity/effects.js'; @@ -38,29 +38,6 @@ export function validate_each_keys(collection, key_fn) { }); } -/** - * @param {Record<string, any>} $$props - * @param {string[]} bindable - * @param {string[]} exports - * @param {Function & { [FILENAME]: string }} component - */ -export function validate_prop_bindings($$props, bindable, exports, component) { - for (const key in $$props) { - var setter = get_descriptor($$props, key)?.set; - var name = component.name; - - if (setter) { - if (exports.includes(key) && !bindable.includes(key)) { - e.bind_invalid_export(component[FILENAME], key, name); - } - - if (!bindable.includes(key)) { - e.bind_not_bindable(key, component[FILENAME], name); - } - } - } -} - /** * @param {string} binding * @param {() => Record<string, any>} get_object diff --git a/packages/svelte/tests/runtime-runes/samples/export-binding/_config.js b/packages/svelte/tests/runtime-runes/samples/export-binding/_config.js deleted file mode 100644 index d3b9c8e4eb..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/export-binding/_config.js +++ /dev/null @@ -1,10 +0,0 @@ -import { test } from '../../test'; - -export default test({ - compileOptions: { - dev: true // to ensure we we catch the error - }, - error: - 'bind_invalid_export\n' + - 'Component counter/index.svelte has an export named `increment` that a consumer component is trying to access using `bind:increment`, which is disallowed. Instead, use `bind:this` (e.g. `<Counter bind:this={component} />`) and then access the property on the bound component instance (e.g. `component.increment`)' -}); diff --git a/packages/svelte/tests/runtime-runes/samples/export-binding/counter/index.svelte b/packages/svelte/tests/runtime-runes/samples/export-binding/counter/index.svelte deleted file mode 100644 index 14e0de961b..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/export-binding/counter/index.svelte +++ /dev/null @@ -1,8 +0,0 @@ -<script> - let count = $state(0); - export function increment() { - count++; - } -</script> - -{count} diff --git a/packages/svelte/tests/runtime-runes/samples/export-binding/main.svelte b/packages/svelte/tests/runtime-runes/samples/export-binding/main.svelte deleted file mode 100644 index 4ad1684701..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/export-binding/main.svelte +++ /dev/null @@ -1,7 +0,0 @@ -<script> - import Counter from './counter/index.svelte'; - let increment; -</script> - -<Counter bind:increment={increment} /> -<button onclick={increment}>increment</button> diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/Counter.svelte b/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/Counter.svelte deleted file mode 100644 index f22fd6e976..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/Counter.svelte +++ /dev/null @@ -1,5 +0,0 @@ -<script> - let { ...rest } = $props(); -</script> - -{rest.count} diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js b/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js deleted file mode 100644 index fa0994c370..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/_config.js +++ /dev/null @@ -1,13 +0,0 @@ -import { test } from '../../test'; - -export default test({ - compileOptions: { - dev: true - }, - - html: '0', - - error: - 'bind_not_bindable\n' + - 'A component is attempting to bind to a non-bindable property `count` belonging to Counter.svelte (i.e. `<Counter bind:count={...}>`). To mark a property as bindable: `let { count = $bindable() } = $props()`' -}); diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/main.svelte deleted file mode 100644 index 80242b75c6..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable-spread/main.svelte +++ /dev/null @@ -1,7 +0,0 @@ -<script> - import Counter from './Counter.svelte'; - - let count = $state(0); -</script> - -<Counter bind:count /> diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/Counter.svelte b/packages/svelte/tests/runtime-runes/samples/props-not-bindable/Counter.svelte deleted file mode 100644 index 4bc2db3968..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/Counter.svelte +++ /dev/null @@ -1,5 +0,0 @@ -<script> - let { count } = $props(); -</script> - -{count} diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js b/packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js deleted file mode 100644 index fa0994c370..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/_config.js +++ /dev/null @@ -1,13 +0,0 @@ -import { test } from '../../test'; - -export default test({ - compileOptions: { - dev: true - }, - - html: '0', - - error: - 'bind_not_bindable\n' + - 'A component is attempting to bind to a non-bindable property `count` belonging to Counter.svelte (i.e. `<Counter bind:count={...}>`). To mark a property as bindable: `let { count = $bindable() } = $props()`' -}); diff --git a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-not-bindable/main.svelte deleted file mode 100644 index 80242b75c6..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/props-not-bindable/main.svelte +++ /dev/null @@ -1,7 +0,0 @@ -<script> - import Counter from './Counter.svelte'; - - let count = $state(0); -</script> - -<Counter bind:count /> From a2565efa37dfbba2e462eb5cdbb6c47424ce98ad Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 8 Jan 2025 19:37:26 +0100 Subject: [PATCH 0067/1429] docs: tweak "invalid assignment" compiler error message (#14955) * docs: tweak "invalid assignment" compiler error message fixes #14702 * tweak wording --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/sharp-dryers-try.md | 5 +++ .../98-reference/.generated/compile-errors.md | 34 ++++++++++++++++++- .../svelte/messages/compile-errors/script.md | 34 ++++++++++++++++++- packages/svelte/src/compiler/errors.js | 4 +-- .../_config.js | 2 +- .../runes-invalid-each-binding/_config.js | 2 +- .../runes-invalid-each-mutation/_config.js | 2 +- 7 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 .changeset/sharp-dryers-try.md diff --git a/.changeset/sharp-dryers-try.md b/.changeset/sharp-dryers-try.md new file mode 100644 index 0000000000..dcdbd99d02 --- /dev/null +++ b/.changeset/sharp-dryers-try.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: tweak "invalid assignment" compiler error message diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index c498920007..a867cfe88c 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -331,7 +331,39 @@ The $ prefix is reserved, and cannot be used for variables and imports ### each_item_invalid_assignment ``` -Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`) +Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`, or `bind:value={array[i]}` instead of `bind:value={entry}`) +``` + +In legacy mode, it was possible to reassign or bind to the each block argument itself: + +```svelte +<script> + let array = [1, 2, 3]; +</script> + +{#each array as entry} + <!-- reassignment --> + <button on:click={() => entry = 4}>change</button> + + <!-- binding --> + <input bind:value={entry}> +{/each} +``` + +This turned out to be buggy and unpredictable, particularly when working with derived values (such as `array.map(...)`), and as such is forbidden in runes mode. You can achieve the same outcome by using the index instead: + +```svelte +<script> + let array = $state([1, 2, 3]); +</script> + +{#each array as entry, i} + <!-- reassignment --> + <button onclick={() => array[i] = 4}>change</button> + + <!-- binding --> + <input bind:value={array[i]}> +{/each} ``` ### effect_invalid_placement diff --git a/packages/svelte/messages/compile-errors/script.md b/packages/svelte/messages/compile-errors/script.md index fa851cec89..0aa6fbed90 100644 --- a/packages/svelte/messages/compile-errors/script.md +++ b/packages/svelte/messages/compile-errors/script.md @@ -32,7 +32,39 @@ ## each_item_invalid_assignment -> Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`) +> Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`, or `bind:value={array[i]}` instead of `bind:value={entry}`) + +In legacy mode, it was possible to reassign or bind to the each block argument itself: + +```svelte +<script> + let array = [1, 2, 3]; +</script> + +{#each array as entry} + <!-- reassignment --> + <button on:click={() => entry = 4}>change</button> + + <!-- binding --> + <input bind:value={entry}> +{/each} +``` + +This turned out to be buggy and unpredictable, particularly when working with derived values (such as `array.map(...)`), and as such is forbidden in runes mode. You can achieve the same outcome by using the index instead: + +```svelte +<script> + let array = $state([1, 2, 3]); +</script> + +{#each array as entry, i} + <!-- reassignment --> + <button onclick={() => array[i] = 4}>change</button> + + <!-- binding --> + <input bind:value={array[i]}> +{/each} +``` ## effect_invalid_placement diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index 870cd9ac09..a997eeef8d 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -151,12 +151,12 @@ export function dollar_prefix_invalid(node) { } /** - * Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`) + * Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`, or `bind:value={array[i]}` instead of `bind:value={entry}`) * @param {null | number | NodeLike} node * @returns {never} */ export function each_item_invalid_assignment(node) { - e(node, "each_item_invalid_assignment", `Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. \`array[i] = value\` instead of \`entry = value\`)\nhttps://svelte.dev/e/each_item_invalid_assignment`); + e(node, "each_item_invalid_assignment", `Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. \`array[i] = value\` instead of \`entry = value\`, or \`bind:value={array[i]}\` instead of \`bind:value={entry}\`)\nhttps://svelte.dev/e/each_item_invalid_assignment`); } /** diff --git a/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding-this/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding-this/_config.js index 524c2f161c..ed02e0960d 100644 --- a/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding-this/_config.js +++ b/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding-this/_config.js @@ -4,6 +4,6 @@ export default test({ error: { code: 'each_item_invalid_assignment', message: - 'Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`)' + 'Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`, or `bind:value={array[i]}` instead of `bind:value={entry}`)' } }); diff --git a/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding/_config.js index 524c2f161c..ed02e0960d 100644 --- a/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding/_config.js +++ b/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-binding/_config.js @@ -4,6 +4,6 @@ export default test({ error: { code: 'each_item_invalid_assignment', message: - 'Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`)' + 'Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`, or `bind:value={array[i]}` instead of `bind:value={entry}`)' } }); diff --git a/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-mutation/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-mutation/_config.js index 524c2f161c..ed02e0960d 100644 --- a/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-mutation/_config.js +++ b/packages/svelte/tests/compiler-errors/samples/runes-invalid-each-mutation/_config.js @@ -4,6 +4,6 @@ export default test({ error: { code: 'each_item_invalid_assignment', message: - 'Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`)' + 'Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. `array[i] = value` instead of `entry = value`, or `bind:value={array[i]}` instead of `bind:value={entry}`)' } }); From 9aae463ae1be27a75e876a7a6f6bcfe5b023c19f Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 8 Jan 2025 20:03:04 +0100 Subject: [PATCH 0068/1429] fix: silence false-positive statel value warning (#14958) fixes #14687 --- .changeset/tough-guests-sniff.md | 5 ++++ .../client/visitors/AssignmentExpression.js | 1 + .../_config.js | 24 ++++++++++++++----- .../main.svelte | 4 +++- 4 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 .changeset/tough-guests-sniff.md diff --git a/.changeset/tough-guests-sniff.md b/.changeset/tough-guests-sniff.md new file mode 100644 index 0000000000..fcb7b67c1a --- /dev/null +++ b/.changeset/tough-guests-sniff.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: silence false-positive stale value warning diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js index 3d8b9aaa96..0c70f7e00c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js @@ -171,6 +171,7 @@ function build_assignment(operator, left, right, context) { // special case — ignore `bind:prop={getter, (v) => (...)}` / `bind:value={x.y}` if ( + path.at(-1) === 'BindDirective' || path.at(-1) === 'Component' || path.at(-1) === 'SvelteComponent' || (path.at(-1) === 'ArrowFunctionExpression' && diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js index ad7bdc654a..f029227472 100644 --- a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js @@ -1,21 +1,33 @@ import { flushSync } from 'svelte'; -import { test } from '../../test'; +import { ok, test } from '../../test'; export default test({ compileOptions: { dev: true }, - html: `<button>items: null</button> <div>x</div>`, + html: `<button>items: null</button> <div>x</div> <input type="checkbox" value="1"><input type="checkbox" value="2">`, test({ assert, target, warnings }) { const btn = target.querySelector('button'); + ok(btn); - flushSync(() => btn?.click()); - assert.htmlEqual(target.innerHTML, `<button>items: []</button> <div>x</div>`); + flushSync(() => btn.click()); + assert.htmlEqual( + target.innerHTML, + `<button>items: []</button> <div>x</div> <input type="checkbox" value="1"><input type="checkbox" value="2">` + ); - flushSync(() => btn?.click()); - assert.htmlEqual(target.innerHTML, `<button>items: [0]</button> <div>x</div>`); + flushSync(() => btn.click()); + assert.htmlEqual( + target.innerHTML, + `<button>items: [0]</button> <div>x</div> <input type="checkbox" value="1"><input type="checkbox" value="2">` + ); + + const input = target.querySelector('input'); + ok(input); + input.checked = true; + flushSync(() => input.dispatchEvent(new Event('change', { bubbles: true }))); assert.deepEqual(warnings, [ 'Assignment to `items` property (main.svelte:8:24) will evaluate to the right-hand side, not the value of `items` following the assignment. This may result in unexpected behaviour.' diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte index 40592e08b8..ad94c4e56e 100644 --- a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte @@ -2,7 +2,7 @@ import Test from './Test.svelte'; let entries = $state([]); - let object = $state({ items: null }); + let object = $state({ items: null, group: [] }); </script> <button onclick={() => (object.items ??= []).push(object.items.length)}> @@ -11,6 +11,8 @@ <!-- these should not emit warnings --> <div bind:this={entries[0]}>x</div> +<input type="checkbox" value=1 bind:group={object.group}> +<input type="checkbox" value=2 bind:group={object.group}> <Test bind:this={entries[1]}></Test> <Test bind:this={() => entries[2], (v) => (entries[2] = v)}></Test> <Test bind:x={entries[3]}></Test> From 80d9f9996f4aec24e6ab4542225a5dffa297b5c7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 14:12:06 -0500 Subject: [PATCH 0069/1429] Version Packages (#14953) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/flat-otters-cheer.md | 5 ----- .changeset/sharp-dryers-try.md | 5 ----- .changeset/tough-guests-sniff.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 .changeset/flat-otters-cheer.md delete mode 100644 .changeset/sharp-dryers-try.md delete mode 100644 .changeset/tough-guests-sniff.md diff --git a/.changeset/flat-otters-cheer.md b/.changeset/flat-otters-cheer.md deleted file mode 100644 index 3f84de8242..0000000000 --- a/.changeset/flat-otters-cheer.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: remove bindable prop validation diff --git a/.changeset/sharp-dryers-try.md b/.changeset/sharp-dryers-try.md deleted file mode 100644 index dcdbd99d02..0000000000 --- a/.changeset/sharp-dryers-try.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: tweak "invalid assignment" compiler error message diff --git a/.changeset/tough-guests-sniff.md b/.changeset/tough-guests-sniff.md deleted file mode 100644 index fcb7b67c1a..0000000000 --- a/.changeset/tough-guests-sniff.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: silence false-positive stale value warning diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index daf8ed209a..72fa09bf96 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.17.1 + +### Patch Changes + +- fix: remove bindable prop validation ([#14946](https://github.com/sveltejs/svelte/pull/14946)) + +- chore: tweak "invalid assignment" compiler error message ([#14955](https://github.com/sveltejs/svelte/pull/14955)) + +- fix: silence false-positive stale value warning ([#14958](https://github.com/sveltejs/svelte/pull/14958)) + ## 5.17.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 5cd078bf77..ce6e4ffc42 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.17.0", + "version": "5.17.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 396c2b07ef..bb772687fb 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.17.0'; +export const VERSION = '5.17.1'; export const PUBLIC_VERSION = '5'; From 79a67c95618a631129997e542fca66266a63d500 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 8 Jan 2025 15:04:41 -0500 Subject: [PATCH 0070/1429] fix: account for parent scale when animating elements (#14957) * WIP fix flip * fix: account for parent scale when animating elements --- .changeset/long-weeks-brush.md | 5 +++++ packages/svelte/src/animate/index.js | 32 ++++++++++++++++++++++------ 2 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 .changeset/long-weeks-brush.md diff --git a/.changeset/long-weeks-brush.md b/.changeset/long-weeks-brush.md new file mode 100644 index 0000000000..b0e184caa0 --- /dev/null +++ b/.changeset/long-weeks-brush.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: account for parent scale when animating elements diff --git a/packages/svelte/src/animate/index.js b/packages/svelte/src/animate/index.js index fbf2ac83e2..2ce7708bc7 100644 --- a/packages/svelte/src/animate/index.js +++ b/packages/svelte/src/animate/index.js @@ -11,18 +11,37 @@ import { cubicOut } from '../easing/index.js'; * @returns {AnimationConfig} */ export function flip(node, { from, to }, params = {}) { + var { delay = 0, duration = (d) => Math.sqrt(d) * 120, easing = cubicOut } = params; + var style = getComputedStyle(node); - var zoom = get_zoom(node); // https://drafts.csswg.org/css-viewport/#effective-zoom + // find the transform origin, expressed as a pair of values between 0 and 1 var transform = style.transform === 'none' ? '' : style.transform; var [ox, oy] = style.transformOrigin.split(' ').map(parseFloat); + ox /= node.clientWidth; + oy /= node.clientHeight; + + // calculate effect of parent transforms and zoom + var zoom = get_zoom(node); // https://drafts.csswg.org/css-viewport/#effective-zoom + var sx = node.clientWidth / to.width / zoom; + var sy = node.clientHeight / to.height / zoom; + + // find the starting position of the transform origin + var fx = from.left + from.width * ox; + var fy = from.top + from.height * oy; + + // find the ending position of the transform origin + var tx = to.left + to.width * ox; + var ty = to.top + to.height * oy; + + // find the translation at the start of the transform + var dx = (fx - tx) * sx; + var dy = (fy - ty) * sy; + + // find the relative scale at the start of the transform var dsx = from.width / to.width; var dsy = from.height / to.height; - var dx = (from.left + dsx * ox - (to.left + ox)) / zoom; - var dy = (from.top + dsy * oy - (to.top + oy)) / zoom; - var { delay = 0, duration = (d) => Math.sqrt(d) * 120, easing = cubicOut } = params; - return { delay, duration: typeof duration === 'function' ? duration(Math.sqrt(dx * dx + dy * dy)) : duration, @@ -32,7 +51,8 @@ export function flip(node, { from, to }, params = {}) { var y = u * dy; var sx = t + u * dsx; var sy = t + u * dsy; - return `transform: ${transform} scale(${sx}, ${sy}) translate(${x}px, ${y}px);`; + + return `transform: ${transform} translate(${x}px, ${y}px) scale(${sx}, ${sy});`; } }; } From bf80c10625d488a479bf0c69bd3e7c0fa7fe82bb Mon Sep 17 00:00:00 2001 From: Basti Ortiz <39114273+BastiDood@users.noreply.github.com> Date: Thu, 9 Jan 2025 04:16:53 +0800 Subject: [PATCH 0071/1429] docs: clarify example on dynamic dependency tracking (#13767) * docs(effect): clarify example on dynamic dependency tracking * use an if block, it's clearer * fix * toggle --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- documentation/docs/02-runes/04-$effect.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md index c48a82f9a5..1ea960de70 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -125,7 +125,11 @@ An effect only reruns when the object it reads changes, not when a property insi <p>{state.value} doubled is {derived.value}</p> ``` -An effect only depends on the values that it read the last time it ran. If `a` is true, changes to `b` will [not cause this effect to rerun](/playground/untitled#H4sIAAAAAAAAE3WQ0WrDMAxFf0U1hTow1vcsMfQ7lj3YjlxEXTvEymC4_vfFC6Ewtidxde8RkrJw5DGJ9j2LoO8oWnGZJvEi-GuqIn2iZ1x1istsa6dLdqaJ1RAG9sigoYdjYs0onfYJm7fdMX85q3dE59CylA30CnJtDWxjSNHjq49XeZqXEChcT9usLUAOpIbHA0yzM78oColGhDVofLS3neZSS6mqOz-XD51ZmGOAGKwne-vztk-956CL0kAJsi7decupf4l658EUZX4I8yTWt93jSI5wFC3PC5aP8g0Aje5DcQEAAA==): +An effect only depends on the values that it read the last time it ran. This has interesting implications for effects that have conditional code. + +For instance, if `a` is `true` in the code snippet below, the code inside the `if` block will run and `b` will be evaluated. As such, changes to either `a` or `b` [will cause the effect to re-run](/playground/untitled#H4sIAAAAAAAAE3VQzWrDMAx-FdUU4kBp71li6EPstOxge0ox8-QQK2PD-N1nLy2F0Z2Evj9_chKkP1B04pnYscc3cRCT8xhF95IEf8-Vq0DBr8rzPB_jJ3qumNERH-E2ECNxiRF9tIubWY00lgcYNAywj6wZJS8rtk83wjwgCrXHaULLUrYwKEgVGrnkx-Dx6MNFNstK5OjSbFGbwE0gdXuT_zGYrjmAuco515Hr1p_uXak3K3MgCGS9s-9D2grU-judlQYXIencnzad-tdR79qZrMyvw9wd5Z8Yv1h09dz8mn8AkM7Pfo0BAAA=). + +Conversely, if `a` is `false`, `b` will not be evaluated, and the effect will _only_ re-run when `a` changes. ```ts let a = false; @@ -134,8 +138,8 @@ let b = false; $effect(() => { console.log('running'); - if (a || b) { - console.log('inside if block'); + if (a) { + console.log('b:', b); } }); ``` From d15aea4e187a9ef05297fd04a83023d3ccbc0bef Mon Sep 17 00:00:00 2001 From: Gonzalo Ruiz <rgon@outlook.com> Date: Wed, 8 Jan 2025 21:32:57 +0100 Subject: [PATCH 0072/1429] docs: clarify svelte:component migration, avoids common gotcha (#13835) * docs: clarify svelte:component migration to avoid lowercase component name gotcha * docs: move <svelte:component> section out of Breaking Changes, since it is not breaking. Change links. * docs: move migration dot notation component initialization notes to svelte:component section * tweaks * fix link --------- Co-authored-by: Gonzalo Ruiz <rgon@rgon.es> Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .../docs/07-misc/07-v5-migration-guide.md | 93 +++++++++---------- .../99-legacy/30-legacy-svelte-component.md | 2 +- 2 files changed, 45 insertions(+), 50 deletions(-) diff --git a/documentation/docs/07-misc/07-v5-migration-guide.md b/documentation/docs/07-misc/07-v5-migration-guide.md index 09ea84f26c..29179cd849 100644 --- a/documentation/docs/07-misc/07-v5-migration-guide.md +++ b/documentation/docs/07-misc/07-v5-migration-guide.md @@ -597,29 +597,58 @@ export declare const MyComponent: Component<{ To declare that a component of a certain type is required: -```svelte -<script lang="ts"> - import type { ---SvelteComponent--- +++Component+++ } from 'svelte'; - import { - ComponentA, - ComponentB - } from 'component-library'; - - ---let component: typeof SvelteComponent<{ foo: string }>--- - +++let component: Component<{ foo: string }>+++ = $state( - Math.random() ? ComponentA : ComponentB - ); -</script> +```js +import { ComponentA, ComponentB } from 'component-library'; +---import type { SvelteComponent } from 'svelte';--- ++++import type { Component } from 'svelte';+++ -<svelte:component this={component} foo="bar" /> +---let C: typeof SvelteComponent<{ foo: string }> = $state(--- ++++let C: Component<{ foo: string }> = $state(+++ + Math.random() ? ComponentA : ComponentB +); ``` -The two utility types `ComponentEvents` and `ComponentType` are also deprecated. `ComponentEvents` is obsolete because events are defined as callback props now, and `ComponentType` is obsolete because the new `Component` type is the component type already (e.g. `ComponentType<SvelteComponent<{ prop: string }>>` == `Component<{ prop: string }>`). +The two utility types `ComponentEvents` and `ComponentType` are also deprecated. `ComponentEvents` is obsolete because events are defined as callback props now, and `ComponentType` is obsolete because the new `Component` type is the component type already (i.e. `ComponentType<SvelteComponent<{ prop: string }>>` is equivalent to `Component<{ prop: string }>`). ### bind:this changes Because components are no longer classes, using `bind:this` no longer returns a class instance with `$set`, `$on` and `$destroy` methods on it. It only returns the instance exports (`export function/const`) and, if you're using the `accessors` option, a getter/setter-pair for each property. +## `<svelte:component>` is no longer necessary + +In Svelte 4, components are _static_ — if you render `<Thing>`, and the value of `Thing` changes, [nothing happens](/playground/7f1fa24f0ab44c1089dcbb03568f8dfa?version=4.2.18). To make it dynamic you had to use `<svelte:component>`. + +This is no longer true in Svelte 5: + +```svelte +<script> + import A from './A.svelte'; + import B from './B.svelte'; + + let Thing = $state(); +</script> + +<select bind:value={Thing}> + <option value={A}>A</option> + <option value={B}>B</option> +</select> + +<!-- these are equivalent --> +<Thing /> +<svelte:component this={Thing} /> +``` +While migrating, keep in mind that your component's name should be capitalized (`Thing`) to distinguish it from elements, unless using dot notation. + +### Dot notation indicates a component + +In Svelte 4, `<foo.bar>` would create an element with a tag name of `"foo.bar"`. In Svelte 5, `foo.bar` is treated as a component instead. This is particularly useful inside `each` blocks: + +```svelte +{#each items as item} + <item.component {...item.props} /> +{/each} +``` + ## Whitespace handling changed Previously, Svelte employed a very complicated algorithm to determine if whitespace should be kept or not. Svelte 5 simplifies this which makes it easier to reason about as a developer. The rules are: @@ -653,16 +682,6 @@ The `legacy` compiler option, which generated bulkier but IE-friendly code, no l Content inside component tags becomes a snippet prop called `children`. You cannot have a separate prop by that name. -## Dot notation indicates a component - -In Svelte 4, `<foo.bar>` would create an element with a tag name of `"foo.bar"`. In Svelte 5, `foo.bar` is treated as a component instead. This is particularly useful inside `each` blocks: - -```svelte -{#each items as item} - <item.component {...item.props} /> -{/each} -``` - ## Breaking changes in runes mode Some breaking changes only apply once your component is in runes mode. @@ -700,30 +719,6 @@ In Svelte 4, doing the following triggered reactivity: This is because the Svelte compiler treated the assignment to `foo.value` as an instruction to update anything that referenced `foo`. In Svelte 5, reactivity is determined at runtime rather than compile time, so you should define `value` as a reactive `$state` field on the `Foo` class. Wrapping `new Foo()` with `$state(...)` will have no effect — only vanilla objects and arrays are made deeply reactive. -### `<svelte:component>` is no longer necessary - -In Svelte 4, components are _static_ — if you render `<Thing>`, and the value of `Thing` changes, [nothing happens](/playground/7f1fa24f0ab44c1089dcbb03568f8dfa?version=4.2.18). To make it dynamic you must use `<svelte:component>`. - -This is no longer true in Svelte 5: - -```svelte -<script> - import A from './A.svelte'; - import B from './B.svelte'; - - let Thing = $state(); -</script> - -<select bind:value={Thing}> - <option value={A}>A</option> - <option value={B}>B</option> -</select> - -<!-- these are equivalent --> -<Thing /> -<svelte:component this={Thing} /> -``` - ### Touch and wheel events are passive When using `onwheel`, `onmousewheel`, `ontouchstart` and `ontouchmove` event attributes, the handlers are [passive](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#using_passive_listeners) to align with browser defaults. This greatly improves responsiveness by allowing the browser to scroll the document immediately, rather than waiting to see if the event handler calls `event.preventDefault()`. diff --git a/documentation/docs/99-legacy/30-legacy-svelte-component.md b/documentation/docs/99-legacy/30-legacy-svelte-component.md index 3da2e3350e..a0407e58bf 100644 --- a/documentation/docs/99-legacy/30-legacy-svelte-component.md +++ b/documentation/docs/99-legacy/30-legacy-svelte-component.md @@ -2,7 +2,7 @@ title: <svelte:component> --- -In runes mode, `<MyComponent>` will re-render if the value of `MyComponent` changes. See the [Svelte 5 migration guide](/docs/svelte/v5-migration-guide#Breaking-changes-in-runes-mode-svelte:component-is-no-longer-necessary) for an example. +In runes mode, `<MyComponent>` will re-render if the value of `MyComponent` changes. See the [Svelte 5 migration guide](/docs/svelte/v5-migration-guide#svelte:component-is-no-longer-necessary) for an example. In legacy mode, it won't — we must use `<svelte:component>`, which destroys and recreates the component instance when the value of its `this` expression changes: From 3e11b892447a466c95ed85d6a7fd14c54b9c5200 Mon Sep 17 00:00:00 2001 From: Matei Trandafir <catalin_trand2@yahoo.com> Date: Wed, 8 Jan 2025 22:54:41 +0200 Subject: [PATCH 0073/1429] docs: improve section for snippets-slots interop (#13869) * docs: improve section for snippets-slots interop * tweak --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .../docs/07-misc/07-v5-migration-guide.md | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/documentation/docs/07-misc/07-v5-migration-guide.md b/documentation/docs/07-misc/07-v5-migration-guide.md index 29179cd849..ce95bf6ac7 100644 --- a/documentation/docs/07-misc/07-v5-migration-guide.md +++ b/documentation/docs/07-misc/07-v5-migration-guide.md @@ -339,7 +339,31 @@ When spreading props, local event handlers must go _after_ the spread, or they r In Svelte 4, content can be passed to components using slots. Svelte 5 replaces them with snippets which are more powerful and flexible, and as such slots are deprecated in Svelte 5. -They continue to work, however, and you can mix and match snippets and slots in your components. +They continue to work, however, and you can pass snippets to a component that uses slots: + +```svelte +<!--- file: Child.svelte ---> +<slot /> +<hr /> +<slot name="foo" message="hello" /> +``` + +```svelte +<!--- file: Parent.svelte ---> +<script> + import Child from './Child.svelte'; +</script> + +<Child> + default child content + + {#snippet foo({ message })} + message from child: {message} + {/snippet} +</Child> +``` + +(The reverse is not true — you cannot pass slotted content to a component that uses [`{@render ...}`](/docs/svelte/@render) tags.) When using custom elements, you should still use `<slot />` like before. In a future version, when Svelte removes its internal version of slots, it will leave those slots as-is, i.e. output a regular DOM tag instead of transforming it. From 68cffa84892c20717dc7d0e49a3a3af2f403f689 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 8 Jan 2025 16:17:32 -0500 Subject: [PATCH 0074/1429] fix: apply `overflow: hidden` style when transitioning elements, where necessary (#14930) * fix: apply `overflow: hidden` style when transitioning elements, where necessary * notes to self --- .changeset/sixty-paws-compete.md | 5 ++++ .../client/dom/elements/transitions.js | 26 +++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 .changeset/sixty-paws-compete.md diff --git a/.changeset/sixty-paws-compete.md b/.changeset/sixty-paws-compete.md new file mode 100644 index 0000000000..2fa3f9e84f --- /dev/null +++ b/.changeset/sixty-paws-compete.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: apply `overflow: hidden` style when transitioning elements, where necessary diff --git a/packages/svelte/src/internal/client/dom/elements/transitions.js b/packages/svelte/src/internal/client/dom/elements/transitions.js index dee029e88f..b3c16cdd08 100644 --- a/packages/svelte/src/internal/client/dom/elements/transitions.js +++ b/packages/svelte/src/internal/client/dom/elements/transitions.js @@ -192,6 +192,13 @@ export function transition(flags, element, get_fn, get_params) { var inert = element.inert; + /** + * The default overflow style, stashed so we can revert changes during the transition + * that are necessary to work around a Safari <18 bug + * TODO 6.0 remove this, if older versions of Safari have died out enough + */ + var overflow = element.style.overflow; + /** @type {Animation | undefined} */ var intro; @@ -242,6 +249,8 @@ export function transition(flags, element, get_fn, get_params) { // Ensure we cancel the animation to prevent leaking intro?.abort(); intro = current_options = undefined; + + element.style.overflow = overflow; }); }, out(fn) { @@ -382,16 +391,29 @@ function animate(element, options, counterpart, t2, on_finish) { var keyframes = []; if (duration > 0) { + /** + * Whether or not the CSS includes `overflow: hidden`, in which case we need to + * add it as an inline style to work around a Safari <18 bug + * TODO 6.0 remove this, if possible + */ + var needs_overflow_hidden = false; + if (css) { var n = Math.ceil(duration / (1000 / 60)); // `n` must be an integer, or we risk missing the `t2` value for (var i = 0; i <= n; i += 1) { var t = t1 + delta * easing(i / n); - var styles = css(t, 1 - t); - keyframes.push(css_to_keyframe(styles)); + var styles = css_to_keyframe(css(t, 1 - t)); + keyframes.push(styles); + + needs_overflow_hidden ||= styles.overflow === 'hidden'; } } + if (needs_overflow_hidden) { + /** @type {HTMLElement} */ (element).style.overflow = 'hidden'; + } + get_t = () => { var time = /** @type {number} */ ( /** @type {globalThis.Animation} */ (animation).currentTime From c0842d14596dff8a26b487dc6a75207d45198261 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Thu, 9 Jan 2025 05:49:07 -0500 Subject: [PATCH 0075/1429] fix: properly add owners to function bindings (#14962) Closes #14956 --- .changeset/yellow-dodos-smell.md | 5 ++++ .../client/visitors/shared/component.js | 29 ++++++++++++------- .../ownership-function-bindings/Child.svelte | 5 ++++ .../ownership-function-bindings/_config.js | 20 +++++++++++++ .../ownership-function-bindings/main.svelte | 10 +++++++ 5 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 .changeset/yellow-dodos-smell.md create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/main.svelte diff --git a/.changeset/yellow-dodos-smell.md b/.changeset/yellow-dodos-smell.md new file mode 100644 index 0000000000..ea2aead662 --- /dev/null +++ b/.changeset/yellow-dodos-smell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: properly add owners to function bindings diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js index 068971145c..f509cb41a7 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js @@ -174,23 +174,32 @@ export function build_component(node, component_name, context, anchor = context. } else if (attribute.type === 'BindDirective') { const expression = /** @type {Expression} */ (context.visit(attribute.expression)); - if (dev && attribute.name !== 'this' && attribute.expression.type !== 'SequenceExpression') { - const left = object(attribute.expression); - let binding; + if (dev && attribute.name !== 'this') { + let should_add_owner = true; - if (left?.type === 'Identifier') { - binding = context.state.scope.get(left.name); + if (attribute.expression.type !== 'SequenceExpression') { + const left = object(attribute.expression); + + if (left?.type === 'Identifier') { + const binding = context.state.scope.get(left.name); + + // Only run ownership addition on $state fields. + // Theoretically someone could create a `$state` while creating `$state.raw` or inside a `$derived.by`, + // but that feels so much of an edge case that it doesn't warrant a perf hit for the common case. + if (binding?.kind === 'derived' || binding?.kind === 'raw_state') { + should_add_owner = false; + } + } } - // Only run ownership addition on $state fields. - // Theoretically someone could create a `$state` while creating `$state.raw` or inside a `$derived.by`, - // but that feels so much of an edge case that it doesn't warrant a perf hit for the common case. - if (binding?.kind !== 'derived' && binding?.kind !== 'raw_state') { + if (should_add_owner) { binding_initializers.push( b.stmt( b.call( b.id('$.add_owner_effect'), - b.thunk(expression), + expression.type === 'SequenceExpression' + ? expression.expressions[0] + : b.thunk(expression), b.id(component_name), is_ignored(node, 'ownership_invalid_binding') && b.true ) diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/Child.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/Child.svelte new file mode 100644 index 0000000000..ef91b0756d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/Child.svelte @@ -0,0 +1,5 @@ +<script> + let { arr = $bindable() } = $props(); +</script> + +<button onclick={() => arr.push(arr.length)}></button> \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/_config.js b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/_config.js new file mode 100644 index 0000000000..4c77aea206 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/_config.js @@ -0,0 +1,20 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + test({ target, warnings, assert }) { + const btn = target.querySelector('button'); + flushSync(() => { + btn?.click(); + }); + assert.deepEqual(warnings, []); + + flushSync(() => { + btn?.click(); + }); + assert.deepEqual(warnings, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/main.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/main.svelte new file mode 100644 index 0000000000..4a3ce82726 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-function-bindings/main.svelte @@ -0,0 +1,10 @@ +<script> + import Child from './Child.svelte'; + + let arr = $state([]); + let arr2 = $state([]); + + let len = $derived(arr.length + arr2.length); +</script> + +<Child bind:arr={() => len % 2 === 0 ? arr : arr2, (v) => {}} /> \ No newline at end of file From b7400ae93b21f74030bd80a58f3d1e23edd1a5cf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 08:41:06 -0500 Subject: [PATCH 0076/1429] Version Packages (#14959) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/long-weeks-brush.md | 5 ----- .changeset/sixty-paws-compete.md | 5 ----- .changeset/yellow-dodos-smell.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 .changeset/long-weeks-brush.md delete mode 100644 .changeset/sixty-paws-compete.md delete mode 100644 .changeset/yellow-dodos-smell.md diff --git a/.changeset/long-weeks-brush.md b/.changeset/long-weeks-brush.md deleted file mode 100644 index b0e184caa0..0000000000 --- a/.changeset/long-weeks-brush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: account for parent scale when animating elements diff --git a/.changeset/sixty-paws-compete.md b/.changeset/sixty-paws-compete.md deleted file mode 100644 index 2fa3f9e84f..0000000000 --- a/.changeset/sixty-paws-compete.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: apply `overflow: hidden` style when transitioning elements, where necessary diff --git a/.changeset/yellow-dodos-smell.md b/.changeset/yellow-dodos-smell.md deleted file mode 100644 index ea2aead662..0000000000 --- a/.changeset/yellow-dodos-smell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: properly add owners to function bindings diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 72fa09bf96..6226fa851d 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.17.2 + +### Patch Changes + +- fix: account for parent scale when animating elements ([#14957](https://github.com/sveltejs/svelte/pull/14957)) + +- fix: apply `overflow: hidden` style when transitioning elements, where necessary ([#14930](https://github.com/sveltejs/svelte/pull/14930)) + +- fix: properly add owners to function bindings ([#14962](https://github.com/sveltejs/svelte/pull/14962)) + ## 5.17.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index ce6e4ffc42..3d5e350af0 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.17.1", + "version": "5.17.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index bb772687fb..99bf940854 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.17.1'; +export const VERSION = '5.17.2'; export const PUBLIC_VERSION = '5'; From bdc02008e4f8012b439d4ee1c05be0cdbf4dad80 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Thu, 9 Jan 2025 16:26:27 +0000 Subject: [PATCH 0077/1429] fix: reset dependency read versions after reaction execution (#14964) * fix: reset dependency read versions after reaction execution * fix: reset dependency read versions after reaction execution * fix: reset dependency read versions after reaction execution * fix: reset dependency read versions after reaction execution * fix: reset dependency read versions after reaction execution * chore: add test * changeset --------- Co-authored-by: paoloricciuti <ricciutipaolo@gmail.com> Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/fifty-chefs-invent.md | 5 +++++ .../svelte/src/internal/client/runtime.js | 8 ++++++++ .../Component.svelte | 7 +++++++ .../read-version-previous-reaction/_config.js | 19 +++++++++++++++++++ .../main.svelte | 18 ++++++++++++++++++ 5 files changed, 57 insertions(+) create mode 100644 .changeset/fifty-chefs-invent.md create mode 100644 packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/Component.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/main.svelte diff --git a/.changeset/fifty-chefs-invent.md b/.changeset/fifty-chefs-invent.md new file mode 100644 index 0000000000..eb151f67e2 --- /dev/null +++ b/.changeset/fifty-chefs-invent.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: reset dependency read versions after reaction execution diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 202608ce06..3c8879eb31 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -434,6 +434,14 @@ export function update_reaction(reaction) { deps.length = skipped_deps; } + // If we are returning to an previous reaction then + // we need to increment the read version to ensure that + // any dependencies in this reaction aren't marked with + // the same version + if (previous_reaction !== null) { + read_version++; + } + return result; } finally { new_deps = previous_deps; diff --git a/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/Component.svelte b/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/Component.svelte new file mode 100644 index 0000000000..afb62ced2f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/Component.svelte @@ -0,0 +1,7 @@ +<script> + const { label = 0, size = 0 } = $props(); + + const title = $derived(size.toString()); +</script> + +<p {title}>{label}</p> diff --git a/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/_config.js b/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/_config.js new file mode 100644 index 0000000000..650e48e84c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/_config.js @@ -0,0 +1,19 @@ +import { ok, test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + html: `<button></button><p title="0">0</p>`, + + async test({ assert, target }) { + const p = target.querySelector('p'); + const btn = target.querySelector('button'); + flushSync(() => { + btn?.click(); + }); + assert.equal(p?.innerHTML, '1'); + flushSync(() => { + btn?.click(); + }); + assert.equal(p?.innerHTML, '2'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/main.svelte b/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/main.svelte new file mode 100644 index 0000000000..773aabeea3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/read-version-previous-reaction/main.svelte @@ -0,0 +1,18 @@ +<script> + import Component from "./Component.svelte"; + + let props = $state({ + label: 0, + size: 0, + }); + + let filteredProps = $state(); + + $effect.pre(() => { + filteredProps = $state.snapshot(props); + }); +</script> + +<button onclick={()=>props.label++}></button> + +<Component {...filteredProps} /> \ No newline at end of file From dc8ae825b8c3fb8c286d54116af8d11244343974 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 11:36:14 -0500 Subject: [PATCH 0078/1429] Version Packages (#14965) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/fifty-chefs-invent.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/fifty-chefs-invent.md diff --git a/.changeset/fifty-chefs-invent.md b/.changeset/fifty-chefs-invent.md deleted file mode 100644 index eb151f67e2..0000000000 --- a/.changeset/fifty-chefs-invent.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: reset dependency read versions after reaction execution diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 6226fa851d..6023c6d4e1 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.17.3 + +### Patch Changes + +- fix: reset dependency read versions after reaction execution ([#14964](https://github.com/sveltejs/svelte/pull/14964)) + ## 5.17.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 3d5e350af0..459786330b 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.17.2", + "version": "5.17.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 99bf940854..0fd7d2f039 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.17.2'; +export const VERSION = '5.17.3'; export const PUBLIC_VERSION = '5'; From dbe5818560241d5a1b221b6e160298da48aebbb9 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Fri, 10 Jan 2025 01:14:52 +0000 Subject: [PATCH 0079/1429] fix: wrap each block expression in derived to encapsulte effects (#14967) * fix: wrap each block expression in derived to encapsulte effects * add test * Update .changeset/tender-apples-scream.md --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/tender-apples-scream.md | 5 ++ .../src/internal/client/dom/blocks/each.js | 19 ++++---- .../samples/each-updates-9/_config.js | 16 +++++++ .../samples/each-updates-9/main.svelte | 46 +++++++++++++++++++ 4 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 .changeset/tender-apples-scream.md create mode 100644 packages/svelte/tests/runtime-runes/samples/each-updates-9/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/each-updates-9/main.svelte diff --git a/.changeset/tender-apples-scream.md b/.changeset/tender-apples-scream.md new file mode 100644 index 0000000000..836bdaffdf --- /dev/null +++ b/.changeset/tender-apples-scream.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: wrap each block expression in derived to encapsulate effects diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 9e64055940..b17090948a 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -35,8 +35,9 @@ import { source, mutable_source, internal_set } from '../../reactivity/sources.j import { array_from, is_array } from '../../../shared/utils.js'; import { INERT } from '../../constants.js'; import { queue_micro_task } from '../task.js'; -import { active_effect, active_reaction } from '../../runtime.js'; +import { active_effect, active_reaction, get } from '../../runtime.js'; import { DEV } from 'esm-env'; +import { derived_safe_equal } from '../../reactivity/deriveds.js'; /** * The row of a keyed each block that is currently updating. We track this @@ -135,15 +136,17 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f var was_empty = false; - block(() => { + // TODO: ideally we could use derived for runes mode but because of the ability + // to use a store which can be mutated, we can't do that here as mutating a store + // will still result in the collection array being the same from the store + var each_array = derived_safe_equal(() => { var collection = get_collection(); - var array = is_array(collection) - ? collection - : collection == null - ? [] - : array_from(collection); + return is_array(collection) ? collection : collection == null ? [] : array_from(collection); + }); + block(() => { + var array = get(each_array); var length = array.length; if (was_empty && length === 0) { @@ -254,7 +257,7 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f // that a mutation occurred and it's made the collection MAYBE_DIRTY, so reading the // collection again can provide consistency to the reactive graph again as the deriveds // will now be `CLEAN`. - get_collection(); + get(each_array); }); if (hydrating) { diff --git a/packages/svelte/tests/runtime-runes/samples/each-updates-9/_config.js b/packages/svelte/tests/runtime-runes/samples/each-updates-9/_config.js new file mode 100644 index 0000000000..ee35058c59 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/each-updates-9/_config.js @@ -0,0 +1,16 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + const [btn1] = target.querySelectorAll('button'); + + btn1.click(); + flushSync(); + + await Promise.resolve(); + await Promise.resolve(); + + assert.deepEqual(logs, ['cleanup']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/each-updates-9/main.svelte b/packages/svelte/tests/runtime-runes/samples/each-updates-9/main.svelte new file mode 100644 index 0000000000..f5b2c8eb12 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/each-updates-9/main.svelte @@ -0,0 +1,46 @@ +<script> + import { createSubscriber } from 'svelte/reactivity'; + + class MyStore { + #subscribe; + #data = $state([ + ['a', [1, 2]], + ['b', [3, 4]] + ]); + #id; + + constructor(options) { + options?.someBoolean; + this.#id = options?.id; + this.#subscribe = createSubscriber(() => { + debugger + return () => { + console.log('cleanup'); + }; + }); + } + + get data() { + this.#subscribe(); + return this.#data; + } + set data(v) { + this.#data = v; + } + } + + let storeOptions = $state({ + someBoolean: false, + id: 0 + }); + + let myStore = $derived(new MyStore(storeOptions)); +</script> + +<button + onclick={() => { + storeOptions.someBoolean = !storeOptions.someBoolean; + }}>+</button +> + +{#each myStore.data as _}{/each} From 41fb51349e5846bdb406baba555d02cbf4f55b8e Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Fri, 10 Jan 2025 11:28:18 +0100 Subject: [PATCH 0080/1429] fix: store access on component destroy (#14968) Co-authored-by: Oscar Dominguez <dominguez.celada@gmail.com> --- .changeset/hot-kings-shout.md | 5 +++ .../3-transform/client/transform-client.js | 31 +++++++++++++--- .../src/internal/client/reactivity/store.js | 37 ++++++++++++++----- .../store-update-on-destroy/Test.svelte | 12 ++++++ .../store-update-on-destroy/_config.js | 12 ++++++ .../store-update-on-destroy/main.svelte | 15 ++++++++ 6 files changed, 96 insertions(+), 16 deletions(-) create mode 100644 .changeset/hot-kings-shout.md create mode 100644 packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/Test.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/main.svelte diff --git a/.changeset/hot-kings-shout.md b/.changeset/hot-kings-shout.md new file mode 100644 index 0000000000..afba164abf --- /dev/null +++ b/.changeset/hot-kings-shout.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: store access on component destroy diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index a969117ed3..582c32b534 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -214,6 +214,8 @@ export function client_component(analysis, options) { /** @type {ESTree.VariableDeclaration[]} */ const legacy_reactive_declarations = []; + let needs_store_cleanup = false; + for (const [name, binding] of analysis.instance.scope.declarations) { if (binding.kind === 'legacy_reactive') { legacy_reactive_declarations.push( @@ -222,7 +224,10 @@ export function client_component(analysis, options) { } if (binding.kind === 'store_sub') { if (store_setup.length === 0) { - store_setup.push(b.const('$$stores', b.call('$.setup_stores'))); + needs_store_cleanup = true; + store_setup.push( + b.const(b.array_pattern([b.id('$$stores'), b.id('$$cleanup')]), b.call('$.setup_stores')) + ); } // We're creating an arrow function that gets the store value which minifies better for two or more references @@ -391,14 +396,28 @@ export function client_component(analysis, options) { analysis.reactive_statements.size > 0 || component_returned_object.length > 0; + // we want the cleanup function for the stores to run as the very last thing + // so that it can effectively clean up the store subscription even after the user effects runs if (should_inject_context) { component_block.body.unshift(b.stmt(b.call('$.push', ...push_args))); - component_block.body.push( - component_returned_object.length > 0 - ? b.return(b.call('$.pop', b.object(component_returned_object))) - : b.stmt(b.call('$.pop')) - ); + let to_push; + + if (component_returned_object.length > 0) { + let pop_call = b.call('$.pop', b.object(component_returned_object)); + to_push = needs_store_cleanup ? b.var('$$pop', pop_call) : b.return(pop_call); + } else { + to_push = b.stmt(b.call('$.pop')); + } + + component_block.body.push(to_push); + } + + if (needs_store_cleanup) { + component_block.body.push(b.stmt(b.call('$$cleanup'))); + if (component_returned_object.length > 0) { + component_block.body.push(b.return(b.id('$$pop'))); + } } if (analysis.uses_rest_props) { diff --git a/packages/svelte/src/internal/client/reactivity/store.js b/packages/svelte/src/internal/client/reactivity/store.js index 11eee23e0a..e7a92ee052 100644 --- a/packages/svelte/src/internal/client/reactivity/store.js +++ b/packages/svelte/src/internal/client/reactivity/store.js @@ -1,7 +1,8 @@ /** @import { StoreReferencesContainer } from '#client' */ /** @import { Store } from '#shared' */ import { subscribe_to_store } from '../../../store/utils.js'; -import { noop } from '../../shared/utils.js'; +import { get as get_store } from '../../../store/shared/index.js'; +import { define_property, noop } from '../../shared/utils.js'; import { get } from '../runtime.js'; import { teardown } from './effects.js'; import { mutable_source, set } from './sources.js'; @@ -13,6 +14,8 @@ import { mutable_source, set } from './sources.js'; */ let is_store_binding = false; +let IS_UNMOUNTED = Symbol(); + /** * Gets the current value of a store. If the store isn't subscribed to yet, it will create a proxy * signal that will be updated when the store is. The store references container is needed to @@ -30,7 +33,8 @@ export function store_get(store, store_name, stores) { unsubscribe: noop }); - if (entry.store !== store) { + // if the component that setup this is already unmounted we don't want to register a subscription + if (entry.store !== store && !(IS_UNMOUNTED in stores)) { entry.unsubscribe(); entry.store = store ?? null; @@ -54,6 +58,13 @@ export function store_get(store, store_name, stores) { } } + // if the component that setup this stores is already unmounted the source will be out of sync + // so we just use the `get` for the stores, less performant but it avoids to create a memory leak + // and it will keep the value consistent + if (store && IS_UNMOUNTED in stores) { + return get_store(store); + } + return get(entry.source); } @@ -103,20 +114,26 @@ export function invalidate_store(stores, store_name) { /** * Unsubscribes from all auto-subscribed stores on destroy - * @returns {StoreReferencesContainer} + * @returns {[StoreReferencesContainer, ()=>void]} */ export function setup_stores() { /** @type {StoreReferencesContainer} */ const stores = {}; - teardown(() => { - for (var store_name in stores) { - const ref = stores[store_name]; - ref.unsubscribe(); - } - }); + function cleanup() { + teardown(() => { + for (var store_name in stores) { + const ref = stores[store_name]; + ref.unsubscribe(); + } + define_property(stores, IS_UNMOUNTED, { + enumerable: false, + value: true + }); + }); + } - return stores; + return [stores, cleanup]; } /** diff --git a/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/Test.svelte b/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/Test.svelte new file mode 100644 index 0000000000..364a4a7aca --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/Test.svelte @@ -0,0 +1,12 @@ +<script lang="ts"> + const { store } = $props(); + + $effect(() => { + $store; + return () => { + console.log($store); + $store++; + console.log($store); + }; + }); +</script> diff --git a/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/_config.js b/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/_config.js new file mode 100644 index 0000000000..bb99988756 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/_config.js @@ -0,0 +1,12 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + const input = target.querySelector('input'); + flushSync(() => { + input?.click(); + }); + assert.deepEqual(logs, [0, 1]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/main.svelte b/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/main.svelte new file mode 100644 index 0000000000..7ba59b5afc --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/store-update-on-destroy/main.svelte @@ -0,0 +1,15 @@ +<script lang="ts"> + import { writable } from 'svelte/store'; + + import Test from './Test.svelte'; + + let store = writable(0); + + let checked = $state(true); +</script> + +<input type="checkbox" bind:checked /> + +{#if checked} + <Test {store} /> +{/if} From 9b6e65fbeb61297978a16e8f711ebe2dc97f2b10 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Fri, 10 Jan 2025 14:36:26 +0100 Subject: [PATCH 0081/1429] fix: correctly transform `pre` with no content (#14973) Closes #14971 --- .changeset/spicy-insects-check.md | 5 +++++ packages/svelte/src/compiler/phases/3-transform/utils.js | 2 +- .../tests/runtime-runes/samples/pre-no-content/_config.js | 5 +++++ .../tests/runtime-runes/samples/pre-no-content/main.svelte | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .changeset/spicy-insects-check.md create mode 100644 packages/svelte/tests/runtime-runes/samples/pre-no-content/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/pre-no-content/main.svelte diff --git a/.changeset/spicy-insects-check.md b/.changeset/spicy-insects-check.md new file mode 100644 index 0000000000..b998d36400 --- /dev/null +++ b/.changeset/spicy-insects-check.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly transform `pre` with no content diff --git a/packages/svelte/src/compiler/phases/3-transform/utils.js b/packages/svelte/src/compiler/phases/3-transform/utils.js index 62a635de35..46872fbfcf 100644 --- a/packages/svelte/src/compiler/phases/3-transform/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/utils.js @@ -272,7 +272,7 @@ export function clean_nodes( var first = trimmed[0]; // initial newline inside a `<pre>` is disregarded, if not followed by another newline - if (parent.type === 'RegularElement' && parent.name === 'pre' && first.type === 'Text') { + if (parent.type === 'RegularElement' && parent.name === 'pre' && first?.type === 'Text') { const text = first.data.replace(regex_starts_with_newline, ''); if (text !== first.data) { const tmp = text.replace(regex_starts_with_newline, ''); diff --git a/packages/svelte/tests/runtime-runes/samples/pre-no-content/_config.js b/packages/svelte/tests/runtime-runes/samples/pre-no-content/_config.js new file mode 100644 index 0000000000..cb9d31a69f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/pre-no-content/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: `<pre></pre>` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/pre-no-content/main.svelte b/packages/svelte/tests/runtime-runes/samples/pre-no-content/main.svelte new file mode 100644 index 0000000000..a4357066a5 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/pre-no-content/main.svelte @@ -0,0 +1 @@ +<pre></pre> \ No newline at end of file From 76ce303b0aa08e76cefc5d13f1fe21345caa0ce9 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 10 Jan 2025 12:05:23 -0500 Subject: [PATCH 0082/1429] chore: bump esrap (#14969) * chore: bump esrap * fix * bump again --- packages/svelte/package.json | 2 +- packages/svelte/src/compiler/errors.js | 330 +++++++++--------- packages/svelte/src/compiler/warnings.js | 312 ++++++++--------- .../svelte/src/internal/client/warnings.js | 6 +- .../svelte/src/internal/shared/warnings.js | 2 +- .../_expected/client/index.svelte.js | 10 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/client/index.svelte.js | 8 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/client/index.svelte.js | 6 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/client/index.svelte.js | 4 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/client/index.svelte.js | 2 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/client/main.svelte.js | 16 +- .../_expected/server/main.svelte.js | 4 +- .../_expected/client/index.svelte.js | 8 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/client/index.svelte.js | 2 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/client/index.svelte.js | 6 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/client/index.svelte.js | 6 +- .../_expected/server/index.svelte.js | 2 +- .../hmr/_expected/client/index.svelte.js | 6 +- .../hmr/_expected/server/index.svelte.js | 2 +- .../_expected/client/index.svelte.js | 6 +- .../_expected/client/module.svelte.js | 2 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/server/module.svelte.js | 2 +- .../_expected/client/index.svelte.js | 6 +- .../_expected/server/index.svelte.js | 2 +- .../purity/_expected/client/index.svelte.js | 6 +- .../purity/_expected/server/index.svelte.js | 2 +- .../_expected/client/index.svelte.js | 8 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/client/index.svelte.js | 6 +- .../_expected/server/index.svelte.js | 4 +- .../_expected/client/index.svelte.js | 6 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/client/index.svelte.js | 8 +- .../_expected/server/index.svelte.js | 2 +- .../samples/css-injected-map/_config.js | 2 +- pnpm-lock.yaml | 10 +- 45 files changed, 414 insertions(+), 414 deletions(-) diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 459786330b..47aec9ee93 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -155,7 +155,7 @@ "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", - "esrap": "^1.3.2", + "esrap": "^1.4.2", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index a997eeef8d..da038af430 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -52,7 +52,7 @@ function e(node, code, message) { * @returns {never} */ export function options_invalid_value(node, details) { - e(node, "options_invalid_value", `Invalid compiler option: ${details}\nhttps://svelte.dev/e/options_invalid_value`); + e(node, 'options_invalid_value', `Invalid compiler option: ${details}\nhttps://svelte.dev/e/options_invalid_value`); } /** @@ -62,7 +62,7 @@ export function options_invalid_value(node, details) { * @returns {never} */ export function options_removed(node, details) { - e(node, "options_removed", `Invalid compiler option: ${details}\nhttps://svelte.dev/e/options_removed`); + e(node, 'options_removed', `Invalid compiler option: ${details}\nhttps://svelte.dev/e/options_removed`); } /** @@ -72,7 +72,7 @@ export function options_removed(node, details) { * @returns {never} */ export function options_unrecognised(node, keypath) { - e(node, "options_unrecognised", `Unrecognised compiler option ${keypath}\nhttps://svelte.dev/e/options_unrecognised`); + e(node, 'options_unrecognised', `Unrecognised compiler option ${keypath}\nhttps://svelte.dev/e/options_unrecognised`); } /** @@ -81,7 +81,7 @@ export function options_unrecognised(node, keypath) { * @returns {never} */ export function bindable_invalid_location(node) { - e(node, "bindable_invalid_location", `\`$bindable()\` can only be used inside a \`$props()\` declaration\nhttps://svelte.dev/e/bindable_invalid_location`); + e(node, 'bindable_invalid_location', `\`$bindable()\` can only be used inside a \`$props()\` declaration\nhttps://svelte.dev/e/bindable_invalid_location`); } /** @@ -91,7 +91,7 @@ export function bindable_invalid_location(node) { * @returns {never} */ export function constant_assignment(node, thing) { - e(node, "constant_assignment", `Cannot assign to ${thing}\nhttps://svelte.dev/e/constant_assignment`); + e(node, 'constant_assignment', `Cannot assign to ${thing}\nhttps://svelte.dev/e/constant_assignment`); } /** @@ -101,7 +101,7 @@ export function constant_assignment(node, thing) { * @returns {never} */ export function constant_binding(node, thing) { - e(node, "constant_binding", `Cannot bind to ${thing}\nhttps://svelte.dev/e/constant_binding`); + e(node, 'constant_binding', `Cannot bind to ${thing}\nhttps://svelte.dev/e/constant_binding`); } /** @@ -111,7 +111,7 @@ export function constant_binding(node, thing) { * @returns {never} */ export function declaration_duplicate(node, name) { - e(node, "declaration_duplicate", `\`${name}\` has already been declared\nhttps://svelte.dev/e/declaration_duplicate`); + e(node, 'declaration_duplicate', `\`${name}\` has already been declared\nhttps://svelte.dev/e/declaration_duplicate`); } /** @@ -120,7 +120,7 @@ export function declaration_duplicate(node, name) { * @returns {never} */ export function declaration_duplicate_module_import(node) { - e(node, "declaration_duplicate_module_import", `Cannot declare a variable with the same name as an import inside \`<script module>\`\nhttps://svelte.dev/e/declaration_duplicate_module_import`); + e(node, 'declaration_duplicate_module_import', `Cannot declare a variable with the same name as an import inside \`<script module>\`\nhttps://svelte.dev/e/declaration_duplicate_module_import`); } /** @@ -129,7 +129,7 @@ export function declaration_duplicate_module_import(node) { * @returns {never} */ export function derived_invalid_export(node) { - e(node, "derived_invalid_export", `Cannot export derived state from a module. To expose the current derived value, export a function returning its value\nhttps://svelte.dev/e/derived_invalid_export`); + e(node, 'derived_invalid_export', `Cannot export derived state from a module. To expose the current derived value, export a function returning its value\nhttps://svelte.dev/e/derived_invalid_export`); } /** @@ -138,7 +138,7 @@ export function derived_invalid_export(node) { * @returns {never} */ export function dollar_binding_invalid(node) { - e(node, "dollar_binding_invalid", `The $ name is reserved, and cannot be used for variables and imports\nhttps://svelte.dev/e/dollar_binding_invalid`); + e(node, 'dollar_binding_invalid', `The $ name is reserved, and cannot be used for variables and imports\nhttps://svelte.dev/e/dollar_binding_invalid`); } /** @@ -147,7 +147,7 @@ export function dollar_binding_invalid(node) { * @returns {never} */ export function dollar_prefix_invalid(node) { - e(node, "dollar_prefix_invalid", `The $ prefix is reserved, and cannot be used for variables and imports\nhttps://svelte.dev/e/dollar_prefix_invalid`); + e(node, 'dollar_prefix_invalid', `The $ prefix is reserved, and cannot be used for variables and imports\nhttps://svelte.dev/e/dollar_prefix_invalid`); } /** @@ -156,7 +156,7 @@ export function dollar_prefix_invalid(node) { * @returns {never} */ export function each_item_invalid_assignment(node) { - e(node, "each_item_invalid_assignment", `Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. \`array[i] = value\` instead of \`entry = value\`, or \`bind:value={array[i]}\` instead of \`bind:value={entry}\`)\nhttps://svelte.dev/e/each_item_invalid_assignment`); + e(node, 'each_item_invalid_assignment', `Cannot reassign or bind to each block argument in runes mode. Use the array and index variables instead (e.g. \`array[i] = value\` instead of \`entry = value\`, or \`bind:value={array[i]}\` instead of \`bind:value={entry}\`)\nhttps://svelte.dev/e/each_item_invalid_assignment`); } /** @@ -165,7 +165,7 @@ export function each_item_invalid_assignment(node) { * @returns {never} */ export function effect_invalid_placement(node) { - e(node, "effect_invalid_placement", `\`$effect()\` can only be used as an expression statement\nhttps://svelte.dev/e/effect_invalid_placement`); + e(node, 'effect_invalid_placement', `\`$effect()\` can only be used as an expression statement\nhttps://svelte.dev/e/effect_invalid_placement`); } /** @@ -175,7 +175,7 @@ export function effect_invalid_placement(node) { * @returns {never} */ export function export_undefined(node, name) { - e(node, "export_undefined", `\`${name}\` is not defined\nhttps://svelte.dev/e/export_undefined`); + e(node, 'export_undefined', `\`${name}\` is not defined\nhttps://svelte.dev/e/export_undefined`); } /** @@ -185,7 +185,7 @@ export function export_undefined(node, name) { * @returns {never} */ export function global_reference_invalid(node, name) { - e(node, "global_reference_invalid", `\`${name}\` is an illegal variable name. To reference a global variable called \`${name}\`, use \`globalThis.${name}\`\nhttps://svelte.dev/e/global_reference_invalid`); + e(node, 'global_reference_invalid', `\`${name}\` is an illegal variable name. To reference a global variable called \`${name}\`, use \`globalThis.${name}\`\nhttps://svelte.dev/e/global_reference_invalid`); } /** @@ -194,7 +194,7 @@ export function global_reference_invalid(node, name) { * @returns {never} */ export function host_invalid_placement(node) { - e(node, "host_invalid_placement", `\`$host()\` can only be used inside custom element component instances\nhttps://svelte.dev/e/host_invalid_placement`); + e(node, 'host_invalid_placement', `\`$host()\` can only be used inside custom element component instances\nhttps://svelte.dev/e/host_invalid_placement`); } /** @@ -203,7 +203,7 @@ export function host_invalid_placement(node) { * @returns {never} */ export function import_svelte_internal_forbidden(node) { - e(node, "import_svelte_internal_forbidden", `Imports of \`svelte/internal/*\` are forbidden. It contains private runtime code which is subject to change without notice. If you're importing from \`svelte/internal/*\` to work around a limitation of Svelte, please open an issue at https://github.com/sveltejs/svelte and explain your use case\nhttps://svelte.dev/e/import_svelte_internal_forbidden`); + e(node, 'import_svelte_internal_forbidden', `Imports of \`svelte/internal/*\` are forbidden. It contains private runtime code which is subject to change without notice. If you're importing from \`svelte/internal/*\` to work around a limitation of Svelte, please open an issue at https://github.com/sveltejs/svelte and explain your use case\nhttps://svelte.dev/e/import_svelte_internal_forbidden`); } /** @@ -212,7 +212,7 @@ export function import_svelte_internal_forbidden(node) { * @returns {never} */ export function inspect_trace_generator(node) { - e(node, "inspect_trace_generator", `\`$inspect.trace(...)\` cannot be used inside a generator function\nhttps://svelte.dev/e/inspect_trace_generator`); + e(node, 'inspect_trace_generator', `\`$inspect.trace(...)\` cannot be used inside a generator function\nhttps://svelte.dev/e/inspect_trace_generator`); } /** @@ -221,7 +221,7 @@ export function inspect_trace_generator(node) { * @returns {never} */ export function inspect_trace_invalid_placement(node) { - e(node, "inspect_trace_invalid_placement", `\`$inspect.trace(...)\` must be the first statement of a function body\nhttps://svelte.dev/e/inspect_trace_invalid_placement`); + e(node, 'inspect_trace_invalid_placement', `\`$inspect.trace(...)\` must be the first statement of a function body\nhttps://svelte.dev/e/inspect_trace_invalid_placement`); } /** @@ -230,7 +230,7 @@ export function inspect_trace_invalid_placement(node) { * @returns {never} */ export function invalid_arguments_usage(node) { - e(node, "invalid_arguments_usage", `The arguments keyword cannot be used within the template or at the top level of a component\nhttps://svelte.dev/e/invalid_arguments_usage`); + e(node, 'invalid_arguments_usage', `The arguments keyword cannot be used within the template or at the top level of a component\nhttps://svelte.dev/e/invalid_arguments_usage`); } /** @@ -239,7 +239,7 @@ export function invalid_arguments_usage(node) { * @returns {never} */ export function legacy_export_invalid(node) { - e(node, "legacy_export_invalid", `Cannot use \`export let\` in runes mode — use \`$props()\` instead\nhttps://svelte.dev/e/legacy_export_invalid`); + e(node, 'legacy_export_invalid', `Cannot use \`export let\` in runes mode — use \`$props()\` instead\nhttps://svelte.dev/e/legacy_export_invalid`); } /** @@ -248,7 +248,7 @@ export function legacy_export_invalid(node) { * @returns {never} */ export function legacy_props_invalid(node) { - e(node, "legacy_props_invalid", `Cannot use \`$$props\` in runes mode\nhttps://svelte.dev/e/legacy_props_invalid`); + e(node, 'legacy_props_invalid', `Cannot use \`$$props\` in runes mode\nhttps://svelte.dev/e/legacy_props_invalid`); } /** @@ -257,7 +257,7 @@ export function legacy_props_invalid(node) { * @returns {never} */ export function legacy_reactive_statement_invalid(node) { - e(node, "legacy_reactive_statement_invalid", `\`$:\` is not allowed in runes mode, use \`$derived\` or \`$effect\` instead\nhttps://svelte.dev/e/legacy_reactive_statement_invalid`); + e(node, 'legacy_reactive_statement_invalid', `\`$:\` is not allowed in runes mode, use \`$derived\` or \`$effect\` instead\nhttps://svelte.dev/e/legacy_reactive_statement_invalid`); } /** @@ -266,7 +266,7 @@ export function legacy_reactive_statement_invalid(node) { * @returns {never} */ export function legacy_rest_props_invalid(node) { - e(node, "legacy_rest_props_invalid", `Cannot use \`$$restProps\` in runes mode\nhttps://svelte.dev/e/legacy_rest_props_invalid`); + e(node, 'legacy_rest_props_invalid', `Cannot use \`$$restProps\` in runes mode\nhttps://svelte.dev/e/legacy_rest_props_invalid`); } /** @@ -275,7 +275,7 @@ export function legacy_rest_props_invalid(node) { * @returns {never} */ export function module_illegal_default_export(node) { - e(node, "module_illegal_default_export", `A component cannot have a default export\nhttps://svelte.dev/e/module_illegal_default_export`); + e(node, 'module_illegal_default_export', `A component cannot have a default export\nhttps://svelte.dev/e/module_illegal_default_export`); } /** @@ -284,7 +284,7 @@ export function module_illegal_default_export(node) { * @returns {never} */ export function props_duplicate(node) { - e(node, "props_duplicate", `Cannot use \`$props()\` more than once\nhttps://svelte.dev/e/props_duplicate`); + e(node, 'props_duplicate', `Cannot use \`$props()\` more than once\nhttps://svelte.dev/e/props_duplicate`); } /** @@ -293,7 +293,7 @@ export function props_duplicate(node) { * @returns {never} */ export function props_illegal_name(node) { - e(node, "props_illegal_name", `Declaring or accessing a prop starting with \`$$\` is illegal (they are reserved for Svelte internals)\nhttps://svelte.dev/e/props_illegal_name`); + e(node, 'props_illegal_name', `Declaring or accessing a prop starting with \`$$\` is illegal (they are reserved for Svelte internals)\nhttps://svelte.dev/e/props_illegal_name`); } /** @@ -302,7 +302,7 @@ export function props_illegal_name(node) { * @returns {never} */ export function props_invalid_identifier(node) { - e(node, "props_invalid_identifier", `\`$props()\` can only be used with an object destructuring pattern\nhttps://svelte.dev/e/props_invalid_identifier`); + e(node, 'props_invalid_identifier', `\`$props()\` can only be used with an object destructuring pattern\nhttps://svelte.dev/e/props_invalid_identifier`); } /** @@ -311,7 +311,7 @@ export function props_invalid_identifier(node) { * @returns {never} */ export function props_invalid_pattern(node) { - e(node, "props_invalid_pattern", `\`$props()\` assignment must not contain nested properties or computed keys\nhttps://svelte.dev/e/props_invalid_pattern`); + e(node, 'props_invalid_pattern', `\`$props()\` assignment must not contain nested properties or computed keys\nhttps://svelte.dev/e/props_invalid_pattern`); } /** @@ -320,7 +320,7 @@ export function props_invalid_pattern(node) { * @returns {never} */ export function props_invalid_placement(node) { - e(node, "props_invalid_placement", `\`$props()\` can only be used at the top level of components as a variable declaration initializer\nhttps://svelte.dev/e/props_invalid_placement`); + e(node, 'props_invalid_placement', `\`$props()\` can only be used at the top level of components as a variable declaration initializer\nhttps://svelte.dev/e/props_invalid_placement`); } /** @@ -330,7 +330,7 @@ export function props_invalid_placement(node) { * @returns {never} */ export function reactive_declaration_cycle(node, cycle) { - e(node, "reactive_declaration_cycle", `Cyclical dependency detected: ${cycle}\nhttps://svelte.dev/e/reactive_declaration_cycle`); + e(node, 'reactive_declaration_cycle', `Cyclical dependency detected: ${cycle}\nhttps://svelte.dev/e/reactive_declaration_cycle`); } /** @@ -340,7 +340,7 @@ export function reactive_declaration_cycle(node, cycle) { * @returns {never} */ export function rune_invalid_arguments(node, rune) { - e(node, "rune_invalid_arguments", `\`${rune}\` cannot be called with arguments\nhttps://svelte.dev/e/rune_invalid_arguments`); + e(node, 'rune_invalid_arguments', `\`${rune}\` cannot be called with arguments\nhttps://svelte.dev/e/rune_invalid_arguments`); } /** @@ -351,7 +351,7 @@ export function rune_invalid_arguments(node, rune) { * @returns {never} */ export function rune_invalid_arguments_length(node, rune, args) { - e(node, "rune_invalid_arguments_length", `\`${rune}\` must be called with ${args}\nhttps://svelte.dev/e/rune_invalid_arguments_length`); + e(node, 'rune_invalid_arguments_length', `\`${rune}\` must be called with ${args}\nhttps://svelte.dev/e/rune_invalid_arguments_length`); } /** @@ -360,7 +360,7 @@ export function rune_invalid_arguments_length(node, rune, args) { * @returns {never} */ export function rune_invalid_computed_property(node) { - e(node, "rune_invalid_computed_property", `Cannot access a computed property of a rune\nhttps://svelte.dev/e/rune_invalid_computed_property`); + e(node, 'rune_invalid_computed_property', `Cannot access a computed property of a rune\nhttps://svelte.dev/e/rune_invalid_computed_property`); } /** @@ -370,7 +370,7 @@ export function rune_invalid_computed_property(node) { * @returns {never} */ export function rune_invalid_name(node, name) { - e(node, "rune_invalid_name", `\`${name}\` is not a valid rune\nhttps://svelte.dev/e/rune_invalid_name`); + e(node, 'rune_invalid_name', `\`${name}\` is not a valid rune\nhttps://svelte.dev/e/rune_invalid_name`); } /** @@ -380,7 +380,7 @@ export function rune_invalid_name(node, name) { * @returns {never} */ export function rune_invalid_usage(node, rune) { - e(node, "rune_invalid_usage", `Cannot use \`${rune}\` rune in non-runes mode\nhttps://svelte.dev/e/rune_invalid_usage`); + e(node, 'rune_invalid_usage', `Cannot use \`${rune}\` rune in non-runes mode\nhttps://svelte.dev/e/rune_invalid_usage`); } /** @@ -389,7 +389,7 @@ export function rune_invalid_usage(node, rune) { * @returns {never} */ export function rune_missing_parentheses(node) { - e(node, "rune_missing_parentheses", `Cannot use rune without parentheses\nhttps://svelte.dev/e/rune_missing_parentheses`); + e(node, 'rune_missing_parentheses', `Cannot use rune without parentheses\nhttps://svelte.dev/e/rune_missing_parentheses`); } /** @@ -399,7 +399,7 @@ export function rune_missing_parentheses(node) { * @returns {never} */ export function rune_removed(node, name) { - e(node, "rune_removed", `The \`${name}\` rune has been removed\nhttps://svelte.dev/e/rune_removed`); + e(node, 'rune_removed', `The \`${name}\` rune has been removed\nhttps://svelte.dev/e/rune_removed`); } /** @@ -410,7 +410,7 @@ export function rune_removed(node, name) { * @returns {never} */ export function rune_renamed(node, name, replacement) { - e(node, "rune_renamed", `\`${name}\` is now \`${replacement}\`\nhttps://svelte.dev/e/rune_renamed`); + e(node, 'rune_renamed', `\`${name}\` is now \`${replacement}\`\nhttps://svelte.dev/e/rune_renamed`); } /** @@ -420,7 +420,7 @@ export function rune_renamed(node, name, replacement) { * @returns {never} */ export function runes_mode_invalid_import(node, name) { - e(node, "runes_mode_invalid_import", `${name} cannot be used in runes mode\nhttps://svelte.dev/e/runes_mode_invalid_import`); + e(node, 'runes_mode_invalid_import', `${name} cannot be used in runes mode\nhttps://svelte.dev/e/runes_mode_invalid_import`); } /** @@ -429,7 +429,7 @@ export function runes_mode_invalid_import(node, name) { * @returns {never} */ export function snippet_invalid_export(node) { - e(node, "snippet_invalid_export", `An exported snippet can only reference things declared in a \`<script module>\`, or other exportable snippets\nhttps://svelte.dev/e/snippet_invalid_export`); + e(node, 'snippet_invalid_export', `An exported snippet can only reference things declared in a \`<script module>\`, or other exportable snippets\nhttps://svelte.dev/e/snippet_invalid_export`); } /** @@ -438,7 +438,7 @@ export function snippet_invalid_export(node) { * @returns {never} */ export function snippet_parameter_assignment(node) { - e(node, "snippet_parameter_assignment", `Cannot reassign or bind to snippet parameter\nhttps://svelte.dev/e/snippet_parameter_assignment`); + e(node, 'snippet_parameter_assignment', `Cannot reassign or bind to snippet parameter\nhttps://svelte.dev/e/snippet_parameter_assignment`); } /** @@ -447,7 +447,7 @@ export function snippet_parameter_assignment(node) { * @returns {never} */ export function state_invalid_export(node) { - e(node, "state_invalid_export", `Cannot export state from a module if it is reassigned. Either export a function returning the state value or only mutate the state value's properties\nhttps://svelte.dev/e/state_invalid_export`); + e(node, 'state_invalid_export', `Cannot export state from a module if it is reassigned. Either export a function returning the state value or only mutate the state value's properties\nhttps://svelte.dev/e/state_invalid_export`); } /** @@ -457,7 +457,7 @@ export function state_invalid_export(node) { * @returns {never} */ export function state_invalid_placement(node, rune) { - e(node, "state_invalid_placement", `\`${rune}(...)\` can only be used as a variable declaration initializer or a class field\nhttps://svelte.dev/e/state_invalid_placement`); + e(node, 'state_invalid_placement', `\`${rune}(...)\` can only be used as a variable declaration initializer or a class field\nhttps://svelte.dev/e/state_invalid_placement`); } /** @@ -466,7 +466,7 @@ export function state_invalid_placement(node, rune) { * @returns {never} */ export function store_invalid_scoped_subscription(node) { - e(node, "store_invalid_scoped_subscription", `Cannot subscribe to stores that are not declared at the top level of the component\nhttps://svelte.dev/e/store_invalid_scoped_subscription`); + e(node, 'store_invalid_scoped_subscription', `Cannot subscribe to stores that are not declared at the top level of the component\nhttps://svelte.dev/e/store_invalid_scoped_subscription`); } /** @@ -475,7 +475,7 @@ export function store_invalid_scoped_subscription(node) { * @returns {never} */ export function store_invalid_subscription(node) { - e(node, "store_invalid_subscription", `Cannot reference store value inside \`<script module>\`\nhttps://svelte.dev/e/store_invalid_subscription`); + e(node, 'store_invalid_subscription', `Cannot reference store value inside \`<script module>\`\nhttps://svelte.dev/e/store_invalid_subscription`); } /** @@ -484,7 +484,7 @@ export function store_invalid_subscription(node) { * @returns {never} */ export function store_invalid_subscription_module(node) { - e(node, "store_invalid_subscription_module", `Cannot reference store value outside a \`.svelte\` file\nhttps://svelte.dev/e/store_invalid_subscription_module`); + e(node, 'store_invalid_subscription_module', `Cannot reference store value outside a \`.svelte\` file\nhttps://svelte.dev/e/store_invalid_subscription_module`); } /** @@ -494,7 +494,7 @@ export function store_invalid_subscription_module(node) { * @returns {never} */ export function typescript_invalid_feature(node, feature) { - e(node, "typescript_invalid_feature", `TypeScript language features like ${feature} are not natively supported, and their use is generally discouraged. Outside of \`<script>\` tags, these features are not supported. For use within \`<script>\` tags, you will need to use a preprocessor to convert it to JavaScript before it gets passed to the Svelte compiler. If you are using \`vitePreprocess\`, make sure to specifically enable preprocessing script tags (\`vitePreprocess({ script: true })\`)\nhttps://svelte.dev/e/typescript_invalid_feature`); + e(node, 'typescript_invalid_feature', `TypeScript language features like ${feature} are not natively supported, and their use is generally discouraged. Outside of \`<script>\` tags, these features are not supported. For use within \`<script>\` tags, you will need to use a preprocessor to convert it to JavaScript before it gets passed to the Svelte compiler. If you are using \`vitePreprocess\`, make sure to specifically enable preprocessing script tags (\`vitePreprocess({ script: true })\`)\nhttps://svelte.dev/e/typescript_invalid_feature`); } /** @@ -503,7 +503,7 @@ export function typescript_invalid_feature(node, feature) { * @returns {never} */ export function css_empty_declaration(node) { - e(node, "css_empty_declaration", `Declaration cannot be empty\nhttps://svelte.dev/e/css_empty_declaration`); + e(node, 'css_empty_declaration', `Declaration cannot be empty\nhttps://svelte.dev/e/css_empty_declaration`); } /** @@ -512,7 +512,7 @@ export function css_empty_declaration(node) { * @returns {never} */ export function css_expected_identifier(node) { - e(node, "css_expected_identifier", `Expected a valid CSS identifier\nhttps://svelte.dev/e/css_expected_identifier`); + e(node, 'css_expected_identifier', `Expected a valid CSS identifier\nhttps://svelte.dev/e/css_expected_identifier`); } /** @@ -522,7 +522,7 @@ export function css_expected_identifier(node) { * @returns {never} */ export function css_global_block_invalid_combinator(node, name) { - e(node, "css_global_block_invalid_combinator", `A \`:global\` selector cannot follow a \`${name}\` combinator\nhttps://svelte.dev/e/css_global_block_invalid_combinator`); + e(node, 'css_global_block_invalid_combinator', `A \`:global\` selector cannot follow a \`${name}\` combinator\nhttps://svelte.dev/e/css_global_block_invalid_combinator`); } /** @@ -531,7 +531,7 @@ export function css_global_block_invalid_combinator(node, name) { * @returns {never} */ export function css_global_block_invalid_declaration(node) { - e(node, "css_global_block_invalid_declaration", `A top-level \`:global {...}\` block can only contain rules, not declarations\nhttps://svelte.dev/e/css_global_block_invalid_declaration`); + e(node, 'css_global_block_invalid_declaration', `A top-level \`:global {...}\` block can only contain rules, not declarations\nhttps://svelte.dev/e/css_global_block_invalid_declaration`); } /** @@ -540,7 +540,7 @@ export function css_global_block_invalid_declaration(node) { * @returns {never} */ export function css_global_block_invalid_list(node) { - e(node, "css_global_block_invalid_list", `A \`:global\` selector cannot be part of a selector list with more than one item\nhttps://svelte.dev/e/css_global_block_invalid_list`); + e(node, 'css_global_block_invalid_list', `A \`:global\` selector cannot be part of a selector list with more than one item\nhttps://svelte.dev/e/css_global_block_invalid_list`); } /** @@ -549,7 +549,7 @@ export function css_global_block_invalid_list(node) { * @returns {never} */ export function css_global_block_invalid_modifier(node) { - e(node, "css_global_block_invalid_modifier", `A \`:global\` selector cannot modify an existing selector\nhttps://svelte.dev/e/css_global_block_invalid_modifier`); + e(node, 'css_global_block_invalid_modifier', `A \`:global\` selector cannot modify an existing selector\nhttps://svelte.dev/e/css_global_block_invalid_modifier`); } /** @@ -558,7 +558,7 @@ export function css_global_block_invalid_modifier(node) { * @returns {never} */ export function css_global_block_invalid_modifier_start(node) { - e(node, "css_global_block_invalid_modifier_start", `A \`:global\` selector can only be modified if it is a descendant of other selectors\nhttps://svelte.dev/e/css_global_block_invalid_modifier_start`); + e(node, 'css_global_block_invalid_modifier_start', `A \`:global\` selector can only be modified if it is a descendant of other selectors\nhttps://svelte.dev/e/css_global_block_invalid_modifier_start`); } /** @@ -567,7 +567,7 @@ export function css_global_block_invalid_modifier_start(node) { * @returns {never} */ export function css_global_invalid_placement(node) { - e(node, "css_global_invalid_placement", `\`:global(...)\` can be at the start or end of a selector sequence, but not in the middle\nhttps://svelte.dev/e/css_global_invalid_placement`); + e(node, 'css_global_invalid_placement', `\`:global(...)\` can be at the start or end of a selector sequence, but not in the middle\nhttps://svelte.dev/e/css_global_invalid_placement`); } /** @@ -576,7 +576,7 @@ export function css_global_invalid_placement(node) { * @returns {never} */ export function css_global_invalid_selector(node) { - e(node, "css_global_invalid_selector", `\`:global(...)\` must contain exactly one selector\nhttps://svelte.dev/e/css_global_invalid_selector`); + e(node, 'css_global_invalid_selector', `\`:global(...)\` must contain exactly one selector\nhttps://svelte.dev/e/css_global_invalid_selector`); } /** @@ -585,7 +585,7 @@ export function css_global_invalid_selector(node) { * @returns {never} */ export function css_global_invalid_selector_list(node) { - e(node, "css_global_invalid_selector_list", `\`:global(...)\` must not contain type or universal selectors when used in a compound selector\nhttps://svelte.dev/e/css_global_invalid_selector_list`); + e(node, 'css_global_invalid_selector_list', `\`:global(...)\` must not contain type or universal selectors when used in a compound selector\nhttps://svelte.dev/e/css_global_invalid_selector_list`); } /** @@ -594,7 +594,7 @@ export function css_global_invalid_selector_list(node) { * @returns {never} */ export function css_nesting_selector_invalid_placement(node) { - e(node, "css_nesting_selector_invalid_placement", `Nesting selectors can only be used inside a rule or as the first selector inside a lone \`:global(...)\`\nhttps://svelte.dev/e/css_nesting_selector_invalid_placement`); + e(node, 'css_nesting_selector_invalid_placement', `Nesting selectors can only be used inside a rule or as the first selector inside a lone \`:global(...)\`\nhttps://svelte.dev/e/css_nesting_selector_invalid_placement`); } /** @@ -603,7 +603,7 @@ export function css_nesting_selector_invalid_placement(node) { * @returns {never} */ export function css_selector_invalid(node) { - e(node, "css_selector_invalid", `Invalid selector\nhttps://svelte.dev/e/css_selector_invalid`); + e(node, 'css_selector_invalid', `Invalid selector\nhttps://svelte.dev/e/css_selector_invalid`); } /** @@ -612,7 +612,7 @@ export function css_selector_invalid(node) { * @returns {never} */ export function css_type_selector_invalid_placement(node) { - e(node, "css_type_selector_invalid_placement", `\`:global(...)\` must not be followed by a type selector\nhttps://svelte.dev/e/css_type_selector_invalid_placement`); + e(node, 'css_type_selector_invalid_placement', `\`:global(...)\` must not be followed by a type selector\nhttps://svelte.dev/e/css_type_selector_invalid_placement`); } /** @@ -621,7 +621,7 @@ export function css_type_selector_invalid_placement(node) { * @returns {never} */ export function animation_duplicate(node) { - e(node, "animation_duplicate", `An element can only have one 'animate' directive\nhttps://svelte.dev/e/animation_duplicate`); + e(node, 'animation_duplicate', `An element can only have one 'animate' directive\nhttps://svelte.dev/e/animation_duplicate`); } /** @@ -630,7 +630,7 @@ export function animation_duplicate(node) { * @returns {never} */ export function animation_invalid_placement(node) { - e(node, "animation_invalid_placement", `An element that uses the \`animate:\` directive must be the only child of a keyed \`{#each ...}\` block\nhttps://svelte.dev/e/animation_invalid_placement`); + e(node, 'animation_invalid_placement', `An element that uses the \`animate:\` directive must be the only child of a keyed \`{#each ...}\` block\nhttps://svelte.dev/e/animation_invalid_placement`); } /** @@ -639,7 +639,7 @@ export function animation_invalid_placement(node) { * @returns {never} */ export function animation_missing_key(node) { - e(node, "animation_missing_key", `An element that uses the \`animate:\` directive must be the only child of a keyed \`{#each ...}\` block. Did you forget to add a key to your each block?\nhttps://svelte.dev/e/animation_missing_key`); + e(node, 'animation_missing_key', `An element that uses the \`animate:\` directive must be the only child of a keyed \`{#each ...}\` block. Did you forget to add a key to your each block?\nhttps://svelte.dev/e/animation_missing_key`); } /** @@ -648,7 +648,7 @@ export function animation_missing_key(node) { * @returns {never} */ export function attribute_contenteditable_dynamic(node) { - e(node, "attribute_contenteditable_dynamic", `'contenteditable' attribute cannot be dynamic if element uses two-way binding\nhttps://svelte.dev/e/attribute_contenteditable_dynamic`); + e(node, 'attribute_contenteditable_dynamic', `'contenteditable' attribute cannot be dynamic if element uses two-way binding\nhttps://svelte.dev/e/attribute_contenteditable_dynamic`); } /** @@ -657,7 +657,7 @@ export function attribute_contenteditable_dynamic(node) { * @returns {never} */ export function attribute_contenteditable_missing(node) { - e(node, "attribute_contenteditable_missing", `'contenteditable' attribute is required for textContent, innerHTML and innerText two-way bindings\nhttps://svelte.dev/e/attribute_contenteditable_missing`); + e(node, 'attribute_contenteditable_missing', `'contenteditable' attribute is required for textContent, innerHTML and innerText two-way bindings\nhttps://svelte.dev/e/attribute_contenteditable_missing`); } /** @@ -666,7 +666,7 @@ export function attribute_contenteditable_missing(node) { * @returns {never} */ export function attribute_duplicate(node) { - e(node, "attribute_duplicate", `Attributes need to be unique\nhttps://svelte.dev/e/attribute_duplicate`); + e(node, 'attribute_duplicate', `Attributes need to be unique\nhttps://svelte.dev/e/attribute_duplicate`); } /** @@ -675,7 +675,7 @@ export function attribute_duplicate(node) { * @returns {never} */ export function attribute_empty_shorthand(node) { - e(node, "attribute_empty_shorthand", `Attribute shorthand cannot be empty\nhttps://svelte.dev/e/attribute_empty_shorthand`); + e(node, 'attribute_empty_shorthand', `Attribute shorthand cannot be empty\nhttps://svelte.dev/e/attribute_empty_shorthand`); } /** @@ -684,7 +684,7 @@ export function attribute_empty_shorthand(node) { * @returns {never} */ export function attribute_invalid_event_handler(node) { - e(node, "attribute_invalid_event_handler", `Event attribute must be a JavaScript expression, not a string\nhttps://svelte.dev/e/attribute_invalid_event_handler`); + e(node, 'attribute_invalid_event_handler', `Event attribute must be a JavaScript expression, not a string\nhttps://svelte.dev/e/attribute_invalid_event_handler`); } /** @@ -693,7 +693,7 @@ export function attribute_invalid_event_handler(node) { * @returns {never} */ export function attribute_invalid_multiple(node) { - e(node, "attribute_invalid_multiple", `'multiple' attribute must be static if select uses two-way binding\nhttps://svelte.dev/e/attribute_invalid_multiple`); + e(node, 'attribute_invalid_multiple', `'multiple' attribute must be static if select uses two-way binding\nhttps://svelte.dev/e/attribute_invalid_multiple`); } /** @@ -703,7 +703,7 @@ export function attribute_invalid_multiple(node) { * @returns {never} */ export function attribute_invalid_name(node, name) { - e(node, "attribute_invalid_name", `'${name}' is not a valid attribute name\nhttps://svelte.dev/e/attribute_invalid_name`); + e(node, 'attribute_invalid_name', `'${name}' is not a valid attribute name\nhttps://svelte.dev/e/attribute_invalid_name`); } /** @@ -712,7 +712,7 @@ export function attribute_invalid_name(node, name) { * @returns {never} */ export function attribute_invalid_sequence_expression(node) { - e(node, "attribute_invalid_sequence_expression", `Sequence expressions are not allowed as attribute/directive values in runes mode, unless wrapped in parentheses\nhttps://svelte.dev/e/attribute_invalid_sequence_expression`); + e(node, 'attribute_invalid_sequence_expression', `Sequence expressions are not allowed as attribute/directive values in runes mode, unless wrapped in parentheses\nhttps://svelte.dev/e/attribute_invalid_sequence_expression`); } /** @@ -721,7 +721,7 @@ export function attribute_invalid_sequence_expression(node) { * @returns {never} */ export function attribute_invalid_type(node) { - e(node, "attribute_invalid_type", `'type' attribute must be a static text value if input uses two-way binding\nhttps://svelte.dev/e/attribute_invalid_type`); + e(node, 'attribute_invalid_type', `'type' attribute must be a static text value if input uses two-way binding\nhttps://svelte.dev/e/attribute_invalid_type`); } /** @@ -730,7 +730,7 @@ export function attribute_invalid_type(node) { * @returns {never} */ export function attribute_unquoted_sequence(node) { - e(node, "attribute_unquoted_sequence", `Attribute values containing \`{...}\` must be enclosed in quote marks, unless the value only contains the expression\nhttps://svelte.dev/e/attribute_unquoted_sequence`); + e(node, 'attribute_unquoted_sequence', `Attribute values containing \`{...}\` must be enclosed in quote marks, unless the value only contains the expression\nhttps://svelte.dev/e/attribute_unquoted_sequence`); } /** @@ -739,7 +739,7 @@ export function attribute_unquoted_sequence(node) { * @returns {never} */ export function bind_group_invalid_expression(node) { - e(node, "bind_group_invalid_expression", `\`bind:group\` can only bind to an Identifier or MemberExpression\nhttps://svelte.dev/e/bind_group_invalid_expression`); + e(node, 'bind_group_invalid_expression', `\`bind:group\` can only bind to an Identifier or MemberExpression\nhttps://svelte.dev/e/bind_group_invalid_expression`); } /** @@ -748,7 +748,7 @@ export function bind_group_invalid_expression(node) { * @returns {never} */ export function bind_invalid_expression(node) { - e(node, "bind_invalid_expression", `Can only bind to an Identifier or MemberExpression or a \`{get, set}\` pair\nhttps://svelte.dev/e/bind_invalid_expression`); + e(node, 'bind_invalid_expression', `Can only bind to an Identifier or MemberExpression or a \`{get, set}\` pair\nhttps://svelte.dev/e/bind_invalid_expression`); } /** @@ -759,7 +759,7 @@ export function bind_invalid_expression(node) { * @returns {never} */ export function bind_invalid_name(node, name, explanation) { - e(node, "bind_invalid_name", `${explanation ? `\`bind:${name}\` is not a valid binding. ${explanation}` : `\`bind:${name}\` is not a valid binding`}\nhttps://svelte.dev/e/bind_invalid_name`); + e(node, 'bind_invalid_name', `${explanation ? `\`bind:${name}\` is not a valid binding. ${explanation}` : `\`bind:${name}\` is not a valid binding`}\nhttps://svelte.dev/e/bind_invalid_name`); } /** @@ -769,7 +769,7 @@ export function bind_invalid_name(node, name, explanation) { * @returns {never} */ export function bind_invalid_parens(node, name) { - e(node, "bind_invalid_parens", `\`bind:${name}={get, set}\` must not have surrounding parentheses\nhttps://svelte.dev/e/bind_invalid_parens`); + e(node, 'bind_invalid_parens', `\`bind:${name}={get, set}\` must not have surrounding parentheses\nhttps://svelte.dev/e/bind_invalid_parens`); } /** @@ -780,7 +780,7 @@ export function bind_invalid_parens(node, name) { * @returns {never} */ export function bind_invalid_target(node, name, elements) { - e(node, "bind_invalid_target", `\`bind:${name}\` can only be used with ${elements}\nhttps://svelte.dev/e/bind_invalid_target`); + e(node, 'bind_invalid_target', `\`bind:${name}\` can only be used with ${elements}\nhttps://svelte.dev/e/bind_invalid_target`); } /** @@ -789,7 +789,7 @@ export function bind_invalid_target(node, name, elements) { * @returns {never} */ export function bind_invalid_value(node) { - e(node, "bind_invalid_value", `Can only bind to state or props\nhttps://svelte.dev/e/bind_invalid_value`); + e(node, 'bind_invalid_value', `Can only bind to state or props\nhttps://svelte.dev/e/bind_invalid_value`); } /** @@ -799,7 +799,7 @@ export function bind_invalid_value(node) { * @returns {never} */ export function block_duplicate_clause(node, name) { - e(node, "block_duplicate_clause", `${name} cannot appear more than once within a block\nhttps://svelte.dev/e/block_duplicate_clause`); + e(node, 'block_duplicate_clause', `${name} cannot appear more than once within a block\nhttps://svelte.dev/e/block_duplicate_clause`); } /** @@ -808,7 +808,7 @@ export function block_duplicate_clause(node, name) { * @returns {never} */ export function block_invalid_continuation_placement(node) { - e(node, "block_invalid_continuation_placement", `{:...} block is invalid at this position (did you forget to close the preceding element or block?)\nhttps://svelte.dev/e/block_invalid_continuation_placement`); + e(node, 'block_invalid_continuation_placement', `{:...} block is invalid at this position (did you forget to close the preceding element or block?)\nhttps://svelte.dev/e/block_invalid_continuation_placement`); } /** @@ -817,7 +817,7 @@ export function block_invalid_continuation_placement(node) { * @returns {never} */ export function block_invalid_elseif(node) { - e(node, "block_invalid_elseif", `'elseif' should be 'else if'\nhttps://svelte.dev/e/block_invalid_elseif`); + e(node, 'block_invalid_elseif', `'elseif' should be 'else if'\nhttps://svelte.dev/e/block_invalid_elseif`); } /** @@ -828,7 +828,7 @@ export function block_invalid_elseif(node) { * @returns {never} */ export function block_invalid_placement(node, name, location) { - e(node, "block_invalid_placement", `{#${name} ...} block cannot be ${location}\nhttps://svelte.dev/e/block_invalid_placement`); + e(node, 'block_invalid_placement', `{#${name} ...} block cannot be ${location}\nhttps://svelte.dev/e/block_invalid_placement`); } /** @@ -837,7 +837,7 @@ export function block_invalid_placement(node, name, location) { * @returns {never} */ export function block_unclosed(node) { - e(node, "block_unclosed", `Block was left open\nhttps://svelte.dev/e/block_unclosed`); + e(node, 'block_unclosed', `Block was left open\nhttps://svelte.dev/e/block_unclosed`); } /** @@ -847,7 +847,7 @@ export function block_unclosed(node) { * @returns {never} */ export function block_unexpected_character(node, character) { - e(node, "block_unexpected_character", `Expected a \`${character}\` character immediately following the opening bracket\nhttps://svelte.dev/e/block_unexpected_character`); + e(node, 'block_unexpected_character', `Expected a \`${character}\` character immediately following the opening bracket\nhttps://svelte.dev/e/block_unexpected_character`); } /** @@ -856,7 +856,7 @@ export function block_unexpected_character(node, character) { * @returns {never} */ export function block_unexpected_close(node) { - e(node, "block_unexpected_close", `Unexpected block closing tag\nhttps://svelte.dev/e/block_unexpected_close`); + e(node, 'block_unexpected_close', `Unexpected block closing tag\nhttps://svelte.dev/e/block_unexpected_close`); } /** @@ -865,7 +865,7 @@ export function block_unexpected_close(node) { * @returns {never} */ export function component_invalid_directive(node) { - e(node, "component_invalid_directive", `This type of directive is not valid on components\nhttps://svelte.dev/e/component_invalid_directive`); + e(node, 'component_invalid_directive', `This type of directive is not valid on components\nhttps://svelte.dev/e/component_invalid_directive`); } /** @@ -875,7 +875,7 @@ export function component_invalid_directive(node) { * @returns {never} */ export function const_tag_cycle(node, cycle) { - e(node, "const_tag_cycle", `Cyclical dependency detected: ${cycle}\nhttps://svelte.dev/e/const_tag_cycle`); + e(node, 'const_tag_cycle', `Cyclical dependency detected: ${cycle}\nhttps://svelte.dev/e/const_tag_cycle`); } /** @@ -884,7 +884,7 @@ export function const_tag_cycle(node, cycle) { * @returns {never} */ export function const_tag_invalid_expression(node) { - e(node, "const_tag_invalid_expression", `{@const ...} must consist of a single variable declaration\nhttps://svelte.dev/e/const_tag_invalid_expression`); + e(node, 'const_tag_invalid_expression', `{@const ...} must consist of a single variable declaration\nhttps://svelte.dev/e/const_tag_invalid_expression`); } /** @@ -893,7 +893,7 @@ export function const_tag_invalid_expression(node) { * @returns {never} */ export function const_tag_invalid_placement(node) { - e(node, "const_tag_invalid_placement", `\`{@const}\` must be the immediate child of \`{#snippet}\`, \`{#if}\`, \`{:else if}\`, \`{:else}\`, \`{#each}\`, \`{:then}\`, \`{:catch}\`, \`<svelte:fragment>\` or \`<Component>\`\nhttps://svelte.dev/e/const_tag_invalid_placement`); + e(node, 'const_tag_invalid_placement', `\`{@const}\` must be the immediate child of \`{#snippet}\`, \`{#if}\`, \`{:else if}\`, \`{:else}\`, \`{#each}\`, \`{:then}\`, \`{:catch}\`, \`<svelte:fragment>\` or \`<Component>\`\nhttps://svelte.dev/e/const_tag_invalid_placement`); } /** @@ -902,7 +902,7 @@ export function const_tag_invalid_placement(node) { * @returns {never} */ export function debug_tag_invalid_arguments(node) { - e(node, "debug_tag_invalid_arguments", `{@debug ...} arguments must be identifiers, not arbitrary expressions\nhttps://svelte.dev/e/debug_tag_invalid_arguments`); + e(node, 'debug_tag_invalid_arguments', `{@debug ...} arguments must be identifiers, not arbitrary expressions\nhttps://svelte.dev/e/debug_tag_invalid_arguments`); } /** @@ -911,7 +911,7 @@ export function debug_tag_invalid_arguments(node) { * @returns {never} */ export function directive_invalid_value(node) { - e(node, "directive_invalid_value", `Directive value must be a JavaScript expression enclosed in curly braces\nhttps://svelte.dev/e/directive_invalid_value`); + e(node, 'directive_invalid_value', `Directive value must be a JavaScript expression enclosed in curly braces\nhttps://svelte.dev/e/directive_invalid_value`); } /** @@ -921,7 +921,7 @@ export function directive_invalid_value(node) { * @returns {never} */ export function directive_missing_name(node, type) { - e(node, "directive_missing_name", `\`${type}\` name cannot be empty\nhttps://svelte.dev/e/directive_missing_name`); + e(node, 'directive_missing_name', `\`${type}\` name cannot be empty\nhttps://svelte.dev/e/directive_missing_name`); } /** @@ -931,7 +931,7 @@ export function directive_missing_name(node, type) { * @returns {never} */ export function element_invalid_closing_tag(node, name) { - e(node, "element_invalid_closing_tag", `\`</${name}>\` attempted to close an element that was not open\nhttps://svelte.dev/e/element_invalid_closing_tag`); + e(node, 'element_invalid_closing_tag', `\`</${name}>\` attempted to close an element that was not open\nhttps://svelte.dev/e/element_invalid_closing_tag`); } /** @@ -942,7 +942,7 @@ export function element_invalid_closing_tag(node, name) { * @returns {never} */ export function element_invalid_closing_tag_autoclosed(node, name, reason) { - e(node, "element_invalid_closing_tag_autoclosed", `\`</${name}>\` attempted to close element that was already automatically closed by \`<${reason}>\` (cannot nest \`<${reason}>\` inside \`<${name}>\`)\nhttps://svelte.dev/e/element_invalid_closing_tag_autoclosed`); + e(node, 'element_invalid_closing_tag_autoclosed', `\`</${name}>\` attempted to close element that was already automatically closed by \`<${reason}>\` (cannot nest \`<${reason}>\` inside \`<${name}>\`)\nhttps://svelte.dev/e/element_invalid_closing_tag_autoclosed`); } /** @@ -952,7 +952,7 @@ export function element_invalid_closing_tag_autoclosed(node, name, reason) { * @returns {never} */ export function element_unclosed(node, name) { - e(node, "element_unclosed", `\`<${name}>\` was left open\nhttps://svelte.dev/e/element_unclosed`); + e(node, 'element_unclosed', `\`<${name}>\` was left open\nhttps://svelte.dev/e/element_unclosed`); } /** @@ -961,7 +961,7 @@ export function element_unclosed(node, name) { * @returns {never} */ export function event_handler_invalid_component_modifier(node) { - e(node, "event_handler_invalid_component_modifier", `Event modifiers other than 'once' can only be used on DOM elements\nhttps://svelte.dev/e/event_handler_invalid_component_modifier`); + e(node, 'event_handler_invalid_component_modifier', `Event modifiers other than 'once' can only be used on DOM elements\nhttps://svelte.dev/e/event_handler_invalid_component_modifier`); } /** @@ -971,7 +971,7 @@ export function event_handler_invalid_component_modifier(node) { * @returns {never} */ export function event_handler_invalid_modifier(node, list) { - e(node, "event_handler_invalid_modifier", `Valid event modifiers are ${list}\nhttps://svelte.dev/e/event_handler_invalid_modifier`); + e(node, 'event_handler_invalid_modifier', `Valid event modifiers are ${list}\nhttps://svelte.dev/e/event_handler_invalid_modifier`); } /** @@ -982,7 +982,7 @@ export function event_handler_invalid_modifier(node, list) { * @returns {never} */ export function event_handler_invalid_modifier_combination(node, modifier1, modifier2) { - e(node, "event_handler_invalid_modifier_combination", `The '${modifier1}' and '${modifier2}' modifiers cannot be used together\nhttps://svelte.dev/e/event_handler_invalid_modifier_combination`); + e(node, 'event_handler_invalid_modifier_combination', `The '${modifier1}' and '${modifier2}' modifiers cannot be used together\nhttps://svelte.dev/e/event_handler_invalid_modifier_combination`); } /** @@ -991,7 +991,7 @@ export function event_handler_invalid_modifier_combination(node, modifier1, modi * @returns {never} */ export function expected_attribute_value(node) { - e(node, "expected_attribute_value", `Expected attribute value\nhttps://svelte.dev/e/expected_attribute_value`); + e(node, 'expected_attribute_value', `Expected attribute value\nhttps://svelte.dev/e/expected_attribute_value`); } /** @@ -1000,7 +1000,7 @@ export function expected_attribute_value(node) { * @returns {never} */ export function expected_block_type(node) { - e(node, "expected_block_type", `Expected 'if', 'each', 'await', 'key' or 'snippet'\nhttps://svelte.dev/e/expected_block_type`); + e(node, 'expected_block_type', `Expected 'if', 'each', 'await', 'key' or 'snippet'\nhttps://svelte.dev/e/expected_block_type`); } /** @@ -1009,7 +1009,7 @@ export function expected_block_type(node) { * @returns {never} */ export function expected_identifier(node) { - e(node, "expected_identifier", `Expected an identifier\nhttps://svelte.dev/e/expected_identifier`); + e(node, 'expected_identifier', `Expected an identifier\nhttps://svelte.dev/e/expected_identifier`); } /** @@ -1018,7 +1018,7 @@ export function expected_identifier(node) { * @returns {never} */ export function expected_pattern(node) { - e(node, "expected_pattern", `Expected identifier or destructure pattern\nhttps://svelte.dev/e/expected_pattern`); + e(node, 'expected_pattern', `Expected identifier or destructure pattern\nhttps://svelte.dev/e/expected_pattern`); } /** @@ -1028,7 +1028,7 @@ export function expected_pattern(node) { * @returns {never} */ export function expected_token(node, token) { - e(node, "expected_token", `Expected token ${token}\nhttps://svelte.dev/e/expected_token`); + e(node, 'expected_token', `Expected token ${token}\nhttps://svelte.dev/e/expected_token`); } /** @@ -1037,7 +1037,7 @@ export function expected_token(node, token) { * @returns {never} */ export function expected_whitespace(node) { - e(node, "expected_whitespace", `Expected whitespace\nhttps://svelte.dev/e/expected_whitespace`); + e(node, 'expected_whitespace', `Expected whitespace\nhttps://svelte.dev/e/expected_whitespace`); } /** @@ -1047,7 +1047,7 @@ export function expected_whitespace(node) { * @returns {never} */ export function illegal_element_attribute(node, name) { - e(node, "illegal_element_attribute", `\`<${name}>\` does not support non-event attributes or spread attributes\nhttps://svelte.dev/e/illegal_element_attribute`); + e(node, 'illegal_element_attribute', `\`<${name}>\` does not support non-event attributes or spread attributes\nhttps://svelte.dev/e/illegal_element_attribute`); } /** @@ -1057,7 +1057,7 @@ export function illegal_element_attribute(node, name) { * @returns {never} */ export function js_parse_error(node, message) { - e(node, "js_parse_error", `${message}\nhttps://svelte.dev/e/js_parse_error`); + e(node, 'js_parse_error', `${message}\nhttps://svelte.dev/e/js_parse_error`); } /** @@ -1066,7 +1066,7 @@ export function js_parse_error(node, message) { * @returns {never} */ 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`); + e(node, 'let_directive_invalid_placement', `\`let:\` directive at invalid position\nhttps://svelte.dev/e/let_directive_invalid_placement`); } /** @@ -1076,7 +1076,7 @@ export function let_directive_invalid_placement(node) { * @returns {never} */ export function mixed_event_handler_syntaxes(node, name) { - e(node, "mixed_event_handler_syntaxes", `Mixing old (on:${name}) and new syntaxes for event handling is not allowed. Use only the on${name} syntax\nhttps://svelte.dev/e/mixed_event_handler_syntaxes`); + e(node, 'mixed_event_handler_syntaxes', `Mixing old (on:${name}) and new syntaxes for event handling is not allowed. Use only the on${name} syntax\nhttps://svelte.dev/e/mixed_event_handler_syntaxes`); } /** @@ -1086,7 +1086,7 @@ export function mixed_event_handler_syntaxes(node, name) { * @returns {never} */ export function node_invalid_placement(node, message) { - e(node, "node_invalid_placement", `${message}. The browser will 'repair' the HTML (by moving, removing, or inserting elements) which breaks Svelte's assumptions about the structure of your components.\nhttps://svelte.dev/e/node_invalid_placement`); + e(node, 'node_invalid_placement', `${message}. The browser will 'repair' the HTML (by moving, removing, or inserting elements) which breaks Svelte's assumptions about the structure of your components.\nhttps://svelte.dev/e/node_invalid_placement`); } /** @@ -1095,7 +1095,7 @@ export function node_invalid_placement(node, message) { * @returns {never} */ export function render_tag_invalid_call_expression(node) { - e(node, "render_tag_invalid_call_expression", `Calling a snippet function using apply, bind or call is not allowed\nhttps://svelte.dev/e/render_tag_invalid_call_expression`); + e(node, 'render_tag_invalid_call_expression', `Calling a snippet function using apply, bind or call is not allowed\nhttps://svelte.dev/e/render_tag_invalid_call_expression`); } /** @@ -1104,7 +1104,7 @@ export function render_tag_invalid_call_expression(node) { * @returns {never} */ export function render_tag_invalid_expression(node) { - e(node, "render_tag_invalid_expression", `\`{@render ...}\` tags can only contain call expressions\nhttps://svelte.dev/e/render_tag_invalid_expression`); + e(node, 'render_tag_invalid_expression', `\`{@render ...}\` tags can only contain call expressions\nhttps://svelte.dev/e/render_tag_invalid_expression`); } /** @@ -1113,7 +1113,7 @@ export function render_tag_invalid_expression(node) { * @returns {never} */ export function render_tag_invalid_spread_argument(node) { - e(node, "render_tag_invalid_spread_argument", `cannot use spread arguments in \`{@render ...}\` tags\nhttps://svelte.dev/e/render_tag_invalid_spread_argument`); + e(node, 'render_tag_invalid_spread_argument', `cannot use spread arguments in \`{@render ...}\` tags\nhttps://svelte.dev/e/render_tag_invalid_spread_argument`); } /** @@ -1122,7 +1122,7 @@ export function render_tag_invalid_spread_argument(node) { * @returns {never} */ export function script_duplicate(node) { - e(node, "script_duplicate", `A component can have a single top-level \`<script>\` element and/or a single top-level \`<script module>\` element\nhttps://svelte.dev/e/script_duplicate`); + e(node, 'script_duplicate', `A component can have a single top-level \`<script>\` element and/or a single top-level \`<script module>\` element\nhttps://svelte.dev/e/script_duplicate`); } /** @@ -1132,7 +1132,7 @@ export function script_duplicate(node) { * @returns {never} */ export function script_invalid_attribute_value(node, name) { - e(node, "script_invalid_attribute_value", `If the \`${name}\` attribute is supplied, it must be a boolean attribute\nhttps://svelte.dev/e/script_invalid_attribute_value`); + e(node, 'script_invalid_attribute_value', `If the \`${name}\` attribute is supplied, it must be a boolean attribute\nhttps://svelte.dev/e/script_invalid_attribute_value`); } /** @@ -1141,7 +1141,7 @@ export function script_invalid_attribute_value(node, name) { * @returns {never} */ export function script_invalid_context(node) { - e(node, "script_invalid_context", `If the context attribute is supplied, its value must be "module"\nhttps://svelte.dev/e/script_invalid_context`); + e(node, 'script_invalid_context', `If the context attribute is supplied, its value must be "module"\nhttps://svelte.dev/e/script_invalid_context`); } /** @@ -1151,7 +1151,7 @@ export function script_invalid_context(node) { * @returns {never} */ export function script_reserved_attribute(node, name) { - e(node, "script_reserved_attribute", `The \`${name}\` attribute is reserved and cannot be used\nhttps://svelte.dev/e/script_reserved_attribute`); + e(node, 'script_reserved_attribute', `The \`${name}\` attribute is reserved and cannot be used\nhttps://svelte.dev/e/script_reserved_attribute`); } /** @@ -1162,7 +1162,7 @@ export function script_reserved_attribute(node, name) { * @returns {never} */ export function slot_attribute_duplicate(node, name, component) { - e(node, "slot_attribute_duplicate", `Duplicate slot name '${name}' in <${component}>\nhttps://svelte.dev/e/slot_attribute_duplicate`); + e(node, 'slot_attribute_duplicate', `Duplicate slot name '${name}' in <${component}>\nhttps://svelte.dev/e/slot_attribute_duplicate`); } /** @@ -1171,7 +1171,7 @@ export function slot_attribute_duplicate(node, name, component) { * @returns {never} */ export function slot_attribute_invalid(node) { - e(node, "slot_attribute_invalid", `slot attribute must be a static value\nhttps://svelte.dev/e/slot_attribute_invalid`); + e(node, 'slot_attribute_invalid', `slot attribute must be a static value\nhttps://svelte.dev/e/slot_attribute_invalid`); } /** @@ -1180,7 +1180,7 @@ export function slot_attribute_invalid(node) { * @returns {never} */ export function slot_attribute_invalid_placement(node) { - e(node, "slot_attribute_invalid_placement", `Element with a slot='...' attribute must be a child of a component or a descendant of a custom element\nhttps://svelte.dev/e/slot_attribute_invalid_placement`); + e(node, 'slot_attribute_invalid_placement', `Element with a slot='...' attribute must be a child of a component or a descendant of a custom element\nhttps://svelte.dev/e/slot_attribute_invalid_placement`); } /** @@ -1189,7 +1189,7 @@ export function slot_attribute_invalid_placement(node) { * @returns {never} */ export function slot_default_duplicate(node) { - e(node, "slot_default_duplicate", `Found default slot content alongside an explicit slot="default"\nhttps://svelte.dev/e/slot_default_duplicate`); + e(node, 'slot_default_duplicate', `Found default slot content alongside an explicit slot="default"\nhttps://svelte.dev/e/slot_default_duplicate`); } /** @@ -1198,7 +1198,7 @@ export function slot_default_duplicate(node) { * @returns {never} */ export function slot_element_invalid_attribute(node) { - e(node, "slot_element_invalid_attribute", `\`<slot>\` can only receive attributes and (optionally) let directives\nhttps://svelte.dev/e/slot_element_invalid_attribute`); + e(node, 'slot_element_invalid_attribute', `\`<slot>\` can only receive attributes and (optionally) let directives\nhttps://svelte.dev/e/slot_element_invalid_attribute`); } /** @@ -1207,7 +1207,7 @@ export function slot_element_invalid_attribute(node) { * @returns {never} */ export function slot_element_invalid_name(node) { - e(node, "slot_element_invalid_name", `slot attribute must be a static value\nhttps://svelte.dev/e/slot_element_invalid_name`); + e(node, 'slot_element_invalid_name', `slot attribute must be a static value\nhttps://svelte.dev/e/slot_element_invalid_name`); } /** @@ -1216,7 +1216,7 @@ export function slot_element_invalid_name(node) { * @returns {never} */ export function slot_element_invalid_name_default(node) { - e(node, "slot_element_invalid_name_default", `\`default\` is a reserved word — it cannot be used as a slot name\nhttps://svelte.dev/e/slot_element_invalid_name_default`); + e(node, 'slot_element_invalid_name_default', `\`default\` is a reserved word — it cannot be used as a slot name\nhttps://svelte.dev/e/slot_element_invalid_name_default`); } /** @@ -1225,7 +1225,7 @@ export function slot_element_invalid_name_default(node) { * @returns {never} */ export function slot_snippet_conflict(node) { - e(node, "slot_snippet_conflict", `Cannot use \`<slot>\` syntax and \`{@render ...}\` tags in the same component. Migrate towards \`{@render ...}\` tags completely\nhttps://svelte.dev/e/slot_snippet_conflict`); + e(node, 'slot_snippet_conflict', `Cannot use \`<slot>\` syntax and \`{@render ...}\` tags in the same component. Migrate towards \`{@render ...}\` tags completely\nhttps://svelte.dev/e/slot_snippet_conflict`); } /** @@ -1234,7 +1234,7 @@ export function slot_snippet_conflict(node) { * @returns {never} */ export function snippet_conflict(node) { - e(node, "snippet_conflict", `Cannot use explicit children snippet at the same time as implicit children content. Remove either the non-whitespace content or the children snippet block\nhttps://svelte.dev/e/snippet_conflict`); + e(node, 'snippet_conflict', `Cannot use explicit children snippet at the same time as implicit children content. Remove either the non-whitespace content or the children snippet block\nhttps://svelte.dev/e/snippet_conflict`); } /** @@ -1243,7 +1243,7 @@ export function snippet_conflict(node) { * @returns {never} */ export function snippet_invalid_rest_parameter(node) { - e(node, "snippet_invalid_rest_parameter", `Snippets do not support rest parameters; use an array instead\nhttps://svelte.dev/e/snippet_invalid_rest_parameter`); + e(node, 'snippet_invalid_rest_parameter', `Snippets do not support rest parameters; use an array instead\nhttps://svelte.dev/e/snippet_invalid_rest_parameter`); } /** @@ -1253,7 +1253,7 @@ export function snippet_invalid_rest_parameter(node) { * @returns {never} */ export function snippet_shadowing_prop(node, prop) { - e(node, "snippet_shadowing_prop", `This snippet is shadowing the prop \`${prop}\` with the same name\nhttps://svelte.dev/e/snippet_shadowing_prop`); + e(node, 'snippet_shadowing_prop', `This snippet is shadowing the prop \`${prop}\` with the same name\nhttps://svelte.dev/e/snippet_shadowing_prop`); } /** @@ -1262,7 +1262,7 @@ export function snippet_shadowing_prop(node, prop) { * @returns {never} */ export function style_directive_invalid_modifier(node) { - e(node, "style_directive_invalid_modifier", `\`style:\` directive can only use the \`important\` modifier\nhttps://svelte.dev/e/style_directive_invalid_modifier`); + e(node, 'style_directive_invalid_modifier', `\`style:\` directive can only use the \`important\` modifier\nhttps://svelte.dev/e/style_directive_invalid_modifier`); } /** @@ -1271,7 +1271,7 @@ export function style_directive_invalid_modifier(node) { * @returns {never} */ export function style_duplicate(node) { - e(node, "style_duplicate", `A component can have a single top-level \`<style>\` element\nhttps://svelte.dev/e/style_duplicate`); + e(node, 'style_duplicate', `A component can have a single top-level \`<style>\` element\nhttps://svelte.dev/e/style_duplicate`); } /** @@ -1280,7 +1280,7 @@ export function style_duplicate(node) { * @returns {never} */ export function svelte_body_illegal_attribute(node) { - e(node, "svelte_body_illegal_attribute", `\`<svelte:body>\` does not support non-event attributes or spread attributes\nhttps://svelte.dev/e/svelte_body_illegal_attribute`); + e(node, 'svelte_body_illegal_attribute', `\`<svelte:body>\` does not support non-event attributes or spread attributes\nhttps://svelte.dev/e/svelte_body_illegal_attribute`); } /** @@ -1289,7 +1289,7 @@ export function svelte_body_illegal_attribute(node) { * @returns {never} */ export function svelte_boundary_invalid_attribute(node) { - e(node, "svelte_boundary_invalid_attribute", `Valid attributes on \`<svelte:boundary>\` are \`onerror\` and \`failed\`\nhttps://svelte.dev/e/svelte_boundary_invalid_attribute`); + e(node, 'svelte_boundary_invalid_attribute', `Valid attributes on \`<svelte:boundary>\` are \`onerror\` and \`failed\`\nhttps://svelte.dev/e/svelte_boundary_invalid_attribute`); } /** @@ -1298,7 +1298,7 @@ export function svelte_boundary_invalid_attribute(node) { * @returns {never} */ export function svelte_boundary_invalid_attribute_value(node) { - e(node, "svelte_boundary_invalid_attribute_value", `Attribute value must be a non-string expression\nhttps://svelte.dev/e/svelte_boundary_invalid_attribute_value`); + e(node, 'svelte_boundary_invalid_attribute_value', `Attribute value must be a non-string expression\nhttps://svelte.dev/e/svelte_boundary_invalid_attribute_value`); } /** @@ -1307,7 +1307,7 @@ export function svelte_boundary_invalid_attribute_value(node) { * @returns {never} */ export function svelte_component_invalid_this(node) { - e(node, "svelte_component_invalid_this", `Invalid component definition — must be an \`{expression}\`\nhttps://svelte.dev/e/svelte_component_invalid_this`); + e(node, 'svelte_component_invalid_this', `Invalid component definition — must be an \`{expression}\`\nhttps://svelte.dev/e/svelte_component_invalid_this`); } /** @@ -1316,7 +1316,7 @@ export function svelte_component_invalid_this(node) { * @returns {never} */ export function svelte_component_missing_this(node) { - e(node, "svelte_component_missing_this", `\`<svelte:component>\` must have a 'this' attribute\nhttps://svelte.dev/e/svelte_component_missing_this`); + e(node, 'svelte_component_missing_this', `\`<svelte:component>\` must have a 'this' attribute\nhttps://svelte.dev/e/svelte_component_missing_this`); } /** @@ -1325,7 +1325,7 @@ export function svelte_component_missing_this(node) { * @returns {never} */ export function svelte_element_missing_this(node) { - e(node, "svelte_element_missing_this", `\`<svelte:element>\` must have a 'this' attribute with a value\nhttps://svelte.dev/e/svelte_element_missing_this`); + e(node, 'svelte_element_missing_this', `\`<svelte:element>\` must have a 'this' attribute with a value\nhttps://svelte.dev/e/svelte_element_missing_this`); } /** @@ -1334,7 +1334,7 @@ export function svelte_element_missing_this(node) { * @returns {never} */ export function svelte_fragment_invalid_attribute(node) { - e(node, "svelte_fragment_invalid_attribute", `\`<svelte:fragment>\` can only have a slot attribute and (optionally) a let: directive\nhttps://svelte.dev/e/svelte_fragment_invalid_attribute`); + e(node, 'svelte_fragment_invalid_attribute', `\`<svelte:fragment>\` can only have a slot attribute and (optionally) a let: directive\nhttps://svelte.dev/e/svelte_fragment_invalid_attribute`); } /** @@ -1343,7 +1343,7 @@ export function svelte_fragment_invalid_attribute(node) { * @returns {never} */ export function svelte_fragment_invalid_placement(node) { - e(node, "svelte_fragment_invalid_placement", `\`<svelte:fragment>\` must be the direct child of a component\nhttps://svelte.dev/e/svelte_fragment_invalid_placement`); + e(node, 'svelte_fragment_invalid_placement', `\`<svelte:fragment>\` must be the direct child of a component\nhttps://svelte.dev/e/svelte_fragment_invalid_placement`); } /** @@ -1352,7 +1352,7 @@ export function svelte_fragment_invalid_placement(node) { * @returns {never} */ export function svelte_head_illegal_attribute(node) { - e(node, "svelte_head_illegal_attribute", `\`<svelte:head>\` cannot have attributes nor directives\nhttps://svelte.dev/e/svelte_head_illegal_attribute`); + e(node, 'svelte_head_illegal_attribute', `\`<svelte:head>\` cannot have attributes nor directives\nhttps://svelte.dev/e/svelte_head_illegal_attribute`); } /** @@ -1362,7 +1362,7 @@ export function svelte_head_illegal_attribute(node) { * @returns {never} */ export function svelte_meta_duplicate(node, name) { - e(node, "svelte_meta_duplicate", `A component can only have one \`<${name}>\` element\nhttps://svelte.dev/e/svelte_meta_duplicate`); + e(node, 'svelte_meta_duplicate', `A component can only have one \`<${name}>\` element\nhttps://svelte.dev/e/svelte_meta_duplicate`); } /** @@ -1372,7 +1372,7 @@ export function svelte_meta_duplicate(node, name) { * @returns {never} */ export function svelte_meta_invalid_content(node, name) { - e(node, "svelte_meta_invalid_content", `<${name}> cannot have children\nhttps://svelte.dev/e/svelte_meta_invalid_content`); + e(node, 'svelte_meta_invalid_content', `<${name}> cannot have children\nhttps://svelte.dev/e/svelte_meta_invalid_content`); } /** @@ -1382,7 +1382,7 @@ export function svelte_meta_invalid_content(node, name) { * @returns {never} */ export function svelte_meta_invalid_placement(node, name) { - e(node, "svelte_meta_invalid_placement", `\`<${name}>\` tags cannot be inside elements or blocks\nhttps://svelte.dev/e/svelte_meta_invalid_placement`); + e(node, 'svelte_meta_invalid_placement', `\`<${name}>\` tags cannot be inside elements or blocks\nhttps://svelte.dev/e/svelte_meta_invalid_placement`); } /** @@ -1392,7 +1392,7 @@ export function svelte_meta_invalid_placement(node, name) { * @returns {never} */ export function svelte_meta_invalid_tag(node, list) { - e(node, "svelte_meta_invalid_tag", `Valid \`<svelte:...>\` tag names are ${list}\nhttps://svelte.dev/e/svelte_meta_invalid_tag`); + e(node, 'svelte_meta_invalid_tag', `Valid \`<svelte:...>\` tag names are ${list}\nhttps://svelte.dev/e/svelte_meta_invalid_tag`); } /** @@ -1401,7 +1401,7 @@ export function svelte_meta_invalid_tag(node, list) { * @returns {never} */ export function svelte_options_deprecated_tag(node) { - e(node, "svelte_options_deprecated_tag", `"tag" option is deprecated — use "customElement" instead\nhttps://svelte.dev/e/svelte_options_deprecated_tag`); + e(node, 'svelte_options_deprecated_tag', `"tag" option is deprecated — use "customElement" instead\nhttps://svelte.dev/e/svelte_options_deprecated_tag`); } /** @@ -1410,7 +1410,7 @@ export function svelte_options_deprecated_tag(node) { * @returns {never} */ export function svelte_options_invalid_attribute(node) { - e(node, "svelte_options_invalid_attribute", `\`<svelte:options>\` can only receive static attributes\nhttps://svelte.dev/e/svelte_options_invalid_attribute`); + e(node, 'svelte_options_invalid_attribute', `\`<svelte:options>\` can only receive static attributes\nhttps://svelte.dev/e/svelte_options_invalid_attribute`); } /** @@ -1420,7 +1420,7 @@ export function svelte_options_invalid_attribute(node) { * @returns {never} */ export function svelte_options_invalid_attribute_value(node, list) { - e(node, "svelte_options_invalid_attribute_value", `Value must be ${list}, if specified\nhttps://svelte.dev/e/svelte_options_invalid_attribute_value`); + e(node, 'svelte_options_invalid_attribute_value', `Value must be ${list}, if specified\nhttps://svelte.dev/e/svelte_options_invalid_attribute_value`); } /** @@ -1429,7 +1429,7 @@ export function svelte_options_invalid_attribute_value(node, list) { * @returns {never} */ export function svelte_options_invalid_customelement(node) { - e(node, "svelte_options_invalid_customelement", `"customElement" must be a string literal defining a valid custom element name or an object of the form { tag?: string; shadow?: "open" | "none"; props?: { [key: string]: { attribute?: string; reflect?: boolean; type: .. } } }\nhttps://svelte.dev/e/svelte_options_invalid_customelement`); + e(node, 'svelte_options_invalid_customelement', `"customElement" must be a string literal defining a valid custom element name or an object of the form { tag?: string; shadow?: "open" | "none"; props?: { [key: string]: { attribute?: string; reflect?: boolean; type: .. } } }\nhttps://svelte.dev/e/svelte_options_invalid_customelement`); } /** @@ -1438,7 +1438,7 @@ export function svelte_options_invalid_customelement(node) { * @returns {never} */ export function svelte_options_invalid_customelement_props(node) { - e(node, "svelte_options_invalid_customelement_props", `"props" must be a statically analyzable object literal of the form "{ [key: string]: { attribute?: string; reflect?: boolean; type?: "String" | "Boolean" | "Number" | "Array" | "Object" }"\nhttps://svelte.dev/e/svelte_options_invalid_customelement_props`); + e(node, 'svelte_options_invalid_customelement_props', `"props" must be a statically analyzable object literal of the form "{ [key: string]: { attribute?: string; reflect?: boolean; type?: "String" | "Boolean" | "Number" | "Array" | "Object" }"\nhttps://svelte.dev/e/svelte_options_invalid_customelement_props`); } /** @@ -1447,7 +1447,7 @@ export function svelte_options_invalid_customelement_props(node) { * @returns {never} */ export function svelte_options_invalid_customelement_shadow(node) { - e(node, "svelte_options_invalid_customelement_shadow", `"shadow" must be either "open" or "none"\nhttps://svelte.dev/e/svelte_options_invalid_customelement_shadow`); + e(node, 'svelte_options_invalid_customelement_shadow', `"shadow" must be either "open" or "none"\nhttps://svelte.dev/e/svelte_options_invalid_customelement_shadow`); } /** @@ -1456,7 +1456,7 @@ export function svelte_options_invalid_customelement_shadow(node) { * @returns {never} */ export function svelte_options_invalid_tagname(node) { - e(node, "svelte_options_invalid_tagname", `Tag name must be lowercase and hyphenated\nhttps://svelte.dev/e/svelte_options_invalid_tagname`); + e(node, 'svelte_options_invalid_tagname', `Tag name must be lowercase and hyphenated\nhttps://svelte.dev/e/svelte_options_invalid_tagname`); } /** @@ -1465,7 +1465,7 @@ export function svelte_options_invalid_tagname(node) { * @returns {never} */ export function svelte_options_reserved_tagname(node) { - e(node, "svelte_options_reserved_tagname", `Tag name is reserved\nhttps://svelte.dev/e/svelte_options_reserved_tagname`); + e(node, 'svelte_options_reserved_tagname', `Tag name is reserved\nhttps://svelte.dev/e/svelte_options_reserved_tagname`); } /** @@ -1475,7 +1475,7 @@ export function svelte_options_reserved_tagname(node) { * @returns {never} */ export function svelte_options_unknown_attribute(node, name) { - e(node, "svelte_options_unknown_attribute", `\`<svelte:options>\` unknown attribute '${name}'\nhttps://svelte.dev/e/svelte_options_unknown_attribute`); + e(node, 'svelte_options_unknown_attribute', `\`<svelte:options>\` unknown attribute '${name}'\nhttps://svelte.dev/e/svelte_options_unknown_attribute`); } /** @@ -1484,7 +1484,7 @@ export function svelte_options_unknown_attribute(node, name) { * @returns {never} */ export function svelte_self_invalid_placement(node) { - e(node, "svelte_self_invalid_placement", `\`<svelte:self>\` components can only exist inside \`{#if}\` blocks, \`{#each}\` blocks, \`{#snippet}\` blocks or slots passed to components\nhttps://svelte.dev/e/svelte_self_invalid_placement`); + e(node, 'svelte_self_invalid_placement', `\`<svelte:self>\` components can only exist inside \`{#if}\` blocks, \`{#each}\` blocks, \`{#snippet}\` blocks or slots passed to components\nhttps://svelte.dev/e/svelte_self_invalid_placement`); } /** @@ -1493,7 +1493,7 @@ export function svelte_self_invalid_placement(node) { * @returns {never} */ export function tag_invalid_name(node) { - e(node, "tag_invalid_name", `Expected a valid element or component name. Components must have a valid variable name or dot notation expression\nhttps://svelte.dev/e/tag_invalid_name`); + e(node, 'tag_invalid_name', `Expected a valid element or component name. Components must have a valid variable name or dot notation expression\nhttps://svelte.dev/e/tag_invalid_name`); } /** @@ -1504,7 +1504,7 @@ export function tag_invalid_name(node) { * @returns {never} */ export function tag_invalid_placement(node, name, location) { - e(node, "tag_invalid_placement", `{@${name} ...} tag cannot be ${location}\nhttps://svelte.dev/e/tag_invalid_placement`); + e(node, 'tag_invalid_placement', `{@${name} ...} tag cannot be ${location}\nhttps://svelte.dev/e/tag_invalid_placement`); } /** @@ -1513,7 +1513,7 @@ export function tag_invalid_placement(node, name, location) { * @returns {never} */ export function textarea_invalid_content(node) { - e(node, "textarea_invalid_content", `A \`<textarea>\` can have either a value attribute or (equivalently) child content, but not both\nhttps://svelte.dev/e/textarea_invalid_content`); + e(node, 'textarea_invalid_content', `A \`<textarea>\` can have either a value attribute or (equivalently) child content, but not both\nhttps://svelte.dev/e/textarea_invalid_content`); } /** @@ -1522,7 +1522,7 @@ export function textarea_invalid_content(node) { * @returns {never} */ export function title_illegal_attribute(node) { - e(node, "title_illegal_attribute", `\`<title>\` cannot have attributes nor directives\nhttps://svelte.dev/e/title_illegal_attribute`); + e(node, 'title_illegal_attribute', `\`<title>\` cannot have attributes nor directives\nhttps://svelte.dev/e/title_illegal_attribute`); } /** @@ -1531,7 +1531,7 @@ export function title_illegal_attribute(node) { * @returns {never} */ export function title_invalid_content(node) { - e(node, "title_invalid_content", `\`<title>\` can only contain text and {tags}\nhttps://svelte.dev/e/title_invalid_content`); + e(node, 'title_invalid_content', `\`<title>\` can only contain text and {tags}\nhttps://svelte.dev/e/title_invalid_content`); } /** @@ -1542,7 +1542,7 @@ export function title_invalid_content(node) { * @returns {never} */ export function transition_conflict(node, type, existing) { - e(node, "transition_conflict", `Cannot use \`${type}:\` alongside existing \`${existing}:\` directive\nhttps://svelte.dev/e/transition_conflict`); + e(node, 'transition_conflict', `Cannot use \`${type}:\` alongside existing \`${existing}:\` directive\nhttps://svelte.dev/e/transition_conflict`); } /** @@ -1552,7 +1552,7 @@ export function transition_conflict(node, type, existing) { * @returns {never} */ export function transition_duplicate(node, type) { - e(node, "transition_duplicate", `Cannot use multiple \`${type}:\` directives on a single element\nhttps://svelte.dev/e/transition_duplicate`); + e(node, 'transition_duplicate', `Cannot use multiple \`${type}:\` directives on a single element\nhttps://svelte.dev/e/transition_duplicate`); } /** @@ -1561,7 +1561,7 @@ export function transition_duplicate(node, type) { * @returns {never} */ export function unexpected_eof(node) { - e(node, "unexpected_eof", `Unexpected end of input\nhttps://svelte.dev/e/unexpected_eof`); + e(node, 'unexpected_eof', `Unexpected end of input\nhttps://svelte.dev/e/unexpected_eof`); } /** @@ -1571,7 +1571,7 @@ export function unexpected_eof(node) { * @returns {never} */ export function unexpected_reserved_word(node, word) { - e(node, "unexpected_reserved_word", `'${word}' is a reserved word in JavaScript and cannot be used here\nhttps://svelte.dev/e/unexpected_reserved_word`); + e(node, 'unexpected_reserved_word', `'${word}' is a reserved word in JavaScript and cannot be used here\nhttps://svelte.dev/e/unexpected_reserved_word`); } /** @@ -1580,7 +1580,7 @@ export function unexpected_reserved_word(node, word) { * @returns {never} */ export function unterminated_string_constant(node) { - e(node, "unterminated_string_constant", `Unterminated string constant\nhttps://svelte.dev/e/unterminated_string_constant`); + e(node, 'unterminated_string_constant', `Unterminated string constant\nhttps://svelte.dev/e/unterminated_string_constant`); } /** @@ -1589,5 +1589,5 @@ export function unterminated_string_constant(node) { * @returns {never} */ export function void_element_invalid_content(node) { - e(node, "void_element_invalid_content", `Void elements cannot have children or closing tags\nhttps://svelte.dev/e/void_element_invalid_content`); + e(node, 'void_element_invalid_content', `Void elements cannot have children or closing tags\nhttps://svelte.dev/e/void_element_invalid_content`); } \ No newline at end of file diff --git a/packages/svelte/src/compiler/warnings.js b/packages/svelte/src/compiler/warnings.js index 81d4e3240e..a9ea617d3f 100644 --- a/packages/svelte/src/compiler/warnings.js +++ b/packages/svelte/src/compiler/warnings.js @@ -44,84 +44,84 @@ function w(node, code, message) { } export const codes = [ - "a11y_accesskey", - "a11y_aria_activedescendant_has_tabindex", - "a11y_aria_attributes", - "a11y_autocomplete_valid", - "a11y_autofocus", - "a11y_click_events_have_key_events", - "a11y_consider_explicit_label", - "a11y_distracting_elements", - "a11y_figcaption_index", - "a11y_figcaption_parent", - "a11y_hidden", - "a11y_img_redundant_alt", - "a11y_incorrect_aria_attribute_type", - "a11y_incorrect_aria_attribute_type_boolean", - "a11y_incorrect_aria_attribute_type_id", - "a11y_incorrect_aria_attribute_type_idlist", - "a11y_incorrect_aria_attribute_type_integer", - "a11y_incorrect_aria_attribute_type_token", - "a11y_incorrect_aria_attribute_type_tokenlist", - "a11y_incorrect_aria_attribute_type_tristate", - "a11y_interactive_supports_focus", - "a11y_invalid_attribute", - "a11y_label_has_associated_control", - "a11y_media_has_caption", - "a11y_misplaced_role", - "a11y_misplaced_scope", - "a11y_missing_attribute", - "a11y_missing_content", - "a11y_mouse_events_have_key_events", - "a11y_no_abstract_role", - "a11y_no_interactive_element_to_noninteractive_role", - "a11y_no_noninteractive_element_interactions", - "a11y_no_noninteractive_element_to_interactive_role", - "a11y_no_noninteractive_tabindex", - "a11y_no_redundant_roles", - "a11y_no_static_element_interactions", - "a11y_positive_tabindex", - "a11y_role_has_required_aria_props", - "a11y_role_supports_aria_props", - "a11y_role_supports_aria_props_implicit", - "a11y_unknown_aria_attribute", - "a11y_unknown_role", - "legacy_code", - "unknown_code", - "options_deprecated_accessors", - "options_deprecated_immutable", - "options_missing_custom_element", - "options_removed_enable_sourcemap", - "options_removed_hydratable", - "options_removed_loop_guard_timeout", - "options_renamed_ssr_dom", - "export_let_unused", - "legacy_component_creation", - "non_reactive_update", - "perf_avoid_inline_class", - "perf_avoid_nested_class", - "reactive_declaration_invalid_placement", - "reactive_declaration_module_script_dependency", - "state_referenced_locally", - "store_rune_conflict", - "css_unused_selector", - "attribute_avoid_is", - "attribute_global_event_reference", - "attribute_illegal_colon", - "attribute_invalid_property_name", - "attribute_quoted", - "bind_invalid_each_rest", - "block_empty", - "component_name_lowercase", - "element_invalid_self_closing_tag", - "event_directive_deprecated", - "node_invalid_placement_ssr", - "script_context_deprecated", - "script_unknown_attribute", - "slot_element_deprecated", - "svelte_component_deprecated", - "svelte_element_invalid_this", - "svelte_self_deprecated" + 'a11y_accesskey', + 'a11y_aria_activedescendant_has_tabindex', + 'a11y_aria_attributes', + 'a11y_autocomplete_valid', + 'a11y_autofocus', + 'a11y_click_events_have_key_events', + 'a11y_consider_explicit_label', + 'a11y_distracting_elements', + 'a11y_figcaption_index', + 'a11y_figcaption_parent', + 'a11y_hidden', + 'a11y_img_redundant_alt', + 'a11y_incorrect_aria_attribute_type', + 'a11y_incorrect_aria_attribute_type_boolean', + 'a11y_incorrect_aria_attribute_type_id', + 'a11y_incorrect_aria_attribute_type_idlist', + 'a11y_incorrect_aria_attribute_type_integer', + 'a11y_incorrect_aria_attribute_type_token', + 'a11y_incorrect_aria_attribute_type_tokenlist', + 'a11y_incorrect_aria_attribute_type_tristate', + 'a11y_interactive_supports_focus', + 'a11y_invalid_attribute', + 'a11y_label_has_associated_control', + 'a11y_media_has_caption', + 'a11y_misplaced_role', + 'a11y_misplaced_scope', + 'a11y_missing_attribute', + 'a11y_missing_content', + 'a11y_mouse_events_have_key_events', + 'a11y_no_abstract_role', + 'a11y_no_interactive_element_to_noninteractive_role', + 'a11y_no_noninteractive_element_interactions', + 'a11y_no_noninteractive_element_to_interactive_role', + 'a11y_no_noninteractive_tabindex', + 'a11y_no_redundant_roles', + 'a11y_no_static_element_interactions', + 'a11y_positive_tabindex', + 'a11y_role_has_required_aria_props', + 'a11y_role_supports_aria_props', + 'a11y_role_supports_aria_props_implicit', + 'a11y_unknown_aria_attribute', + 'a11y_unknown_role', + 'legacy_code', + 'unknown_code', + 'options_deprecated_accessors', + 'options_deprecated_immutable', + 'options_missing_custom_element', + 'options_removed_enable_sourcemap', + 'options_removed_hydratable', + 'options_removed_loop_guard_timeout', + 'options_renamed_ssr_dom', + 'export_let_unused', + 'legacy_component_creation', + 'non_reactive_update', + 'perf_avoid_inline_class', + 'perf_avoid_nested_class', + 'reactive_declaration_invalid_placement', + 'reactive_declaration_module_script_dependency', + 'state_referenced_locally', + 'store_rune_conflict', + 'css_unused_selector', + 'attribute_avoid_is', + 'attribute_global_event_reference', + 'attribute_illegal_colon', + 'attribute_invalid_property_name', + 'attribute_quoted', + 'bind_invalid_each_rest', + 'block_empty', + 'component_name_lowercase', + 'element_invalid_self_closing_tag', + 'event_directive_deprecated', + 'node_invalid_placement_ssr', + 'script_context_deprecated', + 'script_unknown_attribute', + 'slot_element_deprecated', + 'svelte_component_deprecated', + 'svelte_element_invalid_this', + 'svelte_self_deprecated' ]; /** @@ -129,7 +129,7 @@ export const codes = [ * @param {null | NodeLike} node */ export function a11y_accesskey(node) { - w(node, "a11y_accesskey", `Avoid using accesskey\nhttps://svelte.dev/e/a11y_accesskey`); + w(node, 'a11y_accesskey', `Avoid using accesskey\nhttps://svelte.dev/e/a11y_accesskey`); } /** @@ -137,7 +137,7 @@ export function a11y_accesskey(node) { * @param {null | NodeLike} node */ export function a11y_aria_activedescendant_has_tabindex(node) { - w(node, "a11y_aria_activedescendant_has_tabindex", `An element with an aria-activedescendant attribute should have a tabindex value\nhttps://svelte.dev/e/a11y_aria_activedescendant_has_tabindex`); + w(node, 'a11y_aria_activedescendant_has_tabindex', `An element with an aria-activedescendant attribute should have a tabindex value\nhttps://svelte.dev/e/a11y_aria_activedescendant_has_tabindex`); } /** @@ -146,7 +146,7 @@ export function a11y_aria_activedescendant_has_tabindex(node) { * @param {string} name */ export function a11y_aria_attributes(node, name) { - w(node, "a11y_aria_attributes", `\`<${name}>\` should not have aria-* attributes\nhttps://svelte.dev/e/a11y_aria_attributes`); + w(node, 'a11y_aria_attributes', `\`<${name}>\` should not have aria-* attributes\nhttps://svelte.dev/e/a11y_aria_attributes`); } /** @@ -156,7 +156,7 @@ export function a11y_aria_attributes(node, name) { * @param {string} type */ export function a11y_autocomplete_valid(node, value, type) { - w(node, "a11y_autocomplete_valid", `'${value}' is an invalid value for 'autocomplete' on \`<input type="${type}">\`\nhttps://svelte.dev/e/a11y_autocomplete_valid`); + w(node, 'a11y_autocomplete_valid', `'${value}' is an invalid value for 'autocomplete' on \`<input type="${type}">\`\nhttps://svelte.dev/e/a11y_autocomplete_valid`); } /** @@ -164,7 +164,7 @@ export function a11y_autocomplete_valid(node, value, type) { * @param {null | NodeLike} node */ export function a11y_autofocus(node) { - w(node, "a11y_autofocus", `Avoid using autofocus\nhttps://svelte.dev/e/a11y_autofocus`); + w(node, 'a11y_autofocus', `Avoid using autofocus\nhttps://svelte.dev/e/a11y_autofocus`); } /** @@ -172,7 +172,7 @@ export function a11y_autofocus(node) { * @param {null | NodeLike} node */ export function a11y_click_events_have_key_events(node) { - w(node, "a11y_click_events_have_key_events", `Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as \`<button type="button">\` or \`<a>\` might be more appropriate\nhttps://svelte.dev/e/a11y_click_events_have_key_events`); + w(node, 'a11y_click_events_have_key_events', `Visible, non-interactive elements with a click event must be accompanied by a keyboard event handler. Consider whether an interactive element such as \`<button type="button">\` or \`<a>\` might be more appropriate\nhttps://svelte.dev/e/a11y_click_events_have_key_events`); } /** @@ -180,7 +180,7 @@ export function a11y_click_events_have_key_events(node) { * @param {null | NodeLike} node */ export function a11y_consider_explicit_label(node) { - w(node, "a11y_consider_explicit_label", `Buttons and links should either contain text or have an \`aria-label\` or \`aria-labelledby\` attribute\nhttps://svelte.dev/e/a11y_consider_explicit_label`); + w(node, 'a11y_consider_explicit_label', `Buttons and links should either contain text or have an \`aria-label\` or \`aria-labelledby\` attribute\nhttps://svelte.dev/e/a11y_consider_explicit_label`); } /** @@ -189,7 +189,7 @@ export function a11y_consider_explicit_label(node) { * @param {string} name */ export function a11y_distracting_elements(node, name) { - w(node, "a11y_distracting_elements", `Avoid \`<${name}>\` elements\nhttps://svelte.dev/e/a11y_distracting_elements`); + w(node, 'a11y_distracting_elements', `Avoid \`<${name}>\` elements\nhttps://svelte.dev/e/a11y_distracting_elements`); } /** @@ -197,7 +197,7 @@ export function a11y_distracting_elements(node, name) { * @param {null | NodeLike} node */ export function a11y_figcaption_index(node) { - w(node, "a11y_figcaption_index", `\`<figcaption>\` must be first or last child of \`<figure>\`\nhttps://svelte.dev/e/a11y_figcaption_index`); + w(node, 'a11y_figcaption_index', `\`<figcaption>\` must be first or last child of \`<figure>\`\nhttps://svelte.dev/e/a11y_figcaption_index`); } /** @@ -205,7 +205,7 @@ export function a11y_figcaption_index(node) { * @param {null | NodeLike} node */ export function a11y_figcaption_parent(node) { - w(node, "a11y_figcaption_parent", `\`<figcaption>\` must be an immediate child of \`<figure>\`\nhttps://svelte.dev/e/a11y_figcaption_parent`); + w(node, 'a11y_figcaption_parent', `\`<figcaption>\` must be an immediate child of \`<figure>\`\nhttps://svelte.dev/e/a11y_figcaption_parent`); } /** @@ -214,7 +214,7 @@ export function a11y_figcaption_parent(node) { * @param {string} name */ export function a11y_hidden(node, name) { - w(node, "a11y_hidden", `\`<${name}>\` element should not be hidden\nhttps://svelte.dev/e/a11y_hidden`); + w(node, 'a11y_hidden', `\`<${name}>\` element should not be hidden\nhttps://svelte.dev/e/a11y_hidden`); } /** @@ -222,7 +222,7 @@ export function a11y_hidden(node, name) { * @param {null | NodeLike} node */ export function a11y_img_redundant_alt(node) { - w(node, "a11y_img_redundant_alt", `Screenreaders already announce \`<img>\` elements as an image\nhttps://svelte.dev/e/a11y_img_redundant_alt`); + w(node, 'a11y_img_redundant_alt', `Screenreaders already announce \`<img>\` elements as an image\nhttps://svelte.dev/e/a11y_img_redundant_alt`); } /** @@ -232,7 +232,7 @@ export function a11y_img_redundant_alt(node) { * @param {string} type */ export function a11y_incorrect_aria_attribute_type(node, attribute, type) { - w(node, "a11y_incorrect_aria_attribute_type", `The value of '${attribute}' must be a ${type}\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type`); + w(node, 'a11y_incorrect_aria_attribute_type', `The value of '${attribute}' must be a ${type}\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type`); } /** @@ -241,7 +241,7 @@ export function a11y_incorrect_aria_attribute_type(node, attribute, type) { * @param {string} attribute */ export function a11y_incorrect_aria_attribute_type_boolean(node, attribute) { - w(node, "a11y_incorrect_aria_attribute_type_boolean", `The value of '${attribute}' must be either 'true' or 'false'. It cannot be empty\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_boolean`); + w(node, 'a11y_incorrect_aria_attribute_type_boolean', `The value of '${attribute}' must be either 'true' or 'false'. It cannot be empty\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_boolean`); } /** @@ -250,7 +250,7 @@ export function a11y_incorrect_aria_attribute_type_boolean(node, attribute) { * @param {string} attribute */ export function a11y_incorrect_aria_attribute_type_id(node, attribute) { - w(node, "a11y_incorrect_aria_attribute_type_id", `The value of '${attribute}' must be a string that represents a DOM element ID\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_id`); + w(node, 'a11y_incorrect_aria_attribute_type_id', `The value of '${attribute}' must be a string that represents a DOM element ID\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_id`); } /** @@ -259,7 +259,7 @@ export function a11y_incorrect_aria_attribute_type_id(node, attribute) { * @param {string} attribute */ export function a11y_incorrect_aria_attribute_type_idlist(node, attribute) { - w(node, "a11y_incorrect_aria_attribute_type_idlist", `The value of '${attribute}' must be a space-separated list of strings that represent DOM element IDs\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_idlist`); + w(node, 'a11y_incorrect_aria_attribute_type_idlist', `The value of '${attribute}' must be a space-separated list of strings that represent DOM element IDs\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_idlist`); } /** @@ -268,7 +268,7 @@ export function a11y_incorrect_aria_attribute_type_idlist(node, attribute) { * @param {string} attribute */ export function a11y_incorrect_aria_attribute_type_integer(node, attribute) { - w(node, "a11y_incorrect_aria_attribute_type_integer", `The value of '${attribute}' must be an integer\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_integer`); + w(node, 'a11y_incorrect_aria_attribute_type_integer', `The value of '${attribute}' must be an integer\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_integer`); } /** @@ -278,7 +278,7 @@ export function a11y_incorrect_aria_attribute_type_integer(node, attribute) { * @param {string} values */ export function a11y_incorrect_aria_attribute_type_token(node, attribute, values) { - w(node, "a11y_incorrect_aria_attribute_type_token", `The value of '${attribute}' must be exactly one of ${values}\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_token`); + w(node, 'a11y_incorrect_aria_attribute_type_token', `The value of '${attribute}' must be exactly one of ${values}\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_token`); } /** @@ -288,7 +288,7 @@ export function a11y_incorrect_aria_attribute_type_token(node, attribute, values * @param {string} values */ export function a11y_incorrect_aria_attribute_type_tokenlist(node, attribute, values) { - w(node, "a11y_incorrect_aria_attribute_type_tokenlist", `The value of '${attribute}' must be a space-separated list of one or more of ${values}\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_tokenlist`); + w(node, 'a11y_incorrect_aria_attribute_type_tokenlist', `The value of '${attribute}' must be a space-separated list of one or more of ${values}\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_tokenlist`); } /** @@ -297,7 +297,7 @@ export function a11y_incorrect_aria_attribute_type_tokenlist(node, attribute, va * @param {string} attribute */ export function a11y_incorrect_aria_attribute_type_tristate(node, attribute) { - w(node, "a11y_incorrect_aria_attribute_type_tristate", `The value of '${attribute}' must be exactly one of true, false, or mixed\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_tristate`); + w(node, 'a11y_incorrect_aria_attribute_type_tristate', `The value of '${attribute}' must be exactly one of true, false, or mixed\nhttps://svelte.dev/e/a11y_incorrect_aria_attribute_type_tristate`); } /** @@ -306,7 +306,7 @@ export function a11y_incorrect_aria_attribute_type_tristate(node, attribute) { * @param {string} role */ export function a11y_interactive_supports_focus(node, role) { - w(node, "a11y_interactive_supports_focus", `Elements with the '${role}' interactive role must have a tabindex value\nhttps://svelte.dev/e/a11y_interactive_supports_focus`); + w(node, 'a11y_interactive_supports_focus', `Elements with the '${role}' interactive role must have a tabindex value\nhttps://svelte.dev/e/a11y_interactive_supports_focus`); } /** @@ -316,7 +316,7 @@ export function a11y_interactive_supports_focus(node, role) { * @param {string} href_attribute */ export function a11y_invalid_attribute(node, href_value, href_attribute) { - w(node, "a11y_invalid_attribute", `'${href_value}' is not a valid ${href_attribute} attribute\nhttps://svelte.dev/e/a11y_invalid_attribute`); + w(node, 'a11y_invalid_attribute', `'${href_value}' is not a valid ${href_attribute} attribute\nhttps://svelte.dev/e/a11y_invalid_attribute`); } /** @@ -324,7 +324,7 @@ export function a11y_invalid_attribute(node, href_value, href_attribute) { * @param {null | NodeLike} node */ export function a11y_label_has_associated_control(node) { - w(node, "a11y_label_has_associated_control", `A form label must be associated with a control\nhttps://svelte.dev/e/a11y_label_has_associated_control`); + w(node, 'a11y_label_has_associated_control', `A form label must be associated with a control\nhttps://svelte.dev/e/a11y_label_has_associated_control`); } /** @@ -332,7 +332,7 @@ export function a11y_label_has_associated_control(node) { * @param {null | NodeLike} node */ export function a11y_media_has_caption(node) { - w(node, "a11y_media_has_caption", `\`<video>\` elements must have a \`<track kind="captions">\`\nhttps://svelte.dev/e/a11y_media_has_caption`); + w(node, 'a11y_media_has_caption', `\`<video>\` elements must have a \`<track kind="captions">\`\nhttps://svelte.dev/e/a11y_media_has_caption`); } /** @@ -341,7 +341,7 @@ export function a11y_media_has_caption(node) { * @param {string} name */ export function a11y_misplaced_role(node, name) { - w(node, "a11y_misplaced_role", `\`<${name}>\` should not have role attribute\nhttps://svelte.dev/e/a11y_misplaced_role`); + w(node, 'a11y_misplaced_role', `\`<${name}>\` should not have role attribute\nhttps://svelte.dev/e/a11y_misplaced_role`); } /** @@ -349,7 +349,7 @@ export function a11y_misplaced_role(node, name) { * @param {null | NodeLike} node */ export function a11y_misplaced_scope(node) { - w(node, "a11y_misplaced_scope", `The scope attribute should only be used with \`<th>\` elements\nhttps://svelte.dev/e/a11y_misplaced_scope`); + w(node, 'a11y_misplaced_scope', `The scope attribute should only be used with \`<th>\` elements\nhttps://svelte.dev/e/a11y_misplaced_scope`); } /** @@ -360,7 +360,7 @@ export function a11y_misplaced_scope(node) { * @param {string} sequence */ export function a11y_missing_attribute(node, name, article, sequence) { - w(node, "a11y_missing_attribute", `\`<${name}>\` element should have ${article} ${sequence} attribute\nhttps://svelte.dev/e/a11y_missing_attribute`); + w(node, 'a11y_missing_attribute', `\`<${name}>\` element should have ${article} ${sequence} attribute\nhttps://svelte.dev/e/a11y_missing_attribute`); } /** @@ -369,7 +369,7 @@ export function a11y_missing_attribute(node, name, article, sequence) { * @param {string} name */ export function a11y_missing_content(node, name) { - w(node, "a11y_missing_content", `\`<${name}>\` element should contain text\nhttps://svelte.dev/e/a11y_missing_content`); + w(node, 'a11y_missing_content', `\`<${name}>\` element should contain text\nhttps://svelte.dev/e/a11y_missing_content`); } /** @@ -379,7 +379,7 @@ export function a11y_missing_content(node, name) { * @param {string} accompanied_by */ export function a11y_mouse_events_have_key_events(node, event, accompanied_by) { - w(node, "a11y_mouse_events_have_key_events", `'${event}' event must be accompanied by '${accompanied_by}' event\nhttps://svelte.dev/e/a11y_mouse_events_have_key_events`); + w(node, 'a11y_mouse_events_have_key_events', `'${event}' event must be accompanied by '${accompanied_by}' event\nhttps://svelte.dev/e/a11y_mouse_events_have_key_events`); } /** @@ -388,7 +388,7 @@ export function a11y_mouse_events_have_key_events(node, event, accompanied_by) { * @param {string} role */ export function a11y_no_abstract_role(node, role) { - w(node, "a11y_no_abstract_role", `Abstract role '${role}' is forbidden\nhttps://svelte.dev/e/a11y_no_abstract_role`); + w(node, 'a11y_no_abstract_role', `Abstract role '${role}' is forbidden\nhttps://svelte.dev/e/a11y_no_abstract_role`); } /** @@ -398,7 +398,7 @@ export function a11y_no_abstract_role(node, role) { * @param {string} role */ export function a11y_no_interactive_element_to_noninteractive_role(node, element, role) { - w(node, "a11y_no_interactive_element_to_noninteractive_role", `\`<${element}>\` cannot have role '${role}'\nhttps://svelte.dev/e/a11y_no_interactive_element_to_noninteractive_role`); + w(node, 'a11y_no_interactive_element_to_noninteractive_role', `\`<${element}>\` cannot have role '${role}'\nhttps://svelte.dev/e/a11y_no_interactive_element_to_noninteractive_role`); } /** @@ -407,7 +407,7 @@ export function a11y_no_interactive_element_to_noninteractive_role(node, element * @param {string} element */ export function a11y_no_noninteractive_element_interactions(node, element) { - w(node, "a11y_no_noninteractive_element_interactions", `Non-interactive element \`<${element}>\` should not be assigned mouse or keyboard event listeners\nhttps://svelte.dev/e/a11y_no_noninteractive_element_interactions`); + w(node, 'a11y_no_noninteractive_element_interactions', `Non-interactive element \`<${element}>\` should not be assigned mouse or keyboard event listeners\nhttps://svelte.dev/e/a11y_no_noninteractive_element_interactions`); } /** @@ -417,7 +417,7 @@ export function a11y_no_noninteractive_element_interactions(node, element) { * @param {string} role */ export function a11y_no_noninteractive_element_to_interactive_role(node, element, role) { - w(node, "a11y_no_noninteractive_element_to_interactive_role", `Non-interactive element \`<${element}>\` cannot have interactive role '${role}'\nhttps://svelte.dev/e/a11y_no_noninteractive_element_to_interactive_role`); + w(node, 'a11y_no_noninteractive_element_to_interactive_role', `Non-interactive element \`<${element}>\` cannot have interactive role '${role}'\nhttps://svelte.dev/e/a11y_no_noninteractive_element_to_interactive_role`); } /** @@ -425,7 +425,7 @@ export function a11y_no_noninteractive_element_to_interactive_role(node, element * @param {null | NodeLike} node */ export function a11y_no_noninteractive_tabindex(node) { - w(node, "a11y_no_noninteractive_tabindex", `noninteractive element cannot have nonnegative tabIndex value\nhttps://svelte.dev/e/a11y_no_noninteractive_tabindex`); + w(node, 'a11y_no_noninteractive_tabindex', `noninteractive element cannot have nonnegative tabIndex value\nhttps://svelte.dev/e/a11y_no_noninteractive_tabindex`); } /** @@ -434,7 +434,7 @@ export function a11y_no_noninteractive_tabindex(node) { * @param {string} role */ export function a11y_no_redundant_roles(node, role) { - w(node, "a11y_no_redundant_roles", `Redundant role '${role}'\nhttps://svelte.dev/e/a11y_no_redundant_roles`); + w(node, 'a11y_no_redundant_roles', `Redundant role '${role}'\nhttps://svelte.dev/e/a11y_no_redundant_roles`); } /** @@ -444,7 +444,7 @@ export function a11y_no_redundant_roles(node, role) { * @param {string} handler */ export function a11y_no_static_element_interactions(node, element, handler) { - w(node, "a11y_no_static_element_interactions", `\`<${element}>\` with a ${handler} handler must have an ARIA role\nhttps://svelte.dev/e/a11y_no_static_element_interactions`); + w(node, 'a11y_no_static_element_interactions', `\`<${element}>\` with a ${handler} handler must have an ARIA role\nhttps://svelte.dev/e/a11y_no_static_element_interactions`); } /** @@ -452,7 +452,7 @@ export function a11y_no_static_element_interactions(node, element, handler) { * @param {null | NodeLike} node */ export function a11y_positive_tabindex(node) { - w(node, "a11y_positive_tabindex", `Avoid tabindex values above zero\nhttps://svelte.dev/e/a11y_positive_tabindex`); + w(node, 'a11y_positive_tabindex', `Avoid tabindex values above zero\nhttps://svelte.dev/e/a11y_positive_tabindex`); } /** @@ -462,7 +462,7 @@ export function a11y_positive_tabindex(node) { * @param {string} props */ export function a11y_role_has_required_aria_props(node, role, props) { - w(node, "a11y_role_has_required_aria_props", `Elements with the ARIA role "${role}" must have the following attributes defined: ${props}\nhttps://svelte.dev/e/a11y_role_has_required_aria_props`); + w(node, 'a11y_role_has_required_aria_props', `Elements with the ARIA role "${role}" must have the following attributes defined: ${props}\nhttps://svelte.dev/e/a11y_role_has_required_aria_props`); } /** @@ -472,7 +472,7 @@ export function a11y_role_has_required_aria_props(node, role, props) { * @param {string} role */ export function a11y_role_supports_aria_props(node, attribute, role) { - w(node, "a11y_role_supports_aria_props", `The attribute '${attribute}' is not supported by the role '${role}'\nhttps://svelte.dev/e/a11y_role_supports_aria_props`); + w(node, 'a11y_role_supports_aria_props', `The attribute '${attribute}' is not supported by the role '${role}'\nhttps://svelte.dev/e/a11y_role_supports_aria_props`); } /** @@ -483,7 +483,7 @@ export function a11y_role_supports_aria_props(node, attribute, role) { * @param {string} name */ export function a11y_role_supports_aria_props_implicit(node, attribute, role, name) { - w(node, "a11y_role_supports_aria_props_implicit", `The attribute '${attribute}' is not supported by the role '${role}'. This role is implicit on the element \`<${name}>\`\nhttps://svelte.dev/e/a11y_role_supports_aria_props_implicit`); + w(node, 'a11y_role_supports_aria_props_implicit', `The attribute '${attribute}' is not supported by the role '${role}'. This role is implicit on the element \`<${name}>\`\nhttps://svelte.dev/e/a11y_role_supports_aria_props_implicit`); } /** @@ -493,7 +493,7 @@ export function a11y_role_supports_aria_props_implicit(node, attribute, role, na * @param {string | undefined | null} [suggestion] */ export function a11y_unknown_aria_attribute(node, attribute, suggestion) { - w(node, "a11y_unknown_aria_attribute", `${suggestion ? `Unknown aria attribute 'aria-${attribute}'. Did you mean '${suggestion}'?` : `Unknown aria attribute 'aria-${attribute}'`}\nhttps://svelte.dev/e/a11y_unknown_aria_attribute`); + w(node, 'a11y_unknown_aria_attribute', `${suggestion ? `Unknown aria attribute 'aria-${attribute}'. Did you mean '${suggestion}'?` : `Unknown aria attribute 'aria-${attribute}'`}\nhttps://svelte.dev/e/a11y_unknown_aria_attribute`); } /** @@ -503,7 +503,7 @@ export function a11y_unknown_aria_attribute(node, attribute, suggestion) { * @param {string | undefined | null} [suggestion] */ export function a11y_unknown_role(node, role, suggestion) { - w(node, "a11y_unknown_role", `${suggestion ? `Unknown role '${role}'. Did you mean '${suggestion}'?` : `Unknown role '${role}'`}\nhttps://svelte.dev/e/a11y_unknown_role`); + w(node, 'a11y_unknown_role', `${suggestion ? `Unknown role '${role}'. Did you mean '${suggestion}'?` : `Unknown role '${role}'`}\nhttps://svelte.dev/e/a11y_unknown_role`); } /** @@ -513,7 +513,7 @@ export function a11y_unknown_role(node, role, suggestion) { * @param {string} suggestion */ export function legacy_code(node, code, suggestion) { - w(node, "legacy_code", `\`${code}\` is no longer valid — please use \`${suggestion}\` instead\nhttps://svelte.dev/e/legacy_code`); + w(node, 'legacy_code', `\`${code}\` is no longer valid — please use \`${suggestion}\` instead\nhttps://svelte.dev/e/legacy_code`); } /** @@ -523,7 +523,7 @@ export function legacy_code(node, code, suggestion) { * @param {string | undefined | null} [suggestion] */ export function unknown_code(node, code, suggestion) { - w(node, "unknown_code", `${suggestion ? `\`${code}\` is not a recognised code (did you mean \`${suggestion}\`?)` : `\`${code}\` is not a recognised code`}\nhttps://svelte.dev/e/unknown_code`); + w(node, 'unknown_code', `${suggestion ? `\`${code}\` is not a recognised code (did you mean \`${suggestion}\`?)` : `\`${code}\` is not a recognised code`}\nhttps://svelte.dev/e/unknown_code`); } /** @@ -531,7 +531,7 @@ export function unknown_code(node, code, suggestion) { * @param {null | NodeLike} node */ export function options_deprecated_accessors(node) { - w(node, "options_deprecated_accessors", `The \`accessors\` option has been deprecated. It will have no effect in runes mode\nhttps://svelte.dev/e/options_deprecated_accessors`); + w(node, 'options_deprecated_accessors', `The \`accessors\` option has been deprecated. It will have no effect in runes mode\nhttps://svelte.dev/e/options_deprecated_accessors`); } /** @@ -539,7 +539,7 @@ export function options_deprecated_accessors(node) { * @param {null | NodeLike} node */ export function options_deprecated_immutable(node) { - w(node, "options_deprecated_immutable", `The \`immutable\` option has been deprecated. It will have no effect in runes mode\nhttps://svelte.dev/e/options_deprecated_immutable`); + w(node, 'options_deprecated_immutable', `The \`immutable\` option has been deprecated. It will have no effect in runes mode\nhttps://svelte.dev/e/options_deprecated_immutable`); } /** @@ -547,7 +547,7 @@ export function options_deprecated_immutable(node) { * @param {null | NodeLike} node */ export function options_missing_custom_element(node) { - w(node, "options_missing_custom_element", `The \`customElement\` option is used when generating a custom element. Did you forget the \`customElement: true\` compile option?\nhttps://svelte.dev/e/options_missing_custom_element`); + w(node, 'options_missing_custom_element', `The \`customElement\` option is used when generating a custom element. Did you forget the \`customElement: true\` compile option?\nhttps://svelte.dev/e/options_missing_custom_element`); } /** @@ -555,7 +555,7 @@ export function options_missing_custom_element(node) { * @param {null | NodeLike} node */ export function options_removed_enable_sourcemap(node) { - w(node, "options_removed_enable_sourcemap", `The \`enableSourcemap\` option has been removed. Source maps are always generated now, and tooling can choose to ignore them\nhttps://svelte.dev/e/options_removed_enable_sourcemap`); + w(node, 'options_removed_enable_sourcemap', `The \`enableSourcemap\` option has been removed. Source maps are always generated now, and tooling can choose to ignore them\nhttps://svelte.dev/e/options_removed_enable_sourcemap`); } /** @@ -563,7 +563,7 @@ export function options_removed_enable_sourcemap(node) { * @param {null | NodeLike} node */ export function options_removed_hydratable(node) { - w(node, "options_removed_hydratable", `The \`hydratable\` option has been removed. Svelte components are always hydratable now\nhttps://svelte.dev/e/options_removed_hydratable`); + w(node, 'options_removed_hydratable', `The \`hydratable\` option has been removed. Svelte components are always hydratable now\nhttps://svelte.dev/e/options_removed_hydratable`); } /** @@ -571,7 +571,7 @@ export function options_removed_hydratable(node) { * @param {null | NodeLike} node */ export function options_removed_loop_guard_timeout(node) { - w(node, "options_removed_loop_guard_timeout", `The \`loopGuardTimeout\` option has been removed\nhttps://svelte.dev/e/options_removed_loop_guard_timeout`); + w(node, 'options_removed_loop_guard_timeout', `The \`loopGuardTimeout\` option has been removed\nhttps://svelte.dev/e/options_removed_loop_guard_timeout`); } /** @@ -579,7 +579,7 @@ export function options_removed_loop_guard_timeout(node) { * @param {null | NodeLike} node */ export function options_renamed_ssr_dom(node) { - w(node, "options_renamed_ssr_dom", `\`generate: "dom"\` and \`generate: "ssr"\` options have been renamed to "client" and "server" respectively\nhttps://svelte.dev/e/options_renamed_ssr_dom`); + w(node, 'options_renamed_ssr_dom', `\`generate: "dom"\` and \`generate: "ssr"\` options have been renamed to "client" and "server" respectively\nhttps://svelte.dev/e/options_renamed_ssr_dom`); } /** @@ -588,7 +588,7 @@ export function options_renamed_ssr_dom(node) { * @param {string} name */ export function export_let_unused(node, name) { - w(node, "export_let_unused", `Component has unused export property '${name}'. If it is for external reference only, please consider using \`export const ${name}\`\nhttps://svelte.dev/e/export_let_unused`); + w(node, 'export_let_unused', `Component has unused export property '${name}'. If it is for external reference only, please consider using \`export const ${name}\`\nhttps://svelte.dev/e/export_let_unused`); } /** @@ -596,7 +596,7 @@ export function export_let_unused(node, name) { * @param {null | NodeLike} node */ export function legacy_component_creation(node) { - w(node, "legacy_component_creation", `Svelte 5 components are no longer classes. Instantiate them using \`mount\` or \`hydrate\` (imported from 'svelte') instead.\nhttps://svelte.dev/e/legacy_component_creation`); + w(node, 'legacy_component_creation', `Svelte 5 components are no longer classes. Instantiate them using \`mount\` or \`hydrate\` (imported from 'svelte') instead.\nhttps://svelte.dev/e/legacy_component_creation`); } /** @@ -605,7 +605,7 @@ export function legacy_component_creation(node) { * @param {string} name */ export function non_reactive_update(node, name) { - w(node, "non_reactive_update", `\`${name}\` is updated, but is not declared with \`$state(...)\`. Changing its value will not correctly trigger updates\nhttps://svelte.dev/e/non_reactive_update`); + w(node, 'non_reactive_update', `\`${name}\` is updated, but is not declared with \`$state(...)\`. Changing its value will not correctly trigger updates\nhttps://svelte.dev/e/non_reactive_update`); } /** @@ -613,7 +613,7 @@ export function non_reactive_update(node, name) { * @param {null | NodeLike} node */ export function perf_avoid_inline_class(node) { - w(node, "perf_avoid_inline_class", `Avoid 'new class' — instead, declare the class at the top level scope\nhttps://svelte.dev/e/perf_avoid_inline_class`); + w(node, 'perf_avoid_inline_class', `Avoid 'new class' — instead, declare the class at the top level scope\nhttps://svelte.dev/e/perf_avoid_inline_class`); } /** @@ -621,7 +621,7 @@ export function perf_avoid_inline_class(node) { * @param {null | NodeLike} node */ export function perf_avoid_nested_class(node) { - w(node, "perf_avoid_nested_class", `Avoid declaring classes below the top level scope\nhttps://svelte.dev/e/perf_avoid_nested_class`); + w(node, 'perf_avoid_nested_class', `Avoid declaring classes below the top level scope\nhttps://svelte.dev/e/perf_avoid_nested_class`); } /** @@ -629,7 +629,7 @@ export function perf_avoid_nested_class(node) { * @param {null | NodeLike} node */ export function reactive_declaration_invalid_placement(node) { - w(node, "reactive_declaration_invalid_placement", `Reactive declarations only exist at the top level of the instance script\nhttps://svelte.dev/e/reactive_declaration_invalid_placement`); + w(node, 'reactive_declaration_invalid_placement', `Reactive declarations only exist at the top level of the instance script\nhttps://svelte.dev/e/reactive_declaration_invalid_placement`); } /** @@ -637,7 +637,7 @@ export function reactive_declaration_invalid_placement(node) { * @param {null | NodeLike} node */ export function reactive_declaration_module_script_dependency(node) { - w(node, "reactive_declaration_module_script_dependency", `Reassignments of module-level declarations will not cause reactive statements to update\nhttps://svelte.dev/e/reactive_declaration_module_script_dependency`); + w(node, 'reactive_declaration_module_script_dependency', `Reassignments of module-level declarations will not cause reactive statements to update\nhttps://svelte.dev/e/reactive_declaration_module_script_dependency`); } /** @@ -645,7 +645,7 @@ export function reactive_declaration_module_script_dependency(node) { * @param {null | NodeLike} node */ export function state_referenced_locally(node) { - w(node, "state_referenced_locally", `State referenced in its own scope will never update. Did you mean to reference it inside a closure?\nhttps://svelte.dev/e/state_referenced_locally`); + w(node, 'state_referenced_locally', `State referenced in its own scope will never update. Did you mean to reference it inside a closure?\nhttps://svelte.dev/e/state_referenced_locally`); } /** @@ -654,7 +654,7 @@ export function state_referenced_locally(node) { * @param {string} name */ export function store_rune_conflict(node, name) { - w(node, "store_rune_conflict", `It looks like you're using the \`$${name}\` rune, but there is a local binding called \`${name}\`. Referencing a local variable with a \`$\` prefix will create a store subscription. Please rename \`${name}\` to avoid the ambiguity\nhttps://svelte.dev/e/store_rune_conflict`); + w(node, 'store_rune_conflict', `It looks like you're using the \`$${name}\` rune, but there is a local binding called \`${name}\`. Referencing a local variable with a \`$\` prefix will create a store subscription. Please rename \`${name}\` to avoid the ambiguity\nhttps://svelte.dev/e/store_rune_conflict`); } /** @@ -663,7 +663,7 @@ export function store_rune_conflict(node, name) { * @param {string} name */ export function css_unused_selector(node, name) { - w(node, "css_unused_selector", `Unused CSS selector "${name}"\nhttps://svelte.dev/e/css_unused_selector`); + w(node, 'css_unused_selector', `Unused CSS selector "${name}"\nhttps://svelte.dev/e/css_unused_selector`); } /** @@ -671,7 +671,7 @@ export function css_unused_selector(node, name) { * @param {null | NodeLike} node */ export function attribute_avoid_is(node) { - w(node, "attribute_avoid_is", `The "is" attribute is not supported cross-browser and should be avoided\nhttps://svelte.dev/e/attribute_avoid_is`); + w(node, 'attribute_avoid_is', `The "is" attribute is not supported cross-browser and should be avoided\nhttps://svelte.dev/e/attribute_avoid_is`); } /** @@ -680,7 +680,7 @@ export function attribute_avoid_is(node) { * @param {string} name */ export function attribute_global_event_reference(node, name) { - w(node, "attribute_global_event_reference", `You are referencing \`globalThis.${name}\`. Did you forget to declare a variable with that name?\nhttps://svelte.dev/e/attribute_global_event_reference`); + w(node, 'attribute_global_event_reference', `You are referencing \`globalThis.${name}\`. Did you forget to declare a variable with that name?\nhttps://svelte.dev/e/attribute_global_event_reference`); } /** @@ -688,7 +688,7 @@ export function attribute_global_event_reference(node, name) { * @param {null | NodeLike} node */ export function attribute_illegal_colon(node) { - w(node, "attribute_illegal_colon", `Attributes should not contain ':' characters to prevent ambiguity with Svelte directives\nhttps://svelte.dev/e/attribute_illegal_colon`); + w(node, 'attribute_illegal_colon', `Attributes should not contain ':' characters to prevent ambiguity with Svelte directives\nhttps://svelte.dev/e/attribute_illegal_colon`); } /** @@ -698,7 +698,7 @@ export function attribute_illegal_colon(node) { * @param {string} right */ export function attribute_invalid_property_name(node, wrong, right) { - w(node, "attribute_invalid_property_name", `'${wrong}' is not a valid HTML attribute. Did you mean '${right}'?\nhttps://svelte.dev/e/attribute_invalid_property_name`); + w(node, 'attribute_invalid_property_name', `'${wrong}' is not a valid HTML attribute. Did you mean '${right}'?\nhttps://svelte.dev/e/attribute_invalid_property_name`); } /** @@ -706,7 +706,7 @@ export function attribute_invalid_property_name(node, wrong, right) { * @param {null | NodeLike} node */ export function attribute_quoted(node) { - w(node, "attribute_quoted", `Quoted attributes on components and custom elements will be stringified in a future version of Svelte. If this isn't what you want, remove the quotes\nhttps://svelte.dev/e/attribute_quoted`); + w(node, 'attribute_quoted', `Quoted attributes on components and custom elements will be stringified in a future version of Svelte. If this isn't what you want, remove the quotes\nhttps://svelte.dev/e/attribute_quoted`); } /** @@ -715,7 +715,7 @@ export function attribute_quoted(node) { * @param {string} name */ export function bind_invalid_each_rest(node, name) { - w(node, "bind_invalid_each_rest", `The rest operator (...) will create a new object and binding '${name}' with the original object will not work\nhttps://svelte.dev/e/bind_invalid_each_rest`); + w(node, 'bind_invalid_each_rest', `The rest operator (...) will create a new object and binding '${name}' with the original object will not work\nhttps://svelte.dev/e/bind_invalid_each_rest`); } /** @@ -723,7 +723,7 @@ export function bind_invalid_each_rest(node, name) { * @param {null | NodeLike} node */ export function block_empty(node) { - w(node, "block_empty", `Empty block\nhttps://svelte.dev/e/block_empty`); + w(node, 'block_empty', `Empty block\nhttps://svelte.dev/e/block_empty`); } /** @@ -732,7 +732,7 @@ export function block_empty(node) { * @param {string} name */ export function component_name_lowercase(node, name) { - w(node, "component_name_lowercase", `\`<${name}>\` will be treated as an HTML element unless it begins with a capital letter\nhttps://svelte.dev/e/component_name_lowercase`); + w(node, 'component_name_lowercase', `\`<${name}>\` will be treated as an HTML element unless it begins with a capital letter\nhttps://svelte.dev/e/component_name_lowercase`); } /** @@ -741,7 +741,7 @@ export function component_name_lowercase(node, name) { * @param {string} name */ export function element_invalid_self_closing_tag(node, name) { - w(node, "element_invalid_self_closing_tag", `Self-closing HTML tags for non-void elements are ambiguous — use \`<${name} ...></${name}>\` rather than \`<${name} ... />\`\nhttps://svelte.dev/e/element_invalid_self_closing_tag`); + w(node, 'element_invalid_self_closing_tag', `Self-closing HTML tags for non-void elements are ambiguous — use \`<${name} ...></${name}>\` rather than \`<${name} ... />\`\nhttps://svelte.dev/e/element_invalid_self_closing_tag`); } /** @@ -750,7 +750,7 @@ export function element_invalid_self_closing_tag(node, name) { * @param {string} name */ export function event_directive_deprecated(node, name) { - w(node, "event_directive_deprecated", `Using \`on:${name}\` to listen to the ${name} event is deprecated. Use the event attribute \`on${name}\` instead\nhttps://svelte.dev/e/event_directive_deprecated`); + w(node, 'event_directive_deprecated', `Using \`on:${name}\` to listen to the ${name} event is deprecated. Use the event attribute \`on${name}\` instead\nhttps://svelte.dev/e/event_directive_deprecated`); } /** @@ -759,7 +759,7 @@ export function event_directive_deprecated(node, name) { * @param {string} message */ export function node_invalid_placement_ssr(node, message) { - w(node, "node_invalid_placement_ssr", `${message}. When rendering this component on the server, the resulting HTML will be modified by the browser (by moving, removing, or inserting elements), likely resulting in a \`hydration_mismatch\` warning\nhttps://svelte.dev/e/node_invalid_placement_ssr`); + w(node, 'node_invalid_placement_ssr', `${message}. When rendering this component on the server, the resulting HTML will be modified by the browser (by moving, removing, or inserting elements), likely resulting in a \`hydration_mismatch\` warning\nhttps://svelte.dev/e/node_invalid_placement_ssr`); } /** @@ -767,7 +767,7 @@ export function node_invalid_placement_ssr(node, message) { * @param {null | NodeLike} node */ export function script_context_deprecated(node) { - w(node, "script_context_deprecated", `\`context="module"\` is deprecated, use the \`module\` attribute instead\nhttps://svelte.dev/e/script_context_deprecated`); + w(node, 'script_context_deprecated', `\`context="module"\` is deprecated, use the \`module\` attribute instead\nhttps://svelte.dev/e/script_context_deprecated`); } /** @@ -775,7 +775,7 @@ export function script_context_deprecated(node) { * @param {null | NodeLike} node */ export function script_unknown_attribute(node) { - w(node, "script_unknown_attribute", `Unrecognized attribute — should be one of \`generics\`, \`lang\` or \`module\`. If this exists for a preprocessor, ensure that the preprocessor removes it\nhttps://svelte.dev/e/script_unknown_attribute`); + w(node, 'script_unknown_attribute', `Unrecognized attribute — should be one of \`generics\`, \`lang\` or \`module\`. If this exists for a preprocessor, ensure that the preprocessor removes it\nhttps://svelte.dev/e/script_unknown_attribute`); } /** @@ -783,7 +783,7 @@ export function script_unknown_attribute(node) { * @param {null | NodeLike} node */ export function slot_element_deprecated(node) { - w(node, "slot_element_deprecated", `Using \`<slot>\` to render parent content is deprecated. Use \`{@render ...}\` tags instead\nhttps://svelte.dev/e/slot_element_deprecated`); + w(node, 'slot_element_deprecated', `Using \`<slot>\` to render parent content is deprecated. Use \`{@render ...}\` tags instead\nhttps://svelte.dev/e/slot_element_deprecated`); } /** @@ -791,7 +791,7 @@ export function slot_element_deprecated(node) { * @param {null | NodeLike} node */ export function svelte_component_deprecated(node) { - w(node, "svelte_component_deprecated", `\`<svelte:component>\` is deprecated in runes mode — components are dynamic by default\nhttps://svelte.dev/e/svelte_component_deprecated`); + w(node, 'svelte_component_deprecated', `\`<svelte:component>\` is deprecated in runes mode — components are dynamic by default\nhttps://svelte.dev/e/svelte_component_deprecated`); } /** @@ -799,7 +799,7 @@ export function svelte_component_deprecated(node) { * @param {null | NodeLike} node */ export function svelte_element_invalid_this(node) { - w(node, "svelte_element_invalid_this", `\`this\` should be an \`{expression}\`. Using a string attribute value will cause an error in future versions of Svelte\nhttps://svelte.dev/e/svelte_element_invalid_this`); + w(node, 'svelte_element_invalid_this', `\`this\` should be an \`{expression}\`. Using a string attribute value will cause an error in future versions of Svelte\nhttps://svelte.dev/e/svelte_element_invalid_this`); } /** @@ -809,5 +809,5 @@ export function svelte_element_invalid_this(node) { * @param {string} basename */ export function svelte_self_deprecated(node, name, basename) { - w(node, "svelte_self_deprecated", `\`<svelte:self>\` is deprecated — use self-imports (e.g. \`import ${name} from './${basename}'\`) instead\nhttps://svelte.dev/e/svelte_self_deprecated`); + w(node, 'svelte_self_deprecated', `\`<svelte:self>\` is deprecated — use self-imports (e.g. \`import ${name} from './${basename}'\`) instead\nhttps://svelte.dev/e/svelte_self_deprecated`); } \ No newline at end of file diff --git a/packages/svelte/src/internal/client/warnings.js b/packages/svelte/src/internal/client/warnings.js index 78d457f48f..250c6eca2f 100644 --- a/packages/svelte/src/internal/client/warnings.js +++ b/packages/svelte/src/internal/client/warnings.js @@ -76,7 +76,7 @@ export function hydration_attribute_changed(attribute, html, value) { */ export function hydration_html_changed(location) { if (DEV) { - console.warn(`%c[svelte] hydration_html_changed\n%c${location ? `The value of an \`{@html ...}\` block ${location} changed between server and client renders. The client value will be ignored in favour of the server value` : "The value of an `{@html ...}` block changed between server and client renders. The client value will be ignored in favour of the server value"}\nhttps://svelte.dev/e/hydration_html_changed`, bold, normal); + console.warn(`%c[svelte] hydration_html_changed\n%c${location ? `The value of an \`{@html ...}\` block ${location} changed between server and client renders. The client value will be ignored in favour of the server value` : 'The value of an `{@html ...}` block changed between server and client renders. The client value will be ignored in favour of the server value'}\nhttps://svelte.dev/e/hydration_html_changed`, bold, normal); } else { console.warn(`https://svelte.dev/e/hydration_html_changed`); } @@ -88,7 +88,7 @@ export function hydration_html_changed(location) { */ export function hydration_mismatch(location) { if (DEV) { - console.warn(`%c[svelte] hydration_mismatch\n%c${location ? `Hydration failed because the initial UI does not match what was rendered on the server. The error occurred near ${location}` : "Hydration failed because the initial UI does not match what was rendered on the server"}\nhttps://svelte.dev/e/hydration_mismatch`, bold, normal); + console.warn(`%c[svelte] hydration_mismatch\n%c${location ? `Hydration failed because the initial UI does not match what was rendered on the server. The error occurred near ${location}` : 'Hydration failed because the initial UI does not match what was rendered on the server'}\nhttps://svelte.dev/e/hydration_mismatch`, bold, normal); } else { console.warn(`https://svelte.dev/e/hydration_mismatch`); } @@ -149,7 +149,7 @@ export function ownership_invalid_binding(parent, child, owner) { */ export function ownership_invalid_mutation(component, owner) { if (DEV) { - console.warn(`%c[svelte] ownership_invalid_mutation\n%c${component ? `${component} mutated a value owned by ${owner}. This is strongly discouraged. Consider passing values to child components with \`bind:\`, or use a callback instead` : "Mutating a value outside the component that created it is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead"}\nhttps://svelte.dev/e/ownership_invalid_mutation`, bold, normal); + console.warn(`%c[svelte] ownership_invalid_mutation\n%c${component ? `${component} mutated a value owned by ${owner}. This is strongly discouraged. Consider passing values to child components with \`bind:\`, or use a callback instead` : 'Mutating a value outside the component that created it is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead'}\nhttps://svelte.dev/e/ownership_invalid_mutation`, bold, normal); } else { console.warn(`https://svelte.dev/e/ownership_invalid_mutation`); } diff --git a/packages/svelte/src/internal/shared/warnings.js b/packages/svelte/src/internal/shared/warnings.js index 83c58c9b14..281be08382 100644 --- a/packages/svelte/src/internal/shared/warnings.js +++ b/packages/svelte/src/internal/shared/warnings.js @@ -29,7 +29,7 @@ export function state_snapshot_uncloneable(properties) { ? `The following properties cannot be cloned with \`$state.snapshot\` — the return value contains the originals: ${properties}` - : "Value cannot be cloned with `$state.snapshot` — the original value was returned"}\nhttps://svelte.dev/e/state_snapshot_uncloneable`, bold, normal); + : 'Value cannot be cloned with `$state.snapshot` — the original value was returned'}\nhttps://svelte.dev/e/state_snapshot_uncloneable`, bold, normal); } else { console.warn(`https://svelte.dev/e/state_snapshot_uncloneable`); } diff --git a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/client/index.svelte.js index 87fce120fd..3e5a12ed9d 100644 --- a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/client/index.svelte.js @@ -1,5 +1,5 @@ -import "svelte/internal/disclose-version"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import * as $ from 'svelte/internal/client'; function increment(_, counter) { counter.count += 1; @@ -26,11 +26,11 @@ export default function Await_block_scope($$anchor) { var text_1 = $.sibling(node); $.template_effect(() => { - $.set_text(text, `clicks: ${counter.count ?? ""}`); - $.set_text(text_1, ` ${counter.count ?? ""}`); + $.set_text(text, `clicks: ${counter.count ?? ''}`); + $.set_text(text_1, ` ${counter.count ?? ''}`); }); $.append($$anchor, fragment); } -$.delegate(["click"]); \ No newline at end of file +$.delegate(['click']); \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js index 44c68a7c96..012789a550 100644 --- a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export default function Await_block_scope($$payload) { let counter = { count: 0 }; diff --git a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js index bcddb6f658..fa990b33ee 100644 --- a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js @@ -1,11 +1,11 @@ -import "svelte/internal/disclose-version"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import * as $ from 'svelte/internal/client'; import TextInput from './Child.svelte'; const snippet = ($$anchor) => { $.next(); - var text = $.text("Something"); + var text = $.text('Something'); $.append($$anchor, text); }; @@ -29,6 +29,6 @@ export default function Bind_component_snippet($$anchor) { var text_1 = $.sibling(node); - $.template_effect(() => $.set_text(text_1, ` value: ${$.get(value) ?? ""}`)); + $.template_effect(() => $.set_text(text_1, ` value: ${$.get(value) ?? ''}`)); $.append($$anchor, fragment); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js index d223a31502..c091179c41 100644 --- a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; import TextInput from './Child.svelte'; function snippet($$payload) { diff --git a/packages/svelte/tests/snapshot/samples/bind-this/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-this/_expected/client/index.svelte.js index fedcc87696..dfd32a04e5 100644 --- a/packages/svelte/tests/snapshot/samples/bind-this/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/bind-this/_expected/client/index.svelte.js @@ -1,6 +1,6 @@ -import "svelte/internal/disclose-version"; -import "svelte/internal/flags/legacy"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import 'svelte/internal/flags/legacy'; +import * as $ from 'svelte/internal/client'; export default function Bind_this($$anchor) { $.bind_this(Foo($$anchor, { $$legacy: true }), ($$value) => foo = $$value, () => foo); diff --git a/packages/svelte/tests/snapshot/samples/bind-this/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-this/_expected/server/index.svelte.js index badca8d4a0..148573766f 100644 --- a/packages/svelte/tests/snapshot/samples/bind-this/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/bind-this/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export default function Bind_this($$payload) { Foo($$payload, {}); diff --git a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js index 399fa19b62..2898f31a6f 100644 --- a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js @@ -1,5 +1,5 @@ -import "svelte/internal/disclose-version"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import * as $ from 'svelte/internal/client'; export default function Class_state_field_constructor_assignment($$anchor, $$props) { $.push($$props, true); diff --git a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/server/index.svelte.js index bcc7c2e1f2..2a115a4983 100644 --- a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export default function Class_state_field_constructor_assignment($$payload, $$props) { $.push(); diff --git a/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js index dbda825c4d..9651713c52 100644 --- a/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js @@ -1,5 +1,5 @@ /* index.svelte.js generated by Svelte VERSION */ -import * as $ from "svelte/internal/client"; +import * as $ from 'svelte/internal/client'; let a = $.state(1); let b = $.state(2); diff --git a/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/server/index.svelte.js index 2797d4312b..62b655b266 100644 --- a/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/server/index.svelte.js @@ -1,5 +1,5 @@ /* index.svelte.js generated by Svelte VERSION */ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; let a = 1; let b = 2; diff --git a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js index 846acc67a6..ce77a27e19 100644 --- a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js +++ b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js @@ -1,5 +1,5 @@ -import "svelte/internal/disclose-version"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import * as $ from 'svelte/internal/client'; var root = $.template(`<div></div> <svg></svg> <custom-element></custom-element> <div></div> <svg></svg> <custom-element></custom-element>`, 3); @@ -13,20 +13,20 @@ export default function Main($$anchor) { var custom_element = $.sibling(svg, 2); var div_1 = $.sibling(custom_element, 2); - $.template_effect(() => $.set_attribute(div_1, "foobar", y())); + $.template_effect(() => $.set_attribute(div_1, 'foobar', y())); var svg_1 = $.sibling(div_1, 2); - $.template_effect(() => $.set_attribute(svg_1, "viewBox", y())); + $.template_effect(() => $.set_attribute(svg_1, 'viewBox', y())); var custom_element_1 = $.sibling(svg_1, 2); - $.template_effect(() => $.set_custom_element_data(custom_element_1, "fooBar", y())); + $.template_effect(() => $.set_custom_element_data(custom_element_1, 'fooBar', y())); $.template_effect(() => { - $.set_attribute(div, "foobar", x); - $.set_attribute(svg, "viewBox", x); - $.set_custom_element_data(custom_element, "fooBar", x); + $.set_attribute(div, 'foobar', x); + $.set_attribute(svg, 'viewBox', x); + $.set_custom_element_data(custom_element, 'fooBar', x); }); $.append($$anchor, fragment); diff --git a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/server/main.svelte.js b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/server/main.svelte.js index 5eaa55aa49..4ea5edb6a0 100644 --- a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/server/main.svelte.js +++ b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/server/main.svelte.js @@ -1,9 +1,9 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export default function Main($$payload) { // needs to be a snapshot test because jsdom does auto-correct the attribute casing let x = 'test'; let y = () => 'test'; - $$payload.out += `<div${$.attr("foobar", x)}></div> <svg${$.attr("viewBox", x)}></svg> <custom-element${$.attr("foobar", x)}></custom-element> <div${$.attr("foobar", y())}></div> <svg${$.attr("viewBox", y())}></svg> <custom-element${$.attr("foobar", y())}></custom-element>`; + $$payload.out += `<div${$.attr('foobar', x)}></div> <svg${$.attr('viewBox', x)}></svg> <custom-element${$.attr('foobar', x)}></custom-element> <div${$.attr('foobar', y())}></div> <svg${$.attr('viewBox', y())}></svg> <custom-element${$.attr('foobar', y())}></custom-element>`; } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/client/index.svelte.js index 80f2da11a2..c0626bd416 100644 --- a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/client/index.svelte.js @@ -1,6 +1,6 @@ -import "svelte/internal/disclose-version"; -import "svelte/internal/flags/legacy"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import 'svelte/internal/flags/legacy'; +import * as $ from 'svelte/internal/client'; export default function Each_string_template($$anchor) { var fragment = $.comment(); @@ -11,7 +11,7 @@ export default function Each_string_template($$anchor) { var text = $.text(); - $.template_effect(() => $.set_text(text, `${thing ?? ""}, `)); + $.template_effect(() => $.set_text(text, `${thing ?? ''}, `)); $.append($$anchor, text); }); diff --git a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js index d4debe1727..4386c22ebe 100644 --- a/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/each-string-template/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export default function Each_string_template($$payload) { const each_array = $.ensure_array_like(['foo', 'bar', 'baz']); diff --git a/packages/svelte/tests/snapshot/samples/export-state/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/export-state/_expected/client/index.svelte.js index bab47c8c50..c2a6054bc6 100644 --- a/packages/svelte/tests/snapshot/samples/export-state/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/export-state/_expected/client/index.svelte.js @@ -1,4 +1,4 @@ /* index.svelte.js generated by Svelte VERSION */ -import * as $ from "svelte/internal/client"; +import * as $ from 'svelte/internal/client'; export const object = $.proxy({ ok: true }); \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/export-state/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/export-state/_expected/server/index.svelte.js index a3b619df6e..1f6c244212 100644 --- a/packages/svelte/tests/snapshot/samples/export-state/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/export-state/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ /* index.svelte.js generated by Svelte VERSION */ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export const object = { ok: true }; \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js index 95d1c72017..c545608bca 100644 --- a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js @@ -1,5 +1,5 @@ -import "svelte/internal/disclose-version"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import * as $ from 'svelte/internal/client'; export default function Function_prop_no_getter($$anchor) { let count = $.state(0); @@ -19,7 +19,7 @@ export default function Function_prop_no_getter($$anchor) { var text = $.text(); - $.template_effect(() => $.set_text(text, `clicks: ${$.get(count) ?? ""}`)); + $.template_effect(() => $.set_text(text, `clicks: ${$.get(count) ?? ''}`)); $.append($$anchor, text); }, $$slots: { default: true } diff --git a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js index 05b343d821..88f6f55ee7 100644 --- a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export default function Function_prop_no_getter($$payload) { let count = 0; diff --git a/packages/svelte/tests/snapshot/samples/hello-world/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/hello-world/_expected/client/index.svelte.js index 9f6f291669..899c126001 100644 --- a/packages/svelte/tests/snapshot/samples/hello-world/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/hello-world/_expected/client/index.svelte.js @@ -1,6 +1,6 @@ -import "svelte/internal/disclose-version"; -import "svelte/internal/flags/legacy"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import 'svelte/internal/flags/legacy'; +import * as $ from 'svelte/internal/client'; var root = $.template(`<h1>hello world</h1>`); diff --git a/packages/svelte/tests/snapshot/samples/hello-world/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/hello-world/_expected/server/index.svelte.js index a313799dfd..8766fb1300 100644 --- a/packages/svelte/tests/snapshot/samples/hello-world/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/hello-world/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export default function Hello_world($$payload) { $$payload.out += `<h1>hello world</h1>`; diff --git a/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js index 86c2880abc..3c8322500b 100644 --- a/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js @@ -1,6 +1,6 @@ -import "svelte/internal/disclose-version"; -import "svelte/internal/flags/legacy"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import 'svelte/internal/flags/legacy'; +import * as $ from 'svelte/internal/client'; var root = $.template(`<h1>hello world</h1>`); diff --git a/packages/svelte/tests/snapshot/samples/hmr/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/hmr/_expected/server/index.svelte.js index 33fe307c09..959e0a403e 100644 --- a/packages/svelte/tests/snapshot/samples/hmr/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/hmr/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export default function Hmr($$payload) { $$payload.out += `<h1>hello world</h1>`; diff --git a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/index.svelte.js index 9c7d2f3f23..ebbe191dcb 100644 --- a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/index.svelte.js @@ -1,6 +1,6 @@ -import "svelte/internal/disclose-version"; -import "svelte/internal/flags/legacy"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import 'svelte/internal/flags/legacy'; +import * as $ from 'svelte/internal/client'; import { random } from './module.svelte'; export default function Imports_in_modules($$anchor) { diff --git a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/module.svelte.js b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/module.svelte.js index 6edbc8af77..0d366e6258 100644 --- a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/module.svelte.js +++ b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/client/module.svelte.js @@ -1,5 +1,5 @@ /* module.svelte.js generated by Svelte VERSION */ -import * as $ from "svelte/internal/client"; +import * as $ from 'svelte/internal/client'; import { random } from './export'; export { random }; \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/index.svelte.js index 35b64b4186..4cd6bc59d7 100644 --- a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; import { random } from './module.svelte'; export default function Imports_in_modules($$payload) { diff --git a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/module.svelte.js b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/module.svelte.js index e51aae5a25..2e0af8af84 100644 --- a/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/module.svelte.js +++ b/packages/svelte/tests/snapshot/samples/imports-in-modules/_expected/server/module.svelte.js @@ -1,5 +1,5 @@ /* module.svelte.js generated by Svelte VERSION */ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; import { random } from './export'; export { random }; \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.js index 2a10dbc1b1..5a46b9bbef 100644 --- a/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/props-identifier/_expected/client/index.svelte.js @@ -1,10 +1,10 @@ -import "svelte/internal/disclose-version"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import * as $ from 'svelte/internal/client'; export default function Props_identifier($$anchor, $$props) { $.push($$props, true); - let props = $.rest_props($$props, ["$$slots", "$$events", "$$legacy"]); + let props = $.rest_props($$props, ['$$slots', '$$events', '$$legacy']); $$props.a; props[a]; diff --git a/packages/svelte/tests/snapshot/samples/props-identifier/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/props-identifier/_expected/server/index.svelte.js index 6fef87d63b..33a3633939 100644 --- a/packages/svelte/tests/snapshot/samples/props-identifier/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/props-identifier/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export default function Props_identifier($$payload, $$props) { $.push(); diff --git a/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js index 0a627a55ae..940ed8f9e8 100644 --- a/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js @@ -1,6 +1,6 @@ -import "svelte/internal/disclose-version"; -import "svelte/internal/flags/legacy"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import 'svelte/internal/flags/legacy'; +import * as $ from 'svelte/internal/client'; var root = $.template(`<p></p> <p></p> <!>`, 1); diff --git a/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js index 1eea71e4dd..588332407a 100644 --- a/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export default function Purity($$payload) { $$payload.out += `<p>${$.escape(Math.max(0, Math.min(0, 100)))}</p> <p>${$.escape(location.href)}</p> `; diff --git a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js index 8dd7df82cf..9b203b97e8 100644 --- a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js @@ -1,5 +1,5 @@ -import "svelte/internal/disclose-version"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import * as $ from 'svelte/internal/client'; var root = $.template(`<header><nav><a href="/">Home</a> <a href="/away">Away</a></nav></header> <main><h1> </h1> <div class="static"><p>we don't need to traverse these nodes</p></div> <p>or</p> <p>these</p> <p>ones</p> <!> <p>these</p> <p>trailing</p> <p>nodes</p> <p>can</p> <p>be</p> <p>completely</p> <p>ignored</p></main> <cant-skip><custom-elements></custom-elements></cant-skip> <div><input></div> <div><source></div> <select><option>a</option></select> <img src="..." alt="" loading="lazy"> <div><img src="..." alt="" loading="lazy"></div>`, 3); @@ -20,7 +20,7 @@ export default function Skip_static_subtree($$anchor, $$props) { var cant_skip = $.sibling(main, 2); var custom_elements = $.child(cant_skip); - $.set_custom_element_data(custom_elements, "with", "attributes"); + $.set_custom_element_data(custom_elements, 'with', 'attributes'); $.reset(cant_skip); var div = $.sibling(cant_skip, 2); @@ -38,7 +38,7 @@ export default function Skip_static_subtree($$anchor, $$props) { var select = $.sibling(div_1, 2); var option = $.child(select); - option.value = null == (option.__value = "a") ? "" : "a"; + option.value = null == (option.__value = 'a') ? '' : 'a'; $.reset(select); var img = $.sibling(select, 2); diff --git a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js index 309f5a2b57..e694c12647 100644 --- a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export default function Skip_static_subtree($$payload, $$props) { let { title, content } = $$props; diff --git a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js index c24023c8a4..a67210e541 100644 --- a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js @@ -1,5 +1,5 @@ -import "svelte/internal/disclose-version"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import * as $ from 'svelte/internal/client'; function reset(_, str, tpl) { $.set(str, ''); @@ -30,4 +30,4 @@ export default function State_proxy_literal($$anchor) { $.append($$anchor, fragment); } -$.delegate(["click"]); \ No newline at end of file +$.delegate(['click']); \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js index 3424f807ab..7b2a884d70 100644 --- a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export default function State_proxy_literal($$payload) { let str = ''; @@ -11,5 +11,5 @@ export default function State_proxy_literal($$payload) { tpl = ``; } - $$payload.out += `<input${$.attr("value", str)}> <input${$.attr("value", tpl)}> <button>reset</button>`; + $$payload.out += `<input${$.attr('value', str)}> <input${$.attr('value', tpl)}> <button>reset</button>`; } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js index a4bbea582b..2270005ee0 100644 --- a/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/svelte-element/_expected/client/index.svelte.js @@ -1,8 +1,8 @@ -import "svelte/internal/disclose-version"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import * as $ from 'svelte/internal/client'; export default function Svelte_element($$anchor, $$props) { - let tag = $.prop($$props, "tag", 3, 'hr'); + let tag = $.prop($$props, 'tag', 3, 'hr'); var fragment = $.comment(); var node = $.first_child(fragment); diff --git a/packages/svelte/tests/snapshot/samples/svelte-element/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/svelte-element/_expected/server/index.svelte.js index 4a766f7de5..4426ad1164 100644 --- a/packages/svelte/tests/snapshot/samples/svelte-element/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/svelte-element/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export default function Svelte_element($$payload, $$props) { let { tag = 'hr' } = $$props; diff --git a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js index 8ba14526aa..8e343fcf36 100644 --- a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js @@ -1,5 +1,5 @@ -import "svelte/internal/disclose-version"; -import * as $ from "svelte/internal/client"; +import 'svelte/internal/disclose-version'; +import * as $ from 'svelte/internal/client'; var root = $.template(`<p> </p>`); @@ -16,8 +16,8 @@ export default function Text_nodes_deriveds($$anchor) { } var p = root(); - const stringified_text = $.derived(() => text1() ?? ""); - const stringified_text_1 = $.derived(() => text2() ?? ""); + const stringified_text = $.derived(() => text1() ?? ''); + const stringified_text_1 = $.derived(() => text2() ?? ''); var text = $.child(p); $.template_effect(() => $.set_text(text, `${$.get(stringified_text)}${$.get(stringified_text_1)}`)); diff --git a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/server/index.svelte.js index f7dc4176e4..6f019647f5 100644 --- a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/server/index.svelte.js @@ -1,4 +1,4 @@ -import * as $ from "svelte/internal/server"; +import * as $ from 'svelte/internal/server'; export default function Text_nodes_deriveds($$payload) { let count1 = 0; diff --git a/packages/svelte/tests/sourcemaps/samples/css-injected-map/_config.js b/packages/svelte/tests/sourcemaps/samples/css-injected-map/_config.js index 74a2185076..c2b2fe766a 100644 --- a/packages/svelte/tests/sourcemaps/samples/css-injected-map/_config.js +++ b/packages/svelte/tests/sourcemaps/samples/css-injected-map/_config.js @@ -30,7 +30,7 @@ export default test({ async test({ assert, code_client }) { // Check that the css source map embedded in the js is accurate const match = code_client.match( - /code: "(.*?)(?:\\n\/\*# sourceMappingURL=data:(.*?);charset=(.*?);base64,(.*?) \*\/)?"/ + /code: '(.*?)(?:\\n\/\*# sourceMappingURL=data:(.*?);charset=(.*?);base64,(.*?) \*\/)?'/ ); assert.ok(match); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8bf7bb4224..731bb97edb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -87,8 +87,8 @@ importers: specifier: ^1.2.1 version: 1.2.1 esrap: - specifier: ^1.3.2 - version: 1.3.2 + specifier: ^1.4.2 + version: 1.4.2 is-reference: specifier: ^3.0.3 version: 3.0.3 @@ -1118,8 +1118,8 @@ packages: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} - esrap@1.3.2: - resolution: {integrity: sha512-C4PXusxYhFT98GjLSmb20k9PREuUdporer50dhzGuJu9IJXktbMddVCMLAERl5dAHyAi73GWWCE4FVHGP1794g==} + esrap@1.4.2: + resolution: {integrity: sha512-FhVlJzvTw7ZLxYZ7RyHwQCFE64dkkpzGNNnphaGCLwjqGk1SQcqzbgdx9FowPCktx6NOSHkzvcZ3vsvdH54YXA==} esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} @@ -3324,7 +3324,7 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@1.3.2: + esrap@1.4.2: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 From ab3290fa870838a1f8c8baddc5d093dfa3265280 Mon Sep 17 00:00:00 2001 From: eyalkutz <45904301+eyalkutz@users.noreply.github.com> Date: Sun, 12 Jan 2025 20:52:27 +0200 Subject: [PATCH 0083/1429] Remove out-of-date note from CONTRIBUTING.md (#14986) --- CONTRIBUTING.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dd7bbb476e..f7d15f905e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,8 +62,6 @@ When [opening a new issue](https://github.com/sveltejs/svelte/issues/new/choose) ## Pull requests -> HEADS UP: Svelte 5 will likely change a lot on the compiler. For that reason, please don't open PRs that are large in scope, touch more than a couple of files etc. In other words, bug fixes are fine, but big feature PRs will likely not be merged. - ### Proposing a change If you would like to request a new feature or enhancement but are not yet thinking about opening a pull request, you can also file an issue with [feature template](https://github.com/sveltejs/svelte/issues/new?template=feature_request.yml). From 36ef59df1f35635d53947b357d4211f3bf1f713a Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Mon, 13 Jan 2025 21:14:07 +0000 Subject: [PATCH 0084/1429] chore: improve test cases (#14984) * chore: improve test cases * lint --- .../camel-case-attribute/_config.js | 7 ++-- .../custom-method/_config.js | 4 ++- .../element-effect-context/_config.js | 3 +- .../_config.js | 6 ++-- .../_config.js | 6 ++-- .../_config.js | 6 ++-- .../_config.js | 6 ++-- .../action-custom-event-handler/_config.js | 6 ++-- .../samples/action-this/_config.js | 4 ++- .../samples/await-mutate-array/_config.js | 5 ++- .../await-then-destruct-object-if/_config.js | 3 ++ .../samples/before-render-chain/_config.js | 6 ++-- .../_config.js | 6 ++-- .../binding-contenteditable-text/_config.js | 6 ++-- .../_config.js | 6 ++-- .../binding-select-in-yield/_config.js | 15 +++++---- .../samples/bindings-coalesced/_config.js | 6 ++-- .../component-props-mutated/_config.js | 6 ++-- .../samples/component-slot-let-g/_config.js | 6 ++-- .../samples/destructured-props-2/_config.js | 3 +- .../samples/destructured-props-3/_config.js | 4 ++- .../samples/destructured-props-5/_config.js | 3 +- .../destructuring-assignment-array/_config.js | 7 ++-- .../samples/key-block-expression-2/_config.js | 15 ++++++--- .../samples/prop-const/_config.js | 6 ++-- .../samples/props-reactive-b/_config.js | 9 +++-- .../_config.js | 6 ++-- .../_config.js | 33 +++++++++++-------- .../reactive-value-function/_config.js | 6 ++-- .../reactive-values-text-node/_config.js | 3 ++ .../samples/spread-own-props/_config.js | 6 ++-- .../_config.js | 8 ++--- .../_config.js | 7 ++-- .../samples/store-unreferenced/_config.js | 6 ++-- .../samples/window-event-custom/_config.js | 6 ++-- .../samples/derived-unowned-2/_config.js | 3 +- .../samples/derived-unowned-5/_config.js | 3 +- .../samples/runes-from-func/_config.js | 3 +- 38 files changed, 164 insertions(+), 86 deletions(-) diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/camel-case-attribute/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/camel-case-attribute/_config.js index ba3be3c25b..b9bfdd7782 100644 --- a/packages/svelte/tests/runtime-browser/custom-elements-samples/camel-case-attribute/_config.js +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/camel-case-attribute/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../assert'; const tick = () => Promise.resolve(); @@ -14,8 +15,7 @@ export default test({ el.setAttribute('camel-case', 'universe'); el.setAttribute('an-array', '[3,4]'); el.setAttribute('camelcase2', 'Hi'); - await tick(); - await tick(); + flushSync(); assert.htmlEqual(el.shadowRoot.innerHTML, '<h1>Hi universe!</h1> <p>3</p><p>4</p>'); assert.htmlEqual( target.innerHTML, @@ -25,8 +25,7 @@ export default test({ el.camelCase = 'galaxy'; el.camelCase2 = 'Hey'; el.anArray = [5, 6]; - await tick(); - await tick(); + flushSync(); assert.htmlEqual(el.shadowRoot.innerHTML, '<h1>Hey galaxy!</h1> <p>5</p><p>6</p>'); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/custom-method/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/custom-method/_config.js index dbdf006be5..4314926a94 100644 --- a/packages/svelte/tests/runtime-browser/custom-elements-samples/custom-method/_config.js +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/custom-method/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../assert'; const tick = () => Promise.resolve(); @@ -8,7 +9,8 @@ export default test({ /** @type {any} */ const el = target.querySelector('custom-element'); - await el.updateFoo(42); + el.updateFoo(42); + flushSync(); const p = el.shadowRoot.querySelector('p'); assert.equal(p.textContent, '42'); diff --git a/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js index 3cf7a66df1..7f2ba9f331 100644 --- a/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js +++ b/packages/svelte/tests/runtime-browser/custom-elements-samples/element-effect-context/_config.js @@ -1,4 +1,5 @@ import { test } from '../../assert'; +import { flushSync } from 'svelte'; const tick = () => Promise.resolve(); export default test({ @@ -16,7 +17,7 @@ export default test({ assert.equal(p.innerHTML, 'false'); button.click(); - await tick(); + flushSync(); assert.equal(button.innerHTML, '1'); assert.equal(p.innerHTML, 'false'); diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each-destructured/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each-destructured/_config.js index 650d0ec360..fe13d43bc8 100644 --- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each-destructured/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each-destructured/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,12 +11,13 @@ export default test({ <p>second: </p> `, - async test({ assert, component, target, window }) { + test({ assert, component, target, window }) { const event = new window.MouseEvent('click'); const buttons = target.querySelectorAll('button'); - await buttons[1].dispatchEvent(event); + buttons[1].dispatchEvent(event); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each/_config.js index 21fb167873..83b14cb8a3 100644 --- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-in-each/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,12 +11,13 @@ export default test({ <p>fromState: </p> `, - async test({ assert, component, target, window }) { + test({ assert, component, target, window }) { const event = new window.MouseEvent('click'); const buttons = target.querySelectorAll('button'); - await buttons[1].dispatchEvent(event); + buttons[1].dispatchEvent(event); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-node-context/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-node-context/_config.js index a2edd90f8b..9276ca8a59 100644 --- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-node-context/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-node-context/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ @@ -5,13 +6,14 @@ export default test({ html: '<button>10</button>', - async test({ assert, target, window }) { + test({ assert, target, window }) { const event = new window.MouseEvent('click'); const button = target.querySelector('button'); ok(button); - await button.dispatchEvent(event); + button.dispatchEvent(event); + flushSync(); assert.htmlEqual(target.innerHTML, '<button>11</button>'); } diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-with-context/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-with-context/_config.js index c4d84fc2b3..2e6aa4dfee 100644 --- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-with-context/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler-with-context/_config.js @@ -1,9 +1,10 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ html: '<button>???</button>', - async test({ assert, target, window }) { + test({ assert, target, window }) { const event = new window.MouseEvent('click', { clientX: 42, clientY: 42 @@ -12,7 +13,8 @@ export default test({ const button = target.querySelector('button'); ok(button); - await button.dispatchEvent(event); + button.dispatchEvent(event); + flushSync(); assert.htmlEqual(target.innerHTML, '<button>42</button>'); } diff --git a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler/_config.js index 279171692d..3769a8bd81 100644 --- a/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/action-custom-event-handler/_config.js @@ -1,9 +1,10 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ html: '<button>0, 0</button>', - async test({ assert, target, window }) { + test({ assert, target, window }) { const event = new window.MouseEvent('click', { clientX: 42, clientY: 42 @@ -12,7 +13,8 @@ export default test({ const button = target.querySelector('button'); ok(button); - await button.dispatchEvent(event); + button.dispatchEvent(event); + flushSync(); assert.htmlEqual(target.innerHTML, '<button>42, 42</button>'); } diff --git a/packages/svelte/tests/runtime-legacy/samples/action-this/_config.js b/packages/svelte/tests/runtime-legacy/samples/action-this/_config.js index d296fc5a28..0ab9305ade 100644 --- a/packages/svelte/tests/runtime-legacy/samples/action-this/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/action-this/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ @@ -8,7 +9,8 @@ export default test({ const click = new window.MouseEvent('click'); assert.htmlEqual(target.innerHTML, '<button>1</button>'); - await button.dispatchEvent(click); + button.dispatchEvent(click); + flushSync(); assert.htmlEqual(target.innerHTML, '<button>2</button>'); } }); diff --git a/packages/svelte/tests/runtime-legacy/samples/await-mutate-array/_config.js b/packages/svelte/tests/runtime-legacy/samples/await-mutate-array/_config.js index 67ea2cb0a3..70268a0e1d 100644 --- a/packages/svelte/tests/runtime-legacy/samples/await-mutate-array/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/await-mutate-array/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -11,7 +12,9 @@ export default test({ const [b1] = target.querySelectorAll('button'); b1.click(); - await Promise.resolve(); + Promise.resolve(); + flushSync(); + assert.htmlEqual( target.innerHTML, `<button>2</button><button>3</button><button>4</button>\n-------\n<button>1</button>` diff --git a/packages/svelte/tests/runtime-legacy/samples/await-then-destruct-object-if/_config.js b/packages/svelte/tests/runtime-legacy/samples/await-then-destruct-object-if/_config.js index e823c21c9a..af04467749 100644 --- a/packages/svelte/tests/runtime-legacy/samples/await-then-destruct-object-if/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/await-then-destruct-object-if/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -11,6 +12,7 @@ export default test({ async test({ assert, component, target }) { await (component.thePromise = Promise.resolve({ result: 1 })); + flushSync(); assert.htmlEqual( target.innerHTML, @@ -21,6 +23,7 @@ export default test({ ); await new Promise((resolve) => setTimeout(resolve, 1)); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/before-render-chain/_config.js b/packages/svelte/tests/runtime-legacy/samples/before-render-chain/_config.js index 9364bd9f9e..580df5ca81 100644 --- a/packages/svelte/tests/runtime-legacy/samples/before-render-chain/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/before-render-chain/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -9,8 +10,9 @@ export default test({ <span>1</span> `, - async test({ assert, component, target }) { - await component.list.update(); + test({ assert, component, target }) { + component.list.update(); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text-initial/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text-initial/_config.js index e2a28e8dd9..83ad9c2558 100644 --- a/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text-initial/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text-initial/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ @@ -18,7 +19,7 @@ export default test({ <p>hello</p> `, - async test({ assert, component, target, window }) { + test({ assert, component, target, window }) { assert.equal(component.name, 'world'); const el = target.querySelector('editor'); @@ -27,7 +28,8 @@ export default test({ const event = new window.Event('input'); el.textContent = 'everybody'; - await el.dispatchEvent(event); + el.dispatchEvent(event); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text/_config.js index afecc5b3cd..494c338bfe 100644 --- a/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/binding-contenteditable-text/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ @@ -10,7 +11,7 @@ export default test({ <p>hello world</p> `, - async test({ assert, component, target, window }) { + test({ assert, component, target, window }) { const el = target.querySelector('editor'); ok(el); assert.equal(el.textContent, 'world'); @@ -18,7 +19,8 @@ export default test({ const event = new window.Event('input'); el.textContent = 'everybody'; - await el.dispatchEvent(event); + el.dispatchEvent(event); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-indeterminate/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-indeterminate/_config.js index 631423feaf..eba77322a1 100644 --- a/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-indeterminate/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/binding-input-checkbox-indeterminate/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ @@ -17,7 +18,7 @@ export default test({ <p>indeterminate? true</p> `, - async test({ assert, component, target, window }) { + test({ assert, component, target, window }) { const input = target.querySelector('input'); ok(input); @@ -28,7 +29,8 @@ export default test({ input.checked = true; input.indeterminate = false; - await input.dispatchEvent(event); + input.dispatchEvent(event); + flushSync(); assert.equal(component.indeterminate, false); assert.equal(component.checked, true); diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-select-in-yield/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-select-in-yield/_config.js index f7e5445072..b8f6a9a74a 100644 --- a/packages/svelte/tests/runtime-legacy/samples/binding-select-in-yield/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/binding-select-in-yield/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { ok, test } from '../../test'; export default test({ @@ -7,8 +8,9 @@ export default test({ return { letter: 'b' }; }, - async test({ assert, component, target, window }) { - await component.modal.toggle(); + test({ assert, component, target, window }) { + component.modal.toggle(); + flushSync(); assert.htmlEqual( target.innerHTML, @@ -28,7 +30,8 @@ export default test({ const change = new window.MouseEvent('change'); select.options[2].selected = true; - await select.dispatchEvent(change); + select.dispatchEvent(change); + flushSync(); assert.equal(component.letter, 'c'); assert.deepEqual( @@ -49,9 +52,9 @@ export default test({ ` ); - await component.modal.toggle(); - await component.modal.toggle(); - await Promise.resolve(); + component.modal.toggle(); + component.modal.toggle(); + flushSync(); select = target.querySelector('select'); ok(select); diff --git a/packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/_config.js b/packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/_config.js index 85e1faeb73..3bc5d845a1 100644 --- a/packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/bindings-coalesced/_config.js @@ -1,7 +1,8 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ - async test({ assert, component }) { + test({ assert, component }) { const { foo, p } = component; /** @type {string[]} */ @@ -13,7 +14,8 @@ export default test({ } }); - await foo.double(); + foo.double(); + flushSync(); assert.deepEqual(values, ['6']); } diff --git a/packages/svelte/tests/runtime-legacy/samples/component-props-mutated/_config.js b/packages/svelte/tests/runtime-legacy/samples/component-props-mutated/_config.js index 91d0aaa0d3..4a5c102ba2 100644 --- a/packages/svelte/tests/runtime-legacy/samples/component-props-mutated/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/component-props-mutated/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; const data = { @@ -15,9 +16,10 @@ export default test({ html: '<p>hello</p>', - async test({ assert, component, target }) { + test({ assert, component, target }) { data.message = 'goodbye'; - await component.$set({ data }); + component.$set({ data }); + flushSync(); assert.htmlEqual(target.innerHTML, '<p>goodbye</p>'); } diff --git a/packages/svelte/tests/runtime-legacy/samples/component-slot-let-g/_config.js b/packages/svelte/tests/runtime-legacy/samples/component-slot-let-g/_config.js index db9afae1b0..f3f5e04be1 100644 --- a/packages/svelte/tests/runtime-legacy/samples/component-slot-let-g/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/component-slot-let-g/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -5,7 +6,7 @@ export default test({ <span slot="foo" class="1">1</span> 0 `, - async test({ assert, target, component, window }) { + test({ assert, target, component, window }) { component.x = 2; assert.htmlEqual( @@ -17,7 +18,8 @@ export default test({ ); const span = target.querySelector('span'); - await span?.dispatchEvent(new window.MouseEvent('click', { bubbles: true })); + span?.dispatchEvent(new window.MouseEvent('click', { bubbles: true })); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/destructured-props-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/destructured-props-2/_config.js index 950ee4f1ff..0f3fcedce5 100644 --- a/packages/svelte/tests/runtime-legacy/samples/destructured-props-2/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/destructured-props-2/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,7 +11,7 @@ export default test({ async test({ component, assert, target }) { await component.update(); - await Promise.resolve(); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/destructured-props-3/_config.js b/packages/svelte/tests/runtime-legacy/samples/destructured-props-3/_config.js index 9ec2810e71..c78b84ec8c 100644 --- a/packages/svelte/tests/runtime-legacy/samples/destructured-props-3/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/destructured-props-3/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -8,7 +9,8 @@ export default test({ `, async test({ component, target, assert }) { await component.update(); - await Promise.resolve(); + flushSync(); + assert.htmlEqual( target.innerHTML, ` diff --git a/packages/svelte/tests/runtime-legacy/samples/destructured-props-5/_config.js b/packages/svelte/tests/runtime-legacy/samples/destructured-props-5/_config.js index 8f7a544bfe..103ce64d7c 100644 --- a/packages/svelte/tests/runtime-legacy/samples/destructured-props-5/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/destructured-props-5/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,7 +11,7 @@ export default test({ async test({ component, assert, target }) { await component.update(); - await Promise.resolve(); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/destructuring-assignment-array/_config.js b/packages/svelte/tests/runtime-legacy/samples/destructuring-assignment-array/_config.js index 2df8753114..9c09581097 100644 --- a/packages/svelte/tests/runtime-legacy/samples/destructuring-assignment-array/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/destructuring-assignment-array/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,9 +11,9 @@ export default test({ </ul> `, - async test({ assert, component, target }) { - await component.swap(0, 1); - await Promise.resolve(); + test({ assert, component, target }) { + component.swap(0, 1); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/key-block-expression-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/key-block-expression-2/_config.js index a661ec2596..99fa84e2a9 100644 --- a/packages/svelte/tests/runtime-legacy/samples/key-block-expression-2/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/key-block-expression-2/_config.js @@ -1,19 +1,26 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ html: '<div>3</div>', - async test({ assert, component, target }) { + test({ assert, component, target }) { const div = target.querySelector('div'); - await component.mutate(); + component.mutate(); + flushSync(); + assert.htmlEqual(target.innerHTML, '<div>5</div>'); assert.strictEqual(div, target.querySelector('div')); - await component.reassign(); + component.reassign(); + flushSync(); + assert.htmlEqual(target.innerHTML, '<div>7</div>'); assert.strictEqual(div, target.querySelector('div')); - await component.changeKey(); + component.changeKey(); + flushSync(); + assert.htmlEqual(target.innerHTML, '<div>7</div>'); assert.notStrictEqual(div, target.querySelector('div')); } diff --git a/packages/svelte/tests/runtime-legacy/samples/prop-const/_config.js b/packages/svelte/tests/runtime-legacy/samples/prop-const/_config.js index 040b911cc6..804ee53342 100644 --- a/packages/svelte/tests/runtime-legacy/samples/prop-const/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/prop-const/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -10,11 +11,12 @@ export default test({ <p>b: 2</p> `, - async test({ assert, component, target }) { - await component.$set({ + test({ assert, component, target }) { + component.$set({ a: 5, b: 6 }); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/props-reactive-b/_config.js b/packages/svelte/tests/runtime-legacy/samples/props-reactive-b/_config.js index b92d81bd53..91133f0bf4 100644 --- a/packages/svelte/tests/runtime-legacy/samples/props-reactive-b/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/props-reactive-b/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -11,8 +12,9 @@ export default test({ <p>c: 3</p> `, - async test({ assert, component, target }) { - await component.$set({ a: 4 }); + test({ assert, component, target }) { + component.$set({ a: 4 }); + flushSync(); assert.htmlEqual( target.innerHTML, @@ -23,7 +25,8 @@ export default test({ ` ); - await component.$set({ b: 5 }); + component.$set({ b: 5 }); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-assignment-in-complex-declaration-with-store-3/_config.js b/packages/svelte/tests/runtime-legacy/samples/reactive-assignment-in-complex-declaration-with-store-3/_config.js index 2325d17cd5..1a67e46b38 100644 --- a/packages/svelte/tests/runtime-legacy/samples/reactive-assignment-in-complex-declaration-with-store-3/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/reactive-assignment-in-complex-declaration-with-store-3/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; import { store } from './store.js'; @@ -6,10 +7,9 @@ export default test({ before_test() { store.reset(); }, - async test({ assert, target }) { + test({ assert, target }) { store.set(42); - - await Promise.resolve(); + flushSync(); assert.htmlEqual(target.innerHTML, '<h1>42</h1>'); diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-value-dependency-not-referenced/_config.js b/packages/svelte/tests/runtime-legacy/samples/reactive-value-dependency-not-referenced/_config.js index f2f7fe92b1..3b31ee766e 100644 --- a/packages/svelte/tests/runtime-legacy/samples/reactive-value-dependency-not-referenced/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/reactive-value-dependency-not-referenced/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -6,29 +7,35 @@ export default test({ <p>42</p> `, - async test({ assert, component, target }) { - await component.updateStore(undefined); - await Promise.resolve(); + test({ assert, component, target }) { + component.updateStore(undefined); + flushSync(); + assert.htmlEqual(target.innerHTML, '<p></p><p>42</p>'); - await component.updateStore(33); - await Promise.resolve(); + component.updateStore(33); + flushSync(); + assert.htmlEqual(target.innerHTML, '<p>33</p><p>42</p>'); - await component.updateStore(undefined); - await Promise.resolve(); + component.updateStore(undefined); + flushSync(); + assert.htmlEqual(target.innerHTML, '<p></p><p>42</p>'); - await component.updateVar(undefined); - await Promise.resolve(); + component.updateVar(undefined); + flushSync(); + assert.htmlEqual(target.innerHTML, '<p></p><p></p>'); - await component.updateVar(33); - await Promise.resolve(); + component.updateVar(33); + flushSync(); + assert.htmlEqual(target.innerHTML, '<p></p><p>33</p>'); - await component.updateVar(undefined); - await Promise.resolve(); + component.updateVar(undefined); + flushSync(); + assert.htmlEqual(target.innerHTML, '<p></p><p></p>'); } }); diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/_config.js b/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/_config.js index cbcb19d95e..5317ab496f 100644 --- a/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/reactive-value-function/_config.js @@ -1,10 +1,12 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ html: '1-2', - async test({ assert, component, target }) { - await component.update(); + test({ assert, component, target }) { + component.update(); + flushSync(); assert.htmlEqual(target.innerHTML, '3-4'); } diff --git a/packages/svelte/tests/runtime-legacy/samples/reactive-values-text-node/_config.js b/packages/svelte/tests/runtime-legacy/samples/reactive-values-text-node/_config.js index e05c8c80dd..e97a046f68 100644 --- a/packages/svelte/tests/runtime-legacy/samples/reactive-values-text-node/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/reactive-values-text-node/_config.js @@ -1,5 +1,6 @@ import { test } from '../../test'; import { create_deferred } from '../../../helpers'; +import { flushSync } from 'svelte'; /** @type {ReturnType<typeof create_deferred>} */ let deferred; @@ -17,6 +18,8 @@ export default test({ async test({ assert, target }) { await deferred.promise; + flushSync(); + assert.htmlEqual( target.innerHTML, ` diff --git a/packages/svelte/tests/runtime-legacy/samples/spread-own-props/_config.js b/packages/svelte/tests/runtime-legacy/samples/spread-own-props/_config.js index 708e3615f1..7486055440 100644 --- a/packages/svelte/tests/runtime-legacy/samples/spread-own-props/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/spread-own-props/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -17,13 +18,14 @@ export default test({ <p>quux: core</p></div> `, - async test({ assert, component, target }) { - await component.$set({ + test({ assert, component, target }) { + component.$set({ foo: 'wut', baz: 40 + 3, qux: `this is a ${'rather boring'} string`, quux: 'heart' }); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/store-auto-subscribe-in-reactive-declaration-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/store-auto-subscribe-in-reactive-declaration-2/_config.js index 8e0886c668..d380150e55 100644 --- a/packages/svelte/tests/runtime-legacy/samples/store-auto-subscribe-in-reactive-declaration-2/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/store-auto-subscribe-in-reactive-declaration-2/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -6,10 +7,9 @@ export default test({ <div>Hello World</div> `, - async test({ assert, component, target }) { - await component.update_value('Hi Svelte'); - await Promise.resolve(); - await Promise.resolve(); + test({ assert, component, target }) { + component.update_value('Hi Svelte'); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-legacy/samples/store-increment-updates-reactive/_config.js b/packages/svelte/tests/runtime-legacy/samples/store-increment-updates-reactive/_config.js index 1c81f7c4e8..89ff695d9d 100644 --- a/packages/svelte/tests/runtime-legacy/samples/store-increment-updates-reactive/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/store-increment-updates-reactive/_config.js @@ -1,10 +1,13 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ html: '0', - async test({ assert, component, target }) { - await component.increment(); + test({ assert, component, target }) { + component.increment(); + flushSync(); + assert.htmlEqual(target.innerHTML, '1'); } }); diff --git a/packages/svelte/tests/runtime-legacy/samples/store-unreferenced/_config.js b/packages/svelte/tests/runtime-legacy/samples/store-unreferenced/_config.js index 808ed899d3..7096f5d1fe 100644 --- a/packages/svelte/tests/runtime-legacy/samples/store-unreferenced/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/store-unreferenced/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; import { count } from './store.js'; @@ -8,8 +9,9 @@ export default test({ count.set(0); }, - async test({ assert, component, target }) { - await component.increment(); + test({ assert, component, target }) { + component.increment(); + flushSync(); assert.htmlEqual(target.innerHTML, '<p>count: 1</p>'); } diff --git a/packages/svelte/tests/runtime-legacy/samples/window-event-custom/_config.js b/packages/svelte/tests/runtime-legacy/samples/window-event-custom/_config.js index 32b1c65154..f6b7a8af42 100644 --- a/packages/svelte/tests/runtime-legacy/samples/window-event-custom/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/window-event-custom/_config.js @@ -1,14 +1,16 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ html: '<p>escaped: false</p>', - async test({ assert, target, window }) { + test({ assert, target, window }) { const event = new window.KeyboardEvent('keydown', { key: 'Escape' }); - await window.dispatchEvent(event); + window.dispatchEvent(event); + flushSync(); assert.htmlEqual( target.innerHTML, diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-2/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-2/_config.js index 3604000543..3ca98bb0c6 100644 --- a/packages/svelte/tests/runtime-runes/samples/derived-unowned-2/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-2/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -7,7 +8,7 @@ export default test({ async test({ assert, target }) { await Promise.resolve(); - await Promise.resolve(); + flushSync(); assert.htmlEqual( target.innerHTML, '<div>d2: 3,4,5</div><div>d3: 3,4,5</div><div>d4: 3,4,5</div>' diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-5/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-5/_config.js index 20c3cc112e..5f38394d06 100644 --- a/packages/svelte/tests/runtime-runes/samples/derived-unowned-5/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-5/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -5,7 +6,7 @@ export default test({ // The test has a bunch of queueMicrotasks await Promise.resolve(); await Promise.resolve(); - await Promise.resolve(); + flushSync(); assert.htmlEqual(target.innerHTML, `<div>Zeeba Neighba</div>`); } diff --git a/packages/svelte/tests/runtime-runes/samples/runes-from-func/_config.js b/packages/svelte/tests/runtime-runes/samples/runes-from-func/_config.js index 7d3dd9993f..5ed7579c62 100644 --- a/packages/svelte/tests/runtime-runes/samples/runes-from-func/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/runes-from-func/_config.js @@ -1,3 +1,4 @@ +import { flushSync } from 'svelte'; import { test } from '../../test'; export default test({ @@ -5,7 +6,7 @@ export default test({ async test({ assert, target }) { await Promise.resolve(); - await Promise.resolve(); + flushSync(); assert.htmlEqual(target.innerHTML, `<span>1</span>`); } }); From 8a1acac084096d9c9bd15a1356d3251383e8d258 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Mon, 13 Jan 2025 22:17:39 +0100 Subject: [PATCH 0085/1429] fix: never consider inert boundary effects (#14999) * fix: never consider inert boundary effects * chore: inline bitwise --- .changeset/healthy-hairs-run.md | 5 +++++ .../src/internal/client/reactivity/effects.js | 5 +++-- .../samples/error-boundary-21/Child.svelte | 3 +++ .../samples/error-boundary-21/_config.js | 17 +++++++++++++++++ .../samples/error-boundary-21/main.svelte | 14 ++++++++++++++ 5 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 .changeset/healthy-hairs-run.md create mode 100644 packages/svelte/tests/runtime-runes/samples/error-boundary-21/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/error-boundary-21/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/error-boundary-21/main.svelte diff --git a/.changeset/healthy-hairs-run.md b/.changeset/healthy-hairs-run.md new file mode 100644 index 0000000000..5f0ccd5234 --- /dev/null +++ b/.changeset/healthy-hairs-run.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: never consider inert boundary effects diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 16f076edde..149cbd2d38 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -35,7 +35,8 @@ import { INSPECT_EFFECT, HEAD_EFFECT, MAYBE_DIRTY, - EFFECT_HAS_DERIVED + EFFECT_HAS_DERIVED, + BOUNDARY_EFFECT } from '../constants.js'; import { set } from './sources.js'; import * as e from '../errors.js'; @@ -142,7 +143,7 @@ function create_effect(type, fn, sync, push = true) { effect.first === null && effect.nodes_start === null && effect.teardown === null && - (effect.f & EFFECT_HAS_DERIVED) === 0; + (effect.f & (EFFECT_HAS_DERIVED | BOUNDARY_EFFECT)) === 0; if (!inert && !is_root && push) { if (parent_effect !== null) { diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-21/Child.svelte b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/Child.svelte new file mode 100644 index 0000000000..ea60542af9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/Child.svelte @@ -0,0 +1,3 @@ +<script> + throw new Error(); +</script> diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-21/_config.js b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/_config.js new file mode 100644 index 0000000000..e301f83e60 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/_config.js @@ -0,0 +1,17 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: '<button></button><div>0</div>', + mode: ['client'], + test({ assert, target }) { + let btn = target.querySelector('button'); + let div = target.querySelector('div'); + + flushSync(() => { + btn?.click(); + }); + + assert.equal(div?.innerHTML, `1`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-21/main.svelte b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/main.svelte new file mode 100644 index 0000000000..ed3140b1ef --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-21/main.svelte @@ -0,0 +1,14 @@ +<script> + import Child from "./Child.svelte" + + let count = $state(0); +</script> + +<button onclick={()=>count++}></button> +<svelte:boundary> + <Child /> + + {#snippet failed()} + <div>{count}</div> + {/snippet} +</svelte:boundary> From efa5acf24e040160172550eb5f7fa7ca9390f2e0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 17:18:43 -0500 Subject: [PATCH 0086/1429] Version Packages (#14970) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/healthy-hairs-run.md | 5 ----- .changeset/hot-kings-shout.md | 5 ----- .changeset/spicy-insects-check.md | 5 ----- .changeset/tender-apples-scream.md | 5 ----- packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 7 files changed, 14 insertions(+), 22 deletions(-) delete mode 100644 .changeset/healthy-hairs-run.md delete mode 100644 .changeset/hot-kings-shout.md delete mode 100644 .changeset/spicy-insects-check.md delete mode 100644 .changeset/tender-apples-scream.md diff --git a/.changeset/healthy-hairs-run.md b/.changeset/healthy-hairs-run.md deleted file mode 100644 index 5f0ccd5234..0000000000 --- a/.changeset/healthy-hairs-run.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: never consider inert boundary effects diff --git a/.changeset/hot-kings-shout.md b/.changeset/hot-kings-shout.md deleted file mode 100644 index afba164abf..0000000000 --- a/.changeset/hot-kings-shout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: store access on component destroy diff --git a/.changeset/spicy-insects-check.md b/.changeset/spicy-insects-check.md deleted file mode 100644 index b998d36400..0000000000 --- a/.changeset/spicy-insects-check.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly transform `pre` with no content diff --git a/.changeset/tender-apples-scream.md b/.changeset/tender-apples-scream.md deleted file mode 100644 index 836bdaffdf..0000000000 --- a/.changeset/tender-apples-scream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: wrap each block expression in derived to encapsulate effects diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 6023c6d4e1..4f3b9a168a 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.17.4 + +### Patch Changes + +- fix: never consider inert boundary effects ([#14999](https://github.com/sveltejs/svelte/pull/14999)) + +- fix: store access on component destroy ([#14968](https://github.com/sveltejs/svelte/pull/14968)) + +- fix: correctly transform `pre` with no content ([#14973](https://github.com/sveltejs/svelte/pull/14973)) + +- fix: wrap each block expression in derived to encapsulate effects ([#14967](https://github.com/sveltejs/svelte/pull/14967)) + ## 5.17.3 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 47aec9ee93..f426b97be4 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.17.3", + "version": "5.17.4", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 0fd7d2f039..69a8833938 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.17.3'; +export const VERSION = '5.17.4'; export const PUBLIC_VERSION = '5'; From a129592e5b248a734a68da6e9028941803a3d063 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Tue, 14 Jan 2025 01:36:33 +0100 Subject: [PATCH 0087/1429] feat: allow const tag inside `svelte:boundary` (#14993) * feat: allow const tag inside `svelte:boundary` * chore: add better test * docs: update docs for `@const` --- .changeset/curvy-lies-rush.md | 5 +++++ .../docs/03-template-syntax/09-@const.md | 2 +- .../phases/2-analyze/visitors/ConstTag.js | 1 + .../client/visitors/SvelteBoundary.js | 21 +++++++++++++++---- .../const-tag-boundary/FlakyComponent.svelte | 3 +++ .../samples/const-tag-boundary/_config.js | 17 +++++++++++++++ .../samples/const-tag-boundary/main.svelte | 14 +++++++++++++ .../errors.json | 1 + .../input.svelte | 11 ++++++++++ 9 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 .changeset/curvy-lies-rush.md create mode 100644 packages/svelte/tests/runtime-runes/samples/const-tag-boundary/FlakyComponent.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/const-tag-boundary/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/const-tag-boundary/main.svelte create mode 100644 packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/errors.json create mode 100644 packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/input.svelte diff --git a/.changeset/curvy-lies-rush.md b/.changeset/curvy-lies-rush.md new file mode 100644 index 0000000000..25c6ffc1d9 --- /dev/null +++ b/.changeset/curvy-lies-rush.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +feat: allow const tag inside `svelte:boundary` diff --git a/documentation/docs/03-template-syntax/09-@const.md b/documentation/docs/03-template-syntax/09-@const.md index f4bde77c23..c42d3560fd 100644 --- a/documentation/docs/03-template-syntax/09-@const.md +++ b/documentation/docs/03-template-syntax/09-@const.md @@ -11,4 +11,4 @@ The `{@const ...}` tag defines a local constant. {/each} ``` -`{@const}` is only allowed as an immediate child of a block — `{#if ...}`, `{#each ...}`, `{#snippet ...}` and so on — or a `<Component />`. +`{@const}` is only allowed as an immediate child of a block — `{#if ...}`, `{#each ...}`, `{#snippet ...}` and so on — a `<Component />` or a `<svelte:boundary`. diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ConstTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ConstTag.js index 214b4bcb9e..3f5e0473c5 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ConstTag.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ConstTag.js @@ -24,6 +24,7 @@ export function ConstTag(node, context) { grand_parent?.type !== 'EachBlock' && grand_parent?.type !== 'AwaitBlock' && grand_parent?.type !== 'SnippetBlock' && + grand_parent?.type !== 'SvelteBoundary' && ((grand_parent?.type !== 'RegularElement' && grand_parent?.type !== 'SvelteElement') || !grand_parent.attributes.some((a) => a.type === 'Attribute' && a.name === 'slot'))) ) { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js index ef9f6bd798..325485d4c0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js @@ -33,21 +33,34 @@ export function SvelteBoundary(node, context) { const nodes = []; /** @type {Statement[]} */ - const snippet_statements = []; + const external_statements = []; + + const snippets_visits = []; // Capture the `failed` implicit snippet prop for (const child of node.fragment.nodes) { if (child.type === 'SnippetBlock' && child.expression.name === 'failed') { + // we need to delay the visit of the snippets in case they access a ConstTag that is declared + // after the snippets so that the visitor for the const tag can be updated + snippets_visits.push(() => { + /** @type {Statement[]} */ + const init = []; + context.visit(child, { ...context.state, init }); + props.properties.push(b.prop('init', child.expression, child.expression)); + external_statements.push(...init); + }); + } else if (child.type === 'ConstTag') { /** @type {Statement[]} */ const init = []; context.visit(child, { ...context.state, init }); - props.properties.push(b.prop('init', child.expression, child.expression)); - snippet_statements.push(...init); + external_statements.push(...init); } else { nodes.push(child); } } + snippets_visits.forEach((visit) => visit()); + const block = /** @type {BlockStatement} */ (context.visit({ ...node.fragment, nodes })); const boundary = b.stmt( @@ -56,6 +69,6 @@ export function SvelteBoundary(node, context) { context.state.template.push('<!>'); context.state.init.push( - snippet_statements.length > 0 ? b.block([...snippet_statements, boundary]) : boundary + external_statements.length > 0 ? b.block([...external_statements, boundary]) : boundary ); } diff --git a/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/FlakyComponent.svelte b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/FlakyComponent.svelte new file mode 100644 index 0000000000..8bbec90de4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/FlakyComponent.svelte @@ -0,0 +1,3 @@ +<script> + throw new Error(); +</script> \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/_config.js b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/_config.js new file mode 100644 index 0000000000..4338969a48 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/_config.js @@ -0,0 +1,17 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: '<button></button><p>2</p>', + mode: ['client'], + test({ target, assert }) { + const btn = target.querySelector('button'); + const p = target.querySelector('p'); + + flushSync(() => { + btn?.click(); + }); + + assert.equal(p?.innerHTML, '4'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/main.svelte b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/main.svelte new file mode 100644 index 0000000000..25ea8a3ffc --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/const-tag-boundary/main.svelte @@ -0,0 +1,14 @@ +<script> + import FlakyComponent from "./FlakyComponent.svelte"; + let test=$state(1); +</script> + +<button onclick={()=>test++}></button> + +<svelte:boundary> + {@const double = test * 2} + {#snippet failed()} + <p>{double}</p> + {/snippet} + <FlakyComponent /> +</svelte:boundary> \ No newline at end of file diff --git a/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/errors.json b/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/errors.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/errors.json @@ -0,0 +1 @@ +[] diff --git a/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/input.svelte b/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/input.svelte new file mode 100644 index 0000000000..5708cc36ca --- /dev/null +++ b/packages/svelte/tests/validator/samples/const-tag-placement-svelte-boundary/input.svelte @@ -0,0 +1,11 @@ +<script> + let a = $state(""); +</script> + +<svelte:boundary> + {@const x = a} + {#snippet failed()} + {x} + {/snippet} + <FlakyComponent /> +</svelte:boundary> \ No newline at end of file From 99fdc3f0ab2043af1c6cfc23f6aa886d047ddb0d Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Tue, 14 Jan 2025 10:51:57 +0100 Subject: [PATCH 0088/1429] docs: update error message for `const_tag_invalid_placement` (#15003) --- documentation/docs/98-reference/.generated/compile-errors.md | 2 +- packages/svelte/messages/compile-errors/template.md | 2 +- packages/svelte/src/compiler/errors.js | 4 ++-- .../tests/validator/samples/const-tag-placement-1/errors.json | 2 +- .../tests/validator/samples/const-tag-placement-2/errors.json | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index a867cfe88c..2fef3bd45d 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -187,7 +187,7 @@ Cyclical dependency detected: %cycle% ### const_tag_invalid_placement ``` -`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>` or `<Component>` +`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>`, `<svelte:boundary` or `<Component>` ``` ### constant_assignment diff --git a/packages/svelte/messages/compile-errors/template.md b/packages/svelte/messages/compile-errors/template.md index 287178ef87..d061416dbd 100644 --- a/packages/svelte/messages/compile-errors/template.md +++ b/packages/svelte/messages/compile-errors/template.md @@ -118,7 +118,7 @@ ## const_tag_invalid_placement -> `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>` or `<Component>` +> `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>`, `<svelte:boundary` or `<Component>` ## debug_tag_invalid_arguments diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index da038af430..53a6ac6849 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -888,12 +888,12 @@ export function const_tag_invalid_expression(node) { } /** - * `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>` or `<Component>` + * `{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>`, `<svelte:boundary` or `<Component>` * @param {null | number | NodeLike} node * @returns {never} */ export function const_tag_invalid_placement(node) { - e(node, 'const_tag_invalid_placement', `\`{@const}\` must be the immediate child of \`{#snippet}\`, \`{#if}\`, \`{:else if}\`, \`{:else}\`, \`{#each}\`, \`{:then}\`, \`{:catch}\`, \`<svelte:fragment>\` or \`<Component>\`\nhttps://svelte.dev/e/const_tag_invalid_placement`); + 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`); } /** diff --git a/packages/svelte/tests/validator/samples/const-tag-placement-1/errors.json b/packages/svelte/tests/validator/samples/const-tag-placement-1/errors.json index 144345527a..514e5d0561 100644 --- a/packages/svelte/tests/validator/samples/const-tag-placement-1/errors.json +++ b/packages/svelte/tests/validator/samples/const-tag-placement-1/errors.json @@ -1,7 +1,7 @@ [ { "code": "const_tag_invalid_placement", - "message": "`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>` or `<Component>`", + "message": "`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>`, `<svelte:boundary` or `<Component>`", "start": { "line": 5, "column": 0 diff --git a/packages/svelte/tests/validator/samples/const-tag-placement-2/errors.json b/packages/svelte/tests/validator/samples/const-tag-placement-2/errors.json index 64a24c5f48..6b968f7eda 100644 --- a/packages/svelte/tests/validator/samples/const-tag-placement-2/errors.json +++ b/packages/svelte/tests/validator/samples/const-tag-placement-2/errors.json @@ -1,7 +1,7 @@ [ { "code": "const_tag_invalid_placement", - "message": "`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>` or `<Component>`", + "message": "`{@const}` must be the immediate child of `{#snippet}`, `{#if}`, `{:else if}`, `{:else}`, `{#each}`, `{:then}`, `{:catch}`, `<svelte:fragment>`, `<svelte:boundary` or `<Component>`", "start": { "line": 7, "column": 4 From dae4c5f5e191927d3f05a156b986b605b6d1e97a Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Tue, 14 Jan 2025 15:58:42 +0000 Subject: [PATCH 0089/1429] fix: ensure signal write invalidation within effects is consistent (#14989) * fix: ensure signal write invalidation within effects is persistent * fix: ensure signal write invalidation within effects is persistent * fix: ensure signal write invalidation within effects is persistent * address feedback --- .changeset/tricky-spiders-collect.md | 5 ++ .../src/internal/client/reactivity/sources.js | 22 +++----- .../svelte/src/internal/client/runtime.js | 55 +++++++++++++++---- packages/svelte/tests/signals/test.ts | 36 ++++++++++++ 4 files changed, 93 insertions(+), 25 deletions(-) create mode 100644 .changeset/tricky-spiders-collect.md diff --git a/.changeset/tricky-spiders-collect.md b/.changeset/tricky-spiders-collect.md new file mode 100644 index 0000000000..0ea8b71e5e --- /dev/null +++ b/.changeset/tricky-spiders-collect.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure signal write invalidation within effects is consistent diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index e0facf3b55..4500a7c5a8 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -3,7 +3,6 @@ import { DEV } from 'esm-env'; import { component_context, active_reaction, - new_deps, active_effect, untracked_writes, get, @@ -29,7 +28,8 @@ import { INSPECT_EFFECT, UNOWNED, MAYBE_DIRTY, - BLOCK_EFFECT + BLOCK_EFFECT, + ROOT_EFFECT } from '../constants.js'; import * as e from '../errors.js'; import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js'; @@ -182,26 +182,20 @@ export function internal_set(source, value) { mark_reactions(source, DIRTY); - // If the current signal is running for the first time, it won't have any - // reactions as we only allocate and assign the reactions after the signal - // has fully executed. So in the case of ensuring it registers the reaction + // It's possible that the current reaction might not have up-to-date dependencies + // whilst it's actively running. So in the case of ensuring it registers the reaction // properly for itself, we need to ensure the current effect actually gets // scheduled. i.e: `$effect(() => x++)` if ( is_runes() && active_effect !== null && (active_effect.f & CLEAN) !== 0 && - (active_effect.f & BRANCH_EFFECT) === 0 + (active_effect.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ) { - if (new_deps !== null && new_deps.includes(source)) { - set_signal_status(active_effect, DIRTY); - schedule_effect(active_effect); + if (untracked_writes === null) { + set_untracked_writes([source]); } else { - if (untracked_writes === null) { - set_untracked_writes([source]); - } else { - untracked_writes.push(source); - } + untracked_writes.push(source); } } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 3c8879eb31..eca5ee94f9 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -382,6 +382,34 @@ export function handle_error(error, effect, previous_effect, component_context) } } +/** + * @param {Value} signal + * @param {Effect} effect + * @param {number} [depth] + */ +function schedule_possible_effect_self_invalidation(signal, effect, depth = 0) { + var reactions = signal.reactions; + if (reactions === null) return; + + for (var i = 0; i < reactions.length; i++) { + var reaction = reactions[i]; + if ((reaction.f & DERIVED) !== 0) { + schedule_possible_effect_self_invalidation( + /** @type {Derived} */ (reaction), + effect, + depth + 1 + ); + } else if (effect === reaction) { + if (depth === 0) { + set_signal_status(reaction, DIRTY); + } else if ((reaction.f & CLEAN) !== 0) { + set_signal_status(reaction, MAYBE_DIRTY); + } + schedule_effect(/** @type {Effect} */ (reaction)); + } + } +} + /** * @template V * @param {Reaction} reaction @@ -434,6 +462,22 @@ export function update_reaction(reaction) { deps.length = skipped_deps; } + // If we're inside an effect and we have untracked writes, then we need to + // ensure that if any of those untracked writes result in re-invalidation + // of the current effect, then that happens accordingly + if ( + is_runes() && + untracked_writes !== null && + (reaction.f & (DERIVED | MAYBE_DIRTY | DIRTY)) === 0 + ) { + for (i = 0; i < /** @type {Source[]} */ (untracked_writes).length; i++) { + schedule_possible_effect_self_invalidation( + untracked_writes[i], + /** @type {Effect} */ (reaction) + ); + } + } + // If we are returning to an previous reaction then // we need to increment the read version to ensure that // any dependencies in this reaction aren't marked with @@ -907,17 +951,6 @@ export function get(signal) { } else { new_deps.push(signal); } - - if ( - untracked_writes !== null && - active_effect !== null && - (active_effect.f & CLEAN) !== 0 && - (active_effect.f & BRANCH_EFFECT) === 0 && - untracked_writes.includes(signal) - ) { - set_signal_status(active_effect, DIRTY); - schedule_effect(active_effect); - } } } else if (is_derived && /** @type {Derived} */ (signal).deps === null) { var derived = /** @type {Derived} */ (signal); diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index e198a5a89d..a9d29920cf 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -402,6 +402,42 @@ describe('signals', () => { }; }); + test('schedules rerun when writing to signal before reading it from derived', (runes) => { + if (!runes) return () => {}; + let log: any[] = []; + + const value = state(1); + const double = derived(() => $.get(value) * 2); + + user_effect(() => { + set(value, 10); + log.push($.get(double)); + }); + + return () => { + flushSync(); + assert.deepEqual(log, [20]); + }; + }); + + test('schedules rerun when writing to signal after reading it from derived', (runes) => { + if (!runes) return () => {}; + let log: any[] = []; + + const value = state(1); + const double = derived(() => $.get(value) * 2); + + user_effect(() => { + log.push($.get(double)); + set(value, 10); + }); + + return () => { + flushSync(); + assert.deepEqual(log, [2, 20]); + }; + }); + test('effect teardown is removed on re-run', () => { const count = state(0); let first = true; From a1698c63ec8ffe8974b077154c7a4cff6f791933 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 16:05:05 +0000 Subject: [PATCH 0090/1429] Version Packages (#15001) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/curvy-lies-rush.md | 5 ----- .changeset/tricky-spiders-collect.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/curvy-lies-rush.md delete mode 100644 .changeset/tricky-spiders-collect.md diff --git a/.changeset/curvy-lies-rush.md b/.changeset/curvy-lies-rush.md deleted file mode 100644 index 25c6ffc1d9..0000000000 --- a/.changeset/curvy-lies-rush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -feat: allow const tag inside `svelte:boundary` diff --git a/.changeset/tricky-spiders-collect.md b/.changeset/tricky-spiders-collect.md deleted file mode 100644 index 0ea8b71e5e..0000000000 --- a/.changeset/tricky-spiders-collect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure signal write invalidation within effects is consistent diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 4f3b9a168a..e0ae3f313e 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.17.5 + +### Patch Changes + +- feat: allow const tag inside `svelte:boundary` ([#14993](https://github.com/sveltejs/svelte/pull/14993)) + +- fix: ensure signal write invalidation within effects is consistent ([#14989](https://github.com/sveltejs/svelte/pull/14989)) + ## 5.17.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index f426b97be4..0b69fb6edf 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.17.4", + "version": "5.17.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 69a8833938..b57f60055a 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.17.4'; +export const VERSION = '5.17.5'; export const PUBLIC_VERSION = '5'; From dfa97a5e643655d919a7c6ff80fb3841fe1111f9 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Tue, 14 Jan 2025 17:28:44 +0100 Subject: [PATCH 0091/1429] feat: allow every children in `template` tags (#15007) * feat: allow every children in `template` tags * Update .changeset/dry-mails-return.md --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/dry-mails-return.md | 5 +++++ packages/svelte/src/html-tree-validation.js | 2 ++ .../samples/invalid-node-placement-template/errors.json | 1 + .../samples/invalid-node-placement-template/input.svelte | 6 ++++++ 4 files changed, 14 insertions(+) create mode 100644 .changeset/dry-mails-return.md create mode 100644 packages/svelte/tests/validator/samples/invalid-node-placement-template/errors.json create mode 100644 packages/svelte/tests/validator/samples/invalid-node-placement-template/input.svelte diff --git a/.changeset/dry-mails-return.md b/.changeset/dry-mails-return.md new file mode 100644 index 0000000000..8f87f8e33b --- /dev/null +++ b/.changeset/dry-mails-return.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: allow `<template>` elements to contain any child diff --git a/packages/svelte/src/html-tree-validation.js b/packages/svelte/src/html-tree-validation.js index 98e74b638f..4ec4ecf26f 100644 --- a/packages/svelte/src/html-tree-validation.js +++ b/packages/svelte/src/html-tree-validation.js @@ -186,6 +186,8 @@ export function is_tag_valid_with_ancestor(child_tag, ancestors, child_loc, ance export function is_tag_valid_with_parent(child_tag, parent_tag, child_loc, parent_loc) { if (child_tag.includes('-') || parent_tag?.includes('-')) return null; // custom elements can be anything + if (parent_tag === 'template') return null; // no errors or warning should be thrown in immediate children of template tags + const disallowed = disallowed_children[parent_tag]; const child = child_loc ? `\`<${child_tag}>\` (${child_loc})` : `\`<${child_tag}>\``; diff --git a/packages/svelte/tests/validator/samples/invalid-node-placement-template/errors.json b/packages/svelte/tests/validator/samples/invalid-node-placement-template/errors.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/packages/svelte/tests/validator/samples/invalid-node-placement-template/errors.json @@ -0,0 +1 @@ +[] diff --git a/packages/svelte/tests/validator/samples/invalid-node-placement-template/input.svelte b/packages/svelte/tests/validator/samples/invalid-node-placement-template/input.svelte new file mode 100644 index 0000000000..65aef2a7aa --- /dev/null +++ b/packages/svelte/tests/validator/samples/invalid-node-placement-template/input.svelte @@ -0,0 +1,6 @@ +<template> + <tr> + <td></td> + <td></td> + </tr> +</template> \ No newline at end of file From 360ee70dcbc4a338fce749b8916b46a69ad209cb Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Tue, 14 Jan 2025 22:41:57 +0100 Subject: [PATCH 0092/1429] fix: remove listener on `bind_current_time` teardown (#15013) Fixes #15008 --- .changeset/sour-pots-wonder.md | 5 ++++ .../client/dom/elements/bindings/media.js | 5 +++- .../_config.js | 26 +++++++++++++++++++ .../main.svelte | 12 +++++++++ 4 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 .changeset/sour-pots-wonder.md create mode 100644 packages/svelte/tests/runtime-runes/samples/bind-current-time-remove-listener/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/bind-current-time-remove-listener/main.svelte diff --git a/.changeset/sour-pots-wonder.md b/.changeset/sour-pots-wonder.md new file mode 100644 index 0000000000..3c8fce5f06 --- /dev/null +++ b/.changeset/sour-pots-wonder.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: remove listener on `bind_current_time` teardown diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/media.js b/packages/svelte/src/internal/client/dom/elements/bindings/media.js index 444a1b3439..4893426d55 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/media.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/media.js @@ -52,7 +52,10 @@ export function bind_current_time(media, get, set = get) { } }); - teardown(() => cancelAnimationFrame(raf_id)); + teardown(() => { + cancelAnimationFrame(raf_id); + media.removeEventListener('timeupdate', callback); + }); } /** diff --git a/packages/svelte/tests/runtime-runes/samples/bind-current-time-remove-listener/_config.js b/packages/svelte/tests/runtime-runes/samples/bind-current-time-remove-listener/_config.js new file mode 100644 index 0000000000..29dc5e8d7f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/bind-current-time-remove-listener/_config.js @@ -0,0 +1,26 @@ +import { flushSync } from 'svelte'; +import { ok, test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + const audio = target.querySelector('audio'); + const btn = target.querySelector('button'); + + ok(audio); + + flushSync(() => { + audio.currentTime = 10; + audio.dispatchEvent(new Event('timeupdate')); + }); + assert.deepEqual(logs, ['event']); + + flushSync(() => { + btn?.click(); + }); + flushSync(() => { + audio.currentTime = 20; + audio.dispatchEvent(new Event('timeupdate')); + }); + assert.deepEqual(logs, ['event']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/bind-current-time-remove-listener/main.svelte b/packages/svelte/tests/runtime-runes/samples/bind-current-time-remove-listener/main.svelte new file mode 100644 index 0000000000..40c215378e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/bind-current-time-remove-listener/main.svelte @@ -0,0 +1,12 @@ +<script> + let show = $state(true); + let time = $state(0); +</script> + +<button onclick={()=> show = false}></button> +{#if show} + <audio bind:currentTime={()=>time,(new_time)=>{ + console.log("event"); + time = new_time; + }}></audio> +{/if} \ No newline at end of file From 54196108331441924717ec1ec97ac4a3eb77bbc0 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Tue, 14 Jan 2025 22:08:33 +0000 Subject: [PATCH 0093/1429] fix: ensure resume effects are scheduled in topological order (#15012) * fix: ensure resume effects are scheduled in topological order * fix: ensure resume effects are scheduled in topological order --- .changeset/lemon-llamas-reflect.md | 5 +++ .../src/internal/client/reactivity/effects.js | 16 +++++--- .../samples/transition-each-4/_config.js | 35 +++++++++++++++++ .../samples/transition-each-4/main.svelte | 39 +++++++++++++++++++ 4 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 .changeset/lemon-llamas-reflect.md create mode 100644 packages/svelte/tests/runtime-runes/samples/transition-each-4/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/transition-each-4/main.svelte diff --git a/.changeset/lemon-llamas-reflect.md b/.changeset/lemon-llamas-reflect.md new file mode 100644 index 0000000000..c858403bd4 --- /dev/null +++ b/.changeset/lemon-llamas-reflect.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure resume effects are scheduled in topological order diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 149cbd2d38..428f69281b 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -602,17 +602,21 @@ export function resume_effect(effect) { */ function resume_children(effect, local) { if ((effect.f & INERT) === 0) return; + effect.f ^= INERT; + + // Ensure the effect is marked as clean again so that any dirty child + // effects can schedule themselves for execution + if ((effect.f & CLEAN) === 0) { + effect.f ^= CLEAN; + } // If a dependency of this effect changed while it was paused, - // apply the change now + // schedule the effect to update if (check_dirtiness(effect)) { - update_effect(effect); + set_signal_status(effect, DIRTY); + schedule_effect(effect); } - // Ensure we toggle the flag after possibly updating the effect so that - // each block logic can correctly operate on inert items - effect.f ^= INERT; - var child = effect.first; while (child !== null) { diff --git a/packages/svelte/tests/runtime-runes/samples/transition-each-4/_config.js b/packages/svelte/tests/runtime-runes/samples/transition-each-4/_config.js new file mode 100644 index 0000000000..a0e29ec8ff --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/transition-each-4/_config.js @@ -0,0 +1,35 @@ +import { flushSync } from '../../../../src/index-client.js'; +import { test } from '../../test'; + +export default test({ + async test({ assert, raf, target, logs }) { + assert.htmlEqual( + target.innerHTML, + '<button>Toggle</button><div><div>1</div><div>2</div><div>3</div></div>' + ); + + const btn1 = target.querySelector('button'); + btn1?.click(); + flushSync(); + raf.tick(250); + + assert.htmlEqual( + target.innerHTML, + '<button>Toggle</button><div style="opacity: 0.5;"><div>1</div><div>2</div><div>3</div></div>' + ); + + logs.length = 0; + + await Promise.resolve(); + + flushSync(); + raf.tick(500); + + assert.htmlEqual( + target.innerHTML, + '<button>Toggle</button><div style=""><div>3</div><div>4</div></div>' + ); + + assert.deepEqual(logs, ['$effect.pre', '$effect.pre', '$effect', '$effect']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/transition-each-4/main.svelte b/packages/svelte/tests/runtime-runes/samples/transition-each-4/main.svelte new file mode 100644 index 0000000000..d399bee691 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/transition-each-4/main.svelte @@ -0,0 +1,39 @@ +<script> + function fade(_) { + return { + duration: 500, + css: t => `opacity: ${t}`, + } + } + + let toggle = $state(true); + let items = $state([ 1, 2, 3 ]); + + const handle_toggle = async () => { + toggle = false; + await Promise.resolve(); + items = [3, 4]; + toggle = true; + }; +</script> + +<button onclick={handle_toggle}>Toggle</button> + +{#if toggle} + <div transition:fade> + {#each items as item (item)} + {(() => { + $effect(() => { + items; + console.log('$effect'); + }); + + $effect.pre(() => { + items; + console.log('$effect.pre'); + }); + })()} + <div>{item}</div> + {/each} + </div> +{/if} From ff79704ab831b556f7c70b0d5d6e31ace945f019 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Tue, 14 Jan 2025 18:58:53 -0500 Subject: [PATCH 0094/1429] fix: bump esrap (#15015) * chore: bump esrap * changeset --- .changeset/lucky-students-deny.md | 5 +++++ packages/svelte/package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 3 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 .changeset/lucky-students-deny.md diff --git a/.changeset/lucky-students-deny.md b/.changeset/lucky-students-deny.md new file mode 100644 index 0000000000..83bb72e735 --- /dev/null +++ b/.changeset/lucky-students-deny.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: bump esrap diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 0b69fb6edf..30bfd39ce2 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -155,7 +155,7 @@ "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", - "esrap": "^1.4.2", + "esrap": "^1.4.3", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 731bb97edb..1979ce7b22 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -87,8 +87,8 @@ importers: specifier: ^1.2.1 version: 1.2.1 esrap: - specifier: ^1.4.2 - version: 1.4.2 + specifier: ^1.4.3 + version: 1.4.3 is-reference: specifier: ^3.0.3 version: 3.0.3 @@ -1118,8 +1118,8 @@ packages: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} - esrap@1.4.2: - resolution: {integrity: sha512-FhVlJzvTw7ZLxYZ7RyHwQCFE64dkkpzGNNnphaGCLwjqGk1SQcqzbgdx9FowPCktx6NOSHkzvcZ3vsvdH54YXA==} + esrap@1.4.3: + resolution: {integrity: sha512-Xddc1RsoFJ4z9nR7W7BFaEPIp4UXoeQ0+077UdWLxbafMQFyU79sQJMk7kxNgRwQ9/aVgaKacCHC2pUACGwmYw==} esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} @@ -3324,7 +3324,7 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@1.4.2: + esrap@1.4.3: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 From 1d3c4397b2f7fe01b3e5983e647f5a27dd63e67c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 Jan 2025 21:27:26 -0500 Subject: [PATCH 0095/1429] Version Packages (#15010) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/dry-mails-return.md | 5 ----- .changeset/lemon-llamas-reflect.md | 5 ----- .changeset/lucky-students-deny.md | 5 ----- .changeset/sour-pots-wonder.md | 5 ----- packages/svelte/CHANGELOG.md | 14 ++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 7 files changed, 16 insertions(+), 22 deletions(-) delete mode 100644 .changeset/dry-mails-return.md delete mode 100644 .changeset/lemon-llamas-reflect.md delete mode 100644 .changeset/lucky-students-deny.md delete mode 100644 .changeset/sour-pots-wonder.md diff --git a/.changeset/dry-mails-return.md b/.changeset/dry-mails-return.md deleted file mode 100644 index 8f87f8e33b..0000000000 --- a/.changeset/dry-mails-return.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: allow `<template>` elements to contain any child diff --git a/.changeset/lemon-llamas-reflect.md b/.changeset/lemon-llamas-reflect.md deleted file mode 100644 index c858403bd4..0000000000 --- a/.changeset/lemon-llamas-reflect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure resume effects are scheduled in topological order diff --git a/.changeset/lucky-students-deny.md b/.changeset/lucky-students-deny.md deleted file mode 100644 index 83bb72e735..0000000000 --- a/.changeset/lucky-students-deny.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: bump esrap diff --git a/.changeset/sour-pots-wonder.md b/.changeset/sour-pots-wonder.md deleted file mode 100644 index 3c8fce5f06..0000000000 --- a/.changeset/sour-pots-wonder.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: remove listener on `bind_current_time` teardown diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index e0ae3f313e..0c2313ee71 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,19 @@ # svelte +## 5.18.0 + +### Minor Changes + +- feat: allow `<template>` elements to contain any child ([#15007](https://github.com/sveltejs/svelte/pull/15007)) + +### Patch Changes + +- fix: ensure resume effects are scheduled in topological order ([#15012](https://github.com/sveltejs/svelte/pull/15012)) + +- fix: bump esrap ([#15015](https://github.com/sveltejs/svelte/pull/15015)) + +- fix: remove listener on `bind_current_time` teardown ([#15013](https://github.com/sveltejs/svelte/pull/15013)) + ## 5.17.5 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 30bfd39ce2..2dbd937d04 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.17.5", + "version": "5.18.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b57f60055a..4a7a6d03df 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.17.5'; +export const VERSION = '5.18.0'; export const PUBLIC_VERSION = '5'; From 973b51d16891567851c89f88d03c8aa71e0084a4 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Wed, 15 Jan 2025 12:23:05 +0100 Subject: [PATCH 0096/1429] fix: correctly parse leading comments in function binding (#15020) * fix: correctly parse leading comments in function binding * chore: fix lint --- .changeset/violet-apes-punch.md | 5 + .../phases/1-parse/read/expression.js | 4 + .../2-analyze/visitors/BindDirective.js | 13 +- .../input.svelte | 10 + .../output.json | 326 ++++++++++++++++++ .../errors.json | 1 + .../input.svelte | 10 + 7 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 .changeset/violet-apes-punch.md create mode 100644 packages/svelte/tests/parser-modern/samples/comment-before-function-binding/input.svelte create mode 100644 packages/svelte/tests/parser-modern/samples/comment-before-function-binding/output.json create mode 100644 packages/svelte/tests/validator/samples/comment-before-function-binding/errors.json create mode 100644 packages/svelte/tests/validator/samples/comment-before-function-binding/input.svelte diff --git a/.changeset/violet-apes-punch.md b/.changeset/violet-apes-punch.md new file mode 100644 index 0000000000..77f4e923c8 --- /dev/null +++ b/.changeset/violet-apes-punch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly parse leading comments in function binding diff --git a/packages/svelte/src/compiler/phases/1-parse/read/expression.js b/packages/svelte/src/compiler/phases/1-parse/read/expression.js index 82a667d38c..a596cdf572 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/expression.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/expression.js @@ -38,6 +38,10 @@ export default function read_expression(parser, opening_token, disallow_loose) { let num_parens = 0; + if (node.leadingComments !== undefined && node.leadingComments.length > 0) { + parser.index = node.leadingComments.at(-1).end; + } + for (let i = parser.index; i < /** @type {number} */ (node.start); i += 1) { if (parser.template[i] === '(') num_parens += 1; } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js index b4de1925df..7719eee677 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js @@ -132,8 +132,19 @@ export function BindDirective(node, context) { } let i = /** @type {number} */ (node.expression.start); + let leading_comments_start = /**@type {any}*/ (node.expression.leadingComments?.at(0))?.start; + let leading_comments_end = /**@type {any}*/ (node.expression.leadingComments?.at(-1))?.end; while (context.state.analysis.source[--i] !== '{') { - if (context.state.analysis.source[i] === '(') { + if ( + context.state.analysis.source[i] === '(' && + // if the parenthesis is in a leading comment we don't need to throw the error + !( + leading_comments_start && + leading_comments_end && + i <= leading_comments_end && + i >= leading_comments_start + ) + ) { e.bind_invalid_parens(node, node.name); } } diff --git a/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/input.svelte b/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/input.svelte new file mode 100644 index 0000000000..50200a9eac --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/input.svelte @@ -0,0 +1,10 @@ +<script> + let value = ''; +</script> + +<input bind:value={ + /** ( */ + () => value, + (v) => value = v.toLowerCase() +} +/> diff --git a/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/output.json b/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/output.json new file mode 100644 index 0000000000..dba258a6b1 --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/comment-before-function-binding/output.json @@ -0,0 +1,326 @@ +{ + "css": null, + "js": [], + "start": 37, + "end": 117, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 35, + "end": 37, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "RegularElement", + "start": 37, + "end": 117, + "name": "input", + "attributes": [ + { + "start": 44, + "end": 114, + "type": "BindDirective", + "name": "value", + "expression": { + "type": "SequenceExpression", + "start": 68, + "end": 112, + "loc": { + "start": { + "line": 7, + "column": 1 + }, + "end": { + "line": 8, + "column": 31 + } + }, + "expressions": [ + { + "type": "ArrowFunctionExpression", + "start": 68, + "end": 79, + "loc": { + "start": { + "line": 7, + "column": 1 + }, + "end": { + "line": 7, + "column": 12 + } + }, + "id": null, + "expression": true, + "generator": false, + "async": false, + "params": [], + "body": { + "type": "Identifier", + "start": 74, + "end": 79, + "loc": { + "start": { + "line": 7, + "column": 7 + }, + "end": { + "line": 7, + "column": 12 + } + }, + "name": "value" + } + }, + { + "type": "ArrowFunctionExpression", + "start": 82, + "end": 112, + "loc": { + "start": { + "line": 8, + "column": 1 + }, + "end": { + "line": 8, + "column": 31 + } + }, + "id": null, + "expression": true, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 83, + "end": 84, + "loc": { + "start": { + "line": 8, + "column": 2 + }, + "end": { + "line": 8, + "column": 3 + } + }, + "name": "v" + } + ], + "body": { + "type": "AssignmentExpression", + "start": 89, + "end": 112, + "loc": { + "start": { + "line": 8, + "column": 8 + }, + "end": { + "line": 8, + "column": 31 + } + }, + "operator": "=", + "left": { + "type": "Identifier", + "start": 89, + "end": 94, + "loc": { + "start": { + "line": 8, + "column": 8 + }, + "end": { + "line": 8, + "column": 13 + } + }, + "name": "value" + }, + "right": { + "type": "CallExpression", + "start": 97, + "end": 112, + "loc": { + "start": { + "line": 8, + "column": 16 + }, + "end": { + "line": 8, + "column": 31 + } + }, + "callee": { + "type": "MemberExpression", + "start": 97, + "end": 110, + "loc": { + "start": { + "line": 8, + "column": 16 + }, + "end": { + "line": 8, + "column": 29 + } + }, + "object": { + "type": "Identifier", + "start": 97, + "end": 98, + "loc": { + "start": { + "line": 8, + "column": 16 + }, + "end": { + "line": 8, + "column": 17 + } + }, + "name": "v" + }, + "property": { + "type": "Identifier", + "start": 99, + "end": 110, + "loc": { + "start": { + "line": 8, + "column": 18 + }, + "end": { + "line": 8, + "column": 29 + } + }, + "name": "toLowerCase" + }, + "computed": false, + "optional": false + }, + "arguments": [], + "optional": false + } + } + } + ], + "leadingComments": [ + { + "type": "Block", + "value": "* ( ", + "start": 58, + "end": 66 + } + ] + }, + "modifiers": [] + } + ], + "fragment": { + "type": "Fragment", + "nodes": [] + } + } + ] + }, + "options": null, + "instance": { + "type": "Script", + "start": 0, + "end": 35, + "context": "default", + "content": { + "type": "Program", + "start": 8, + "end": 26, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 3, + "column": 0 + } + }, + "body": [ + { + "type": "VariableDeclaration", + "start": 10, + "end": 25, + "loc": { + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 16 + } + }, + "declarations": [ + { + "type": "VariableDeclarator", + "start": 14, + "end": 24, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 15 + } + }, + "id": { + "type": "Identifier", + "start": 14, + "end": 19, + "loc": { + "start": { + "line": 2, + "column": 5 + }, + "end": { + "line": 2, + "column": 10 + } + }, + "name": "value" + }, + "init": { + "type": "Literal", + "start": 22, + "end": 24, + "loc": { + "start": { + "line": 2, + "column": 13 + }, + "end": { + "line": 2, + "column": 15 + } + }, + "value": "", + "raw": "''" + } + } + ], + "kind": "let" + } + ], + "sourceType": "module" + }, + "attributes": [] + } +} diff --git a/packages/svelte/tests/validator/samples/comment-before-function-binding/errors.json b/packages/svelte/tests/validator/samples/comment-before-function-binding/errors.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/packages/svelte/tests/validator/samples/comment-before-function-binding/errors.json @@ -0,0 +1 @@ +[] diff --git a/packages/svelte/tests/validator/samples/comment-before-function-binding/input.svelte b/packages/svelte/tests/validator/samples/comment-before-function-binding/input.svelte new file mode 100644 index 0000000000..50200a9eac --- /dev/null +++ b/packages/svelte/tests/validator/samples/comment-before-function-binding/input.svelte @@ -0,0 +1,10 @@ +<script> + let value = ''; +</script> + +<input bind:value={ + /** ( */ + () => value, + (v) => value = v.toLowerCase() +} +/> From 2aefc5430decf93cb3ead70e6da0503ce52d401f Mon Sep 17 00:00:00 2001 From: Puru Vijay <47742487+PuruVJ@users.noreply.github.com> Date: Thu, 16 Jan 2025 19:02:10 +0530 Subject: [PATCH 0097/1429] feat(elements): Expose ClassValue (#15035) * Push * Cleanup * Add changeset * Remove redundant string * Update documentation/docs/03-template-syntax/18-class.md Co-authored-by: Paolo Ricciuti <ricciutipaolo@gmail.com> * Update documentation/docs/03-template-syntax/18-class.md --------- Co-authored-by: Paolo Ricciuti <ricciutipaolo@gmail.com> --- .changeset/thirty-starfishes-shout.md | 5 +++++ documentation/docs/03-template-syntax/18-class.md | 12 ++++++++++++ packages/svelte/elements.d.ts | 6 ++++-- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 .changeset/thirty-starfishes-shout.md diff --git a/.changeset/thirty-starfishes-shout.md b/.changeset/thirty-starfishes-shout.md new file mode 100644 index 0000000000..3c577dcee1 --- /dev/null +++ b/.changeset/thirty-starfishes-shout.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: Expose `ClassValue` from `svelte/elements` diff --git a/documentation/docs/03-template-syntax/18-class.md b/documentation/docs/03-template-syntax/18-class.md index 880a34e9ec..1ea4a208df 100644 --- a/documentation/docs/03-template-syntax/18-class.md +++ b/documentation/docs/03-template-syntax/18-class.md @@ -71,6 +71,18 @@ The user of this component has the same flexibility to use a mixture of objects, </Button> ``` +Svelte also exposes the `ClassValue` type, which is the type of value that the `class` attribute on elements accept. This is useful if you want to use a type-safe class name in component props: + +```svelte +<script lang="ts"> + import type { ClassValue } from 'svelte/elements'; + + const props: { class: ClassValue } = $props(); +</script> + +<div class={['original', props.class]}>...</div> +``` + ## The `class:` directive Prior to Svelte 5.16, the `class:` directive was the most convenient way to set classes on elements conditionally. diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts index 604403f0a2..96f1589800 100644 --- a/packages/svelte/elements.d.ts +++ b/packages/svelte/elements.d.ts @@ -741,7 +741,7 @@ export interface HTMLAttributes<T extends EventTarget> extends AriaAttributes, D accesskey?: string | undefined | null; autocapitalize?: 'characters' | 'off' | 'on' | 'none' | 'sentences' | 'words' | undefined | null; autofocus?: boolean | undefined | null; - class?: string | import('clsx').ClassArray | import('clsx').ClassDictionary | undefined | null; + class?: ClassValue | undefined | null; contenteditable?: Booleanish | 'inherit' | 'plaintext-only' | undefined | null; contextmenu?: string | undefined | null; dir?: 'ltr' | 'rtl' | 'auto' | undefined | null; @@ -1522,7 +1522,7 @@ export interface SvelteWindowAttributes extends HTMLAttributes<Window> { export interface SVGAttributes<T extends EventTarget> extends AriaAttributes, DOMAttributes<T> { // Attributes which also defined in HTMLAttributes className?: string | undefined | null; - class?: string | import('clsx').ClassArray | import('clsx').ClassDictionary | undefined | null; + class?: ClassValue | undefined | null; color?: string | undefined | null; height?: number | string | undefined | null; id?: string | undefined | null; @@ -2059,3 +2059,5 @@ export interface SvelteHTMLElements { [name: string]: { [name: string]: any }; } + +export type ClassValue = string | import('clsx').ClassArray | import('clsx').ClassDictionary; From 509ba568f3b16c82c9a2e0c55e5b3634edd46e85 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 17 Jan 2025 10:32:51 -0500 Subject: [PATCH 0098/1429] fix: create fewer deriveds for concatenated strings (#15041) * fix: create fewer deriveds for concatenated strings * stringified_text -> expression --- .changeset/rich-donkeys-wink.md | 5 +++++ .../phases/3-transform/client/visitors/shared/utils.js | 4 ++-- .../text-nodes-deriveds/_expected/client/index.svelte.js | 6 +++--- 3 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 .changeset/rich-donkeys-wink.md diff --git a/.changeset/rich-donkeys-wink.md b/.changeset/rich-donkeys-wink.md new file mode 100644 index 0000000000..be8d02b670 --- /dev/null +++ b/.changeset/rich-donkeys-wink.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: create fewer deriveds for concatenated strings diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index 1854baa1e9..69fd883214 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -47,8 +47,8 @@ export function build_template_chunk(values, visit, state) { quasi.value.cooked += node.expression.value + ''; } } else { - if (contains_multiple_call_expression) { - const id = b.id(state.scope.generate('stringified_text')); + if (node.metadata.expression.has_call && contains_multiple_call_expression) { + const id = b.id(state.scope.generate('expression')); state.init.push( b.const( id, diff --git a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js index 8e343fcf36..2e0bd651da 100644 --- a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js @@ -16,11 +16,11 @@ export default function Text_nodes_deriveds($$anchor) { } var p = root(); - const stringified_text = $.derived(() => text1() ?? ''); - const stringified_text_1 = $.derived(() => text2() ?? ''); + const expression = $.derived(() => text1() ?? ''); + const expression_1 = $.derived(() => text2() ?? ''); var text = $.child(p); - $.template_effect(() => $.set_text(text, `${$.get(stringified_text)}${$.get(stringified_text_1)}`)); + $.template_effect(() => $.set_text(text, `${$.get(expression)}${$.get(expression_1)}`)); $.reset(p); $.append($$anchor, p); } \ No newline at end of file From a9d1f46dbb39a38d5fb15ca5721646c347139aa6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:36:38 -0500 Subject: [PATCH 0099/1429] Version Packages (#15021) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/rich-donkeys-wink.md | 5 ----- .changeset/thirty-starfishes-shout.md | 5 ----- .changeset/violet-apes-punch.md | 5 ----- packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 14 insertions(+), 17 deletions(-) delete mode 100644 .changeset/rich-donkeys-wink.md delete mode 100644 .changeset/thirty-starfishes-shout.md delete mode 100644 .changeset/violet-apes-punch.md diff --git a/.changeset/rich-donkeys-wink.md b/.changeset/rich-donkeys-wink.md deleted file mode 100644 index be8d02b670..0000000000 --- a/.changeset/rich-donkeys-wink.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: create fewer deriveds for concatenated strings diff --git a/.changeset/thirty-starfishes-shout.md b/.changeset/thirty-starfishes-shout.md deleted file mode 100644 index 3c577dcee1..0000000000 --- a/.changeset/thirty-starfishes-shout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: Expose `ClassValue` from `svelte/elements` diff --git a/.changeset/violet-apes-punch.md b/.changeset/violet-apes-punch.md deleted file mode 100644 index 77f4e923c8..0000000000 --- a/.changeset/violet-apes-punch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly parse leading comments in function binding diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 0c2313ee71..59ac9f2864 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.19.0 + +### Minor Changes + +- feat: Expose `ClassValue` from `svelte/elements` ([#15035](https://github.com/sveltejs/svelte/pull/15035)) + +### Patch Changes + +- fix: create fewer deriveds for concatenated strings ([#15041](https://github.com/sveltejs/svelte/pull/15041)) + +- fix: correctly parse leading comments in function binding ([#15020](https://github.com/sveltejs/svelte/pull/15020)) + ## 5.18.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 2dbd937d04..95e7a7319d 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.18.0", + "version": "5.19.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 4a7a6d03df..7347e7546d 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.18.0'; +export const VERSION = '5.19.0'; export const PUBLIC_VERSION = '5'; From 700029bd4d459a39142927ecef290342f81614c1 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Sat, 18 Jan 2025 10:26:47 -0500 Subject: [PATCH 0100/1429] fix: more efficient template effect grouping (#15050) * WIP * drive-by * group attribute updates * fix * more * unused * more * WIP * unused * simplify * simplify * simplify * more * unused * unused * more * tweak * update how class/style directives are handled * more * more * simplify * changeset --- .changeset/cyan-games-cheat.md | 5 + .../phases/2-analyze/visitors/Attribute.js | 3 - .../2-analyze/visitors/shared/function.js | 3 +- .../3-transform/client/transform-client.js | 2 +- .../phases/3-transform/client/types.d.ts | 4 +- .../3-transform/client/visitors/Fragment.js | 21 +-- .../client/visitors/RegularElement.js | 58 +++---- .../client/visitors/SlotElement.js | 7 +- .../client/visitors/SvelteBoundary.js | 2 +- .../client/visitors/SvelteElement.js | 16 +- .../client/visitors/shared/component.js | 16 +- .../client/visitors/shared/element.js | 64 ++++---- .../client/visitors/shared/fragment.js | 8 +- .../client/visitors/shared/utils.js | 154 ++++++++++++------ packages/svelte/src/compiler/phases/nodes.js | 1 - .../svelte/src/compiler/types/template.d.ts | 1 - .../src/internal/client/reactivity/effects.js | 16 +- .../_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../_expected/client/main.svelte.js | 21 +-- .../_expected/client/index.svelte.js | 4 +- 24 files changed, 227 insertions(+), 199 deletions(-) create mode 100644 .changeset/cyan-games-cheat.md diff --git a/.changeset/cyan-games-cheat.md b/.changeset/cyan-games-cheat.md new file mode 100644 index 0000000000..d90901783b --- /dev/null +++ b/.changeset/cyan-games-cheat.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: more efficient template effect grouping diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js index 9d801e095e..41144fc74c 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -61,9 +61,6 @@ export function Attribute(node, context) { ) { continue; } - - node.metadata.expression.has_state ||= chunk.metadata.expression.has_state; - node.metadata.expression.has_call ||= chunk.metadata.expression.has_call; } if (is_event_attribute(node)) { diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/function.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/function.js index c6151992bf..c892efd421 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/function.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/function.js @@ -15,6 +15,7 @@ export function visit_function(node, context) { context.next({ ...context.state, - function_depth: context.state.function_depth + 1 + function_depth: context.state.function_depth + 1, + expression: null }); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 582c32b534..f4a6c9a414 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -169,9 +169,9 @@ export function client_component(analysis, options) { module_level_snippets: [], // these are set inside the `Fragment` visitor, and cannot be used until then - before_init: /** @type {any} */ (null), init: /** @type {any} */ (null), update: /** @type {any} */ (null), + expressions: /** @type {any} */ (null), after_update: /** @type {any} */ (null), template: /** @type {any} */ (null), locations: /** @type {any} */ (null) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts index 5c8476de3e..db4adf451c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts +++ b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts @@ -46,14 +46,14 @@ export interface ComponentClientTransformState extends ClientTransformState { readonly events: Set<string>; readonly is_instance: boolean; - /** Stuff that happens before the render effect(s) */ - readonly before_init: Statement[]; /** Stuff that happens before the render effect(s) */ readonly init: Statement[]; /** Stuff that happens inside the render effect */ readonly update: Statement[]; /** Stuff that happens after the render effect (control blocks, dynamic elements, bindings, actions, etc) */ readonly after_update: Statement[]; + /** Expressions used inside the render effect */ + readonly expressions: Expression[]; /** The HTML template string */ readonly template: Array<string | Expression>; readonly locations: SourceLocation[]; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js index 0e6ea29614..e9cfd9c506 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js @@ -61,9 +61,9 @@ export function Fragment(node, context) { /** @type {ComponentClientTransformState} */ const state = { ...context.state, - before_init: [], init: [], update: [], + expressions: [], after_update: [], template: [], locations: [], @@ -124,18 +124,13 @@ export function Fragment(node, context) { add_template(template_name, args); - body.push(b.var(id, b.call(template_name)), ...state.before_init, ...state.init); + body.push(b.var(id, b.call(template_name))); close = b.stmt(b.call('$.append', b.id('$$anchor'), id)); } else if (is_single_child_not_needing_template) { context.visit(trimmed[0], state); - body.push(...state.before_init, ...state.init); } else if (trimmed.length === 1 && trimmed[0].type === 'Text') { const id = b.id(context.state.scope.generate('text')); - body.push( - b.var(id, b.call('$.text', b.literal(trimmed[0].data))), - ...state.before_init, - ...state.init - ); + body.push(b.var(id, b.call('$.text', b.literal(trimmed[0].data)))); close = b.stmt(b.call('$.append', b.id('$$anchor'), id)); } else if (trimmed.length > 0) { const id = b.id(context.state.scope.generate('fragment')); @@ -153,7 +148,7 @@ export function Fragment(node, context) { state }); - body.push(b.var(id, b.call('$.text')), ...state.before_init, ...state.init); + body.push(b.var(id, b.call('$.text'))); close = b.stmt(b.call('$.append', b.id('$$anchor'), id)); } else { if (is_standalone) { @@ -182,15 +177,13 @@ export function Fragment(node, context) { close = b.stmt(b.call('$.append', b.id('$$anchor'), id)); } - - body.push(...state.before_init, ...state.init); } - } else { - body.push(...state.before_init, ...state.init); } + body.push(...state.init); + if (state.update.length > 0) { - body.push(build_render_statement(state.update)); + body.push(build_render_statement(state)); } body.push(...state.after_update); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index ffd06dfd86..21a78de032 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -1,4 +1,4 @@ -/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */ +/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { SourceLocation } from '#shared' */ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */ @@ -16,7 +16,7 @@ import { is_event_attribute, is_text_attribute } from '../../../../utils/ast.js' import * as b from '../../../../utils/builders.js'; import { is_custom_element_node } from '../../../nodes.js'; import { clean_nodes, determine_namespace_for_children } from '../../utils.js'; -import { build_getter, create_derived } from '../utils.js'; +import { build_getter } from '../utils.js'; import { get_attribute_name, build_attribute_value, @@ -28,8 +28,8 @@ import { process_children } from './shared/fragment.js'; import { build_render_statement, build_template_chunk, - build_update, - build_update_assignment + build_update_assignment, + get_expression_id } from './shared/utils.js'; import { visit_event_attribute } from './shared/events.js'; @@ -409,7 +409,7 @@ export function RegularElement(node, context) { b.block([ ...child_state.init, ...element_state.init, - child_state.update.length > 0 ? build_render_statement(child_state.update) : b.empty, + child_state.update.length > 0 ? build_render_statement(child_state) : b.empty, ...child_state.after_update, ...element_state.after_update ]) @@ -536,7 +536,10 @@ function build_element_attribute_update_assignment( const name = get_attribute_name(element, attribute); const is_svg = context.state.metadata.namespace === 'svg' || element.name === 'svg'; const is_mathml = context.state.metadata.namespace === 'mathml'; - let { has_call, value } = build_attribute_value(attribute.value, context); + + let { value, has_state } = build_attribute_value(attribute.value, context, (value) => + get_expression_id(state, value) + ); if (name === 'autofocus') { state.init.push(b.stmt(b.call('$.autofocus', node_id, value))); @@ -557,15 +560,6 @@ function build_element_attribute_update_assignment( value = b.call('$.clsx', value); } - if (attribute.metadata.expression.has_state && has_call) { - // ensure we're not creating a separate template effect for this so that - // potential class directives are added to the same effect and therefore always apply - const id = b.id(state.scope.generate('class_derived')); - state.init.push(b.const(id, create_derived(state, b.thunk(value)))); - value = b.call('$.get', id); - has_call = false; - } - update = b.stmt( b.call( is_svg ? '$.set_svg_class' : is_mathml ? '$.set_mathml_class' : '$.set_class', @@ -605,14 +599,6 @@ function build_element_attribute_update_assignment( } else if (is_dom_property(name)) { update = b.stmt(b.assignment('=', b.member(node_id, name), value)); } else { - if (name === 'style' && attribute.metadata.expression.has_state && has_call) { - // ensure we're not creating a separate template effect for this so that - // potential style directives are added to the same effect and therefore always apply - const id = b.id(state.scope.generate('style_derived')); - state.init.push(b.const(id, create_derived(state, b.thunk(value)))); - value = b.call('$.get', id); - has_call = false; - } const callee = name.startsWith('xlink') ? '$.set_xlink_attribute' : '$.set_attribute'; update = b.stmt( b.call( @@ -625,12 +611,8 @@ function build_element_attribute_update_assignment( ); } - if (attribute.metadata.expression.has_state) { - if (has_call) { - state.init.push(build_update(update)); - } else { - state.update.push(update); - } + if (has_state) { + state.update.push(update); return true; } else { state.init.push(update); @@ -648,7 +630,7 @@ function build_element_attribute_update_assignment( function build_custom_element_attribute_update_assignment(node_id, attribute, context) { const state = context.state; const name = attribute.name; // don't lowercase, as we set the element's property, which might be case sensitive - let { has_call, value } = build_attribute_value(attribute.value, context); + let { value, has_state } = build_attribute_value(attribute.value, context); // We assume that noone's going to redefine the semantics of the class attribute on custom elements, i.e. it's still used for CSS classes if (name === 'class' && attribute.metadata.needs_clsx) { @@ -660,12 +642,10 @@ function build_custom_element_attribute_update_assignment(node_id, attribute, co const update = b.stmt(b.call('$.set_custom_element_data', node_id, b.literal(name), value)); - if (attribute.metadata.expression.has_state) { - if (has_call) { - state.init.push(build_update(update)); - } else { - state.update.push(update); - } + if (has_state) { + // this is different from other updates — it doesn't get grouped, + // because set_custom_element_data may not be idempotent + state.init.push(b.stmt(b.call('$.template_effect', b.thunk(update.expression)))); return true; } else { state.init.push(update); @@ -685,7 +665,9 @@ function build_custom_element_attribute_update_assignment(node_id, attribute, co */ function build_element_special_value_attribute(element, node_id, attribute, context) { const state = context.state; - const { value } = build_attribute_value(attribute.value, context); + const { value, has_state } = build_attribute_value(attribute.value, context, (value) => + get_expression_id(state, value) + ); const inner_assignment = b.assignment( '=', @@ -719,7 +701,7 @@ function build_element_special_value_attribute(element, node_id, attribute, cont state.init.push(b.stmt(b.call('$.init_select', node_id, b.thunk(value)))); } - if (attribute.metadata.expression.has_state) { + if (has_state) { const id = state.scope.generate(`${node_id.name}_value`); build_update_assignment( state, diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js index 86734a07ab..f1b08acbc6 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js @@ -3,6 +3,7 @@ /** @import { ComponentContext } from '../types' */ import * as b from '../../../../utils/builders.js'; import { build_attribute_value } from './shared/element.js'; +import { memoize_expression } from './shared/utils.js'; /** * @param {AST.SlotElement} node @@ -29,13 +30,15 @@ export function SlotElement(node, context) { if (attribute.type === 'SpreadAttribute') { spreads.push(b.thunk(/** @type {Expression} */ (context.visit(attribute)))); } else if (attribute.type === 'Attribute') { - const { value } = build_attribute_value(attribute.value, context); + const { value, has_state } = build_attribute_value(attribute.value, context, (value) => + memoize_expression(context.state, value) + ); if (attribute.name === 'name') { name = /** @type {Literal} */ (value); is_default = false; } else if (attribute.name !== 'slot') { - if (attribute.metadata.expression.has_state) { + if (has_state) { props.push(b.get(attribute.name, [b.return(value)])); } else { props.push(b.init(attribute.name, value)); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js index 325485d4c0..6da650a591 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js @@ -23,7 +23,7 @@ export function SvelteBoundary(node, context) { const expression = /** @type {Expression} */ (context.visit(chunk.expression, context.state)); - if (attribute.metadata.expression.has_state) { + if (chunk.metadata.expression.has_state) { props.properties.push(b.get(attribute.name, [b.return(expression)])); } else { props.properties.push(b.init(attribute.name, expression)); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js index ba66fe29d6..e275283655 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js @@ -1,12 +1,8 @@ -/** @import { BlockStatement, Expression, ExpressionStatement, Identifier, ObjectExpression, Statement } from 'estree' */ +/** @import { BlockStatement, Expression, ExpressionStatement, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import { dev, is_ignored, locator } from '../../../../state.js'; -import { - get_attribute_expression, - is_event_attribute, - is_text_attribute -} from '../../../../utils/ast.js'; +import { dev, locator } from '../../../../state.js'; +import { is_text_attribute } from '../../../../utils/ast.js'; import * as b from '../../../../utils/builders.js'; import { determine_namespace_for_children } from '../../utils.js'; import { @@ -15,7 +11,7 @@ import { build_set_attributes, build_style_directives } from './shared/element.js'; -import { build_render_statement, build_update } from './shared/utils.js'; +import { build_render_statement } from './shared/utils.js'; /** * @param {AST.SvelteElement} node @@ -49,9 +45,9 @@ export function SvelteElement(node, context) { state: { ...context.state, node: element_id, - before_init: [], init: [], update: [], + expressions: [], after_update: [] } }; @@ -123,7 +119,7 @@ export function SvelteElement(node, context) { /** @type {Statement[]} */ const inner = inner_context.state.init; if (inner_context.state.update.length > 0) { - inner.push(build_render_statement(inner_context.state.update)); + inner.push(build_render_statement(inner_context.state)); } inner.push(...inner_context.state.after_update); inner.push( diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js index f509cb41a7..9ac0bac120 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js @@ -5,7 +5,7 @@ import { dev, is_ignored } from '../../../../../state.js'; import { get_attribute_chunks, object } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; import { create_derived } from '../../utils.js'; -import { build_bind_this, validate_binding } from '../shared/utils.js'; +import { build_bind_this, memoize_expression, validate_binding } from '../shared/utils.js'; import { build_attribute_value } from '../shared/element.js'; import { build_event_handler } from './events.js'; import { determine_slot } from '../../../../../utils/slot.js'; @@ -132,7 +132,13 @@ export function build_component(node, component_name, context, anchor = context. } else if (attribute.type === 'Attribute') { if (attribute.name.startsWith('--')) { custom_css_props.push( - b.init(attribute.name, build_attribute_value(attribute.value, context).value) + b.init( + attribute.name, + build_attribute_value(attribute.value, context, (value) => + // TODO put the derived in the local block + memoize_expression(context.state, value) + ).value + ) ); continue; } @@ -145,9 +151,11 @@ export function build_component(node, component_name, context, anchor = context. has_children_prop = true; } - const { value } = build_attribute_value(attribute.value, context); + const { value, has_state } = build_attribute_value(attribute.value, context, (value) => + memoize_expression(context.state, value) + ); - if (attribute.metadata.expression.has_state) { + if (has_state) { let arg = value; // When we have a non-simple computation, anything other than an Identifier or Member expression, diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index 1b0737e31e..8fb6b8bdde 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -1,12 +1,12 @@ /** @import { Expression, Identifier, ObjectExpression } from 'estree' */ -/** @import { AST, Namespace } from '#compiler' */ +/** @import { AST } from '#compiler' */ /** @import { ComponentClientTransformState, ComponentContext } from '../../types' */ import { normalize_attribute } from '../../../../../../utils.js'; import { is_ignored } from '../../../../../state.js'; -import { get_attribute_expression, is_event_attribute } from '../../../../../utils/ast.js'; +import { is_event_attribute } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; import { build_getter, create_derived } from '../../utils.js'; -import { build_template_chunk, build_update } from './utils.js'; +import { build_template_chunk, get_expression_id } from './utils.js'; /** * @param {Array<AST.Attribute | AST.SpreadAttribute>} attributes @@ -28,14 +28,16 @@ export function build_set_attributes( is_custom_element, state ) { - let has_state = false; + let is_dynamic = false; /** @type {ObjectExpression['properties']} */ const values = []; for (const attribute of attributes) { if (attribute.type === 'Attribute') { - const { value } = build_attribute_value(attribute.value, context); + const { value, has_state } = build_attribute_value(attribute.value, context, (value) => + get_expression_id(context.state, value) + ); if ( is_event_attribute(attribute) && @@ -49,10 +51,10 @@ export function build_set_attributes( values.push(b.init(attribute.name, value)); } - has_state ||= attribute.metadata.expression.has_state; + is_dynamic ||= has_state; } else { // objects could contain reactive getters -> play it safe and always assume spread attributes are reactive - has_state = true; + is_dynamic = true; let value = /** @type {Expression} */ (context.visit(attribute)); @@ -68,7 +70,7 @@ export function build_set_attributes( const call = b.call( '$.set_attributes', element_id, - has_state ? attributes_id : b.literal(null), + is_dynamic ? attributes_id : b.literal(null), b.object(values), context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash), preserve_attribute_case, @@ -76,7 +78,7 @@ export function build_set_attributes( is_ignored(element, 'hydration_attribute_changed') && b.true ); - if (has_state) { + if (is_dynamic) { context.state.init.push(b.let(attributes_id)); const update = b.stmt(b.assignment('=', attributes_id, call)); context.state.update.push(update); @@ -104,19 +106,14 @@ export function build_style_directives( const state = context.state; for (const directive of style_directives) { - const { has_state, has_call } = directive.metadata.expression; + const { has_state } = directive.metadata.expression; let value = directive.value === true ? build_getter({ name: directive.name, type: 'Identifier' }, context.state) - : build_attribute_value(directive.value, context).value; - - if (has_call) { - const id = b.id(state.scope.generate('style_directive')); - - state.init.push(b.const(id, create_derived(state, b.thunk(value)))); - value = b.call('$.get', id); - } + : build_attribute_value(directive.value, context, (value) => + get_expression_id(context.state, value) + ).value; const update = b.stmt( b.call( @@ -128,9 +125,7 @@ export function build_style_directives( ) ); - if (!is_attributes_reactive && has_call) { - state.init.push(build_update(update)); - } else if (is_attributes_reactive || has_state || has_call) { + if (has_state || is_attributes_reactive) { state.update.push(update); } else { state.init.push(update); @@ -158,17 +153,12 @@ export function build_class_directives( let value = /** @type {Expression} */ (context.visit(directive.expression)); if (has_call) { - const id = b.id(state.scope.generate('class_directive')); - - state.init.push(b.const(id, create_derived(state, b.thunk(value)))); - value = b.call('$.get', id); + value = get_expression_id(state, value); } const update = b.stmt(b.call('$.toggle_class', element_id, b.literal(directive.name), value)); - if (!is_attributes_reactive && has_call) { - state.init.push(build_update(update)); - } else if (is_attributes_reactive || has_state || has_call) { + if (is_attributes_reactive || has_state) { state.update.push(update); } else { state.init.push(update); @@ -179,28 +169,30 @@ export function build_class_directives( /** * @param {AST.Attribute['value']} value * @param {ComponentContext} context - * @returns {{ value: Expression, has_state: boolean, has_call: boolean }} + * @param {(value: Expression) => Expression} memoize + * @returns {{ value: Expression, has_state: boolean }} */ -export function build_attribute_value(value, context) { +export function build_attribute_value(value, context, memoize = (value) => value) { if (value === true) { - return { has_state: false, has_call: false, value: b.literal(true) }; + return { value: b.literal(true), has_state: false }; } if (!Array.isArray(value) || value.length === 1) { const chunk = Array.isArray(value) ? value[0] : value; if (chunk.type === 'Text') { - return { has_state: false, has_call: false, value: b.literal(chunk.data) }; + return { value: b.literal(chunk.data), has_state: false }; } + let expression = /** @type {Expression} */ (context.visit(chunk.expression)); + return { - has_state: chunk.metadata.expression.has_state, - has_call: chunk.metadata.expression.has_call, - value: /** @type {Expression} */ (context.visit(chunk.expression)) + value: chunk.metadata.expression.has_call ? memoize(expression) : expression, + has_state: chunk.metadata.expression.has_state }; } - return build_template_chunk(value, context.visit, context.state); + return build_template_chunk(value, context.visit, context.state, memoize); } /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js index 7674fd1eb2..0b4ac87342 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js @@ -4,7 +4,7 @@ import { cannot_be_set_statically } from '../../../../../../utils.js'; import { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; -import { build_template_chunk, build_update } from './utils.js'; +import { build_template_chunk } from './utils.js'; /** * Processes an array of template nodes, joining sibling text/expression nodes @@ -69,7 +69,7 @@ export function process_children(nodes, initial, is_element, { visit, state }) { state.template.push(' '); - const { has_state, has_call, value } = build_template_chunk(sequence, visit, state); + const { has_state, value } = build_template_chunk(sequence, visit, state); // if this is a standalone `{expression}`, make sure we handle the case where // no text node was created because the expression was empty during SSR @@ -78,9 +78,7 @@ export function process_children(nodes, initial, is_element, { visit, state }) { const update = b.stmt(b.call('$.set_text', id, value)); - if (has_call && !within_bound_contenteditable) { - state.init.push(build_update(update)); - } else if (has_state && !within_bound_contenteditable) { + if (has_state && !within_bound_contenteditable) { state.update.push(update); } else { state.init.push(b.stmt(b.assignment('=', b.member(id, 'nodeValue'), value))); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index 69fd883214..9dd2984329 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -6,36 +6,95 @@ import { object } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; import { sanitize_template_string } from '../../../../../utils/sanitize_template_string.js'; import { regex_is_valid_identifier } from '../../../../patterns.js'; -import { create_derived } from '../../utils.js'; import is_reference from 'is-reference'; import { locator } from '../../../../../state.js'; +import { create_derived } from '../../utils.js'; + +/** + * @param {ComponentClientTransformState} state + * @param {Expression} value + */ +export function memoize_expression(state, value) { + const id = b.id(state.scope.generate('expression')); + state.init.push(b.const(id, create_derived(state, b.thunk(value)))); + return b.call('$.get', id); +} + +/** + * + * @param {ComponentClientTransformState} state + * @param {Expression} value + */ +export function get_expression_id(state, value) { + for (let i = 0; i < state.expressions.length; i += 1) { + if (compare_expressions(state.expressions[i], value)) { + return b.id(`$${i}`); + } + } + + return b.id(`$${state.expressions.push(value) - 1}`); +} + +/** + * Returns true of two expressions have an identical AST shape + * @param {Expression} a + * @param {Expression} b + */ +function compare_expressions(a, b) { + if (a.type !== b.type) { + return false; + } + + for (const key in a) { + if (key === 'type' || key === 'metadata' || key === 'loc' || key === 'start' || key === 'end') { + continue; + } + + const va = /** @type {any} */ (a)[key]; + const vb = /** @type {any} */ (b)[key]; + + if ((typeof va === 'object') !== (typeof vb === 'object')) { + return false; + } + + if (typeof va !== 'object' || va === null || vb === null) { + if (va !== vb) return false; + } else if (Array.isArray(va)) { + if (va.length !== vb.length) { + return false; + } + + if (va.some((v, i) => !compare_expressions(v, vb[i]))) { + return false; + } + } else if (!compare_expressions(va, vb)) { + return false; + } + } + + return true; +} /** * @param {Array<AST.Text | AST.ExpressionTag>} values * @param {(node: AST.SvelteNode, state: any) => any} visit * @param {ComponentClientTransformState} state - * @returns {{ value: Expression, has_state: boolean, has_call: boolean }} + * @param {(value: Expression) => Expression} memoize + * @returns {{ value: Expression, has_state: boolean }} */ -export function build_template_chunk(values, visit, state) { +export function build_template_chunk( + values, + visit, + state, + memoize = (value) => get_expression_id(state, value) +) { /** @type {Expression[]} */ const expressions = []; let quasi = b.quasi(''); const quasis = [quasi]; - let has_call = false; let has_state = false; - let contains_multiple_call_expression = false; - - for (const node of values) { - if (node.type === 'ExpressionTag') { - const metadata = node.metadata.expression; - - contains_multiple_call_expression ||= has_call && metadata.has_call; - has_call ||= metadata.has_call; - has_state ||= metadata.has_state; - } - } for (let i = 0; i < values.length; i++) { const node = values[i]; @@ -47,30 +106,20 @@ export function build_template_chunk(values, visit, state) { quasi.value.cooked += node.expression.value + ''; } } else { - if (node.metadata.expression.has_call && contains_multiple_call_expression) { - const id = b.id(state.scope.generate('expression')); - state.init.push( - b.const( - id, - create_derived( - state, - b.thunk( - b.logical( - '??', - /** @type {Expression} */ (visit(node.expression, state)), - b.literal('') - ) - ) - ) - ) - ); - expressions.push(b.call('$.get', id)); - } else if (values.length === 1) { + let value = /** @type {Expression} */ (visit(node.expression, state)); + + has_state ||= node.metadata.expression.has_state; + + if (node.metadata.expression.has_call) { + value = memoize(value); + } + + if (values.length === 1) { // If we have a single expression, then pass that in directly to possibly avoid doing // extra work in the template_effect (instead we do the work in set_text). - return { value: visit(node.expression, state), has_state, has_call }; + return { value, has_state }; } else { - expressions.push(b.logical('??', visit(node.expression, state), b.literal(''))); + expressions.push(b.logical('??', value, b.literal(''))); } quasi = b.quasi('', i + 1 === values.length); @@ -84,26 +133,27 @@ export function build_template_chunk(values, visit, state) { const value = b.template(quasis, expressions); - return { value, has_state, has_call }; -} - -/** - * @param {Statement} statement - */ -export function build_update(statement) { - const body = - statement.type === 'ExpressionStatement' ? statement.expression : b.block([statement]); - - return b.stmt(b.call('$.template_effect', b.thunk(body))); + return { value, has_state }; } /** - * @param {Statement[]} update + * @param {ComponentClientTransformState} state */ -export function build_render_statement(update) { - return update.length === 1 - ? build_update(update[0]) - : b.stmt(b.call('$.template_effect', b.thunk(b.block(update)))); +export function build_render_statement(state) { + return b.stmt( + b.call( + '$.template_effect', + b.arrow( + state.expressions.map((_, i) => b.id(`$${i}`)), + state.update.length === 1 && state.update[0].type === 'ExpressionStatement' + ? state.update[0].expression + : b.block(state.update) + ), + state.expressions.length > 0 && + b.array(state.expressions.map((expression) => b.thunk(expression))), + state.expressions.length > 0 && !state.analysis.runes && b.id('$.derived_safe_equal') + ) + ); } /** diff --git a/packages/svelte/src/compiler/phases/nodes.js b/packages/svelte/src/compiler/phases/nodes.js index 5066833feb..5ca4ce3380 100644 --- a/packages/svelte/src/compiler/phases/nodes.js +++ b/packages/svelte/src/compiler/phases/nodes.js @@ -44,7 +44,6 @@ export function create_attribute(name, start, end, value) { name, value, metadata: { - expression: create_expression_metadata(), delegated: null, needs_clsx: false } diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index 8be9aed177..fb60966895 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -479,7 +479,6 @@ export namespace AST { value: true | ExpressionTag | Array<Text | ExpressionTag>; /** @internal */ metadata: { - expression: ExpressionMetadata; /** May be set if this is an event attribute */ delegated: null | DelegatedEvent; /** May be `true` if this is a `class` attribute that needs `clsx` */ diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 428f69281b..e854633150 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -43,7 +43,8 @@ import * as e from '../errors.js'; import { DEV } from 'esm-env'; import { define_property } from '../../shared/utils.js'; import { get_next_sibling } from '../dom/operations.js'; -import { destroy_derived } from './deriveds.js'; +import { derived, derived_safe_equal, destroy_derived } from './deriveds.js'; +import { legacy_mode_flag } from '../../flags/index.js'; /** * @param {'$effect' | '$effect.pre' | '$inspect'} rune @@ -343,16 +344,21 @@ export function render_effect(fn) { } /** - * @param {() => void | (() => void)} fn + * @param {(...expressions: any) => void | (() => void)} fn + * @param {Array<() => any>} thunks * @returns {Effect} */ -export function template_effect(fn) { +export function template_effect(fn, thunks = [], d = derived) { + const deriveds = thunks.map(d); + const effect = () => fn(...deriveds.map(get)); + if (DEV) { - define_property(fn, 'name', { + define_property(effect, 'name', { value: '{expression}' }); } - return block(fn); + + return block(effect); } /** diff --git a/packages/svelte/tests/runtime-legacy/samples/lifecycle-render-order-for-children/_config.js b/packages/svelte/tests/runtime-legacy/samples/lifecycle-render-order-for-children/_config.js index 73bfd09ceb..99f9681c4b 100644 --- a/packages/svelte/tests/runtime-legacy/samples/lifecycle-render-order-for-children/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/lifecycle-render-order-for-children/_config.js @@ -16,13 +16,13 @@ export default test({ test({ assert, compileOptions, component }) { assert.deepEqual(order, [ 'parent: beforeUpdate 0', - 'parent: render 0', '1: beforeUpdate 0', '1: render 0', '2: beforeUpdate 0', '2: render 0', '3: beforeUpdate 0', '3: render 0', + 'parent: render 0', '1: onMount 0', '1: afterUpdate 0', '2: onMount 0', @@ -39,13 +39,13 @@ export default test({ assert.deepEqual(order, [ 'parent: beforeUpdate 1', - 'parent: render 1', '1: beforeUpdate 1', '1: render 1', '2: beforeUpdate 1', '2: render 1', '3: beforeUpdate 1', '3: render 1', + 'parent: render 1', '1: afterUpdate 1', '2: afterUpdate 1', '3: afterUpdate 1', diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/_config.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/_config.js index 1066d9a2df..cb8e648645 100644 --- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-2/_config.js @@ -10,7 +10,6 @@ export default test({ assert.deepEqual(logs, [ 'parent: $effect.pre 0', 'parent: $effect.pre (2) 0', - 'parent: render 0', '1: $effect.pre 0', '1: $effect.pre (2) 0', '1: render 0', @@ -20,6 +19,7 @@ export default test({ '3: $effect.pre 0', '3: $effect.pre (2) 0', '3: render 0', + 'parent: render 0', '1: $effect 0', '2: $effect 0', '3: $effect 0', @@ -33,7 +33,6 @@ export default test({ assert.deepEqual(logs, [ 'parent: $effect.pre 1', 'parent: $effect.pre (2) 1', - 'parent: render 1', '1: $effect.pre 1', '1: $effect.pre (2) 1', '1: render 1', @@ -43,6 +42,7 @@ export default test({ '3: $effect.pre 1', '3: $effect.pre (2) 1', '3: render 1', + 'parent: render 1', '1: $effect 1', '2: $effect 1', '3: $effect 1', diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/_config.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/_config.js index 55847c35a2..6c063bcb3e 100644 --- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-3/_config.js @@ -8,13 +8,13 @@ export default test({ async test({ assert, component, logs }) { assert.deepEqual(logs, [ - 'parent: render 0', '1: $effect.pre 0', '1: render 0', '2: $effect.pre 0', '2: render 0', '3: $effect.pre 0', '3: render 0', + 'parent: render 0', '1: $effect 0', '2: $effect 0', '3: $effect 0', @@ -26,13 +26,13 @@ export default test({ flushSync(() => (component.n += 1)); assert.deepEqual(logs, [ - 'parent: render 1', '1: $effect.pre 1', '1: render 1', '2: $effect.pre 1', '2: render 1', '3: $effect.pre 1', '3: render 1', + 'parent: render 1', '1: $effect 1', '2: $effect 1', '3: $effect 1', diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/_config.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/_config.js index 0cd7e15a37..29b0b67a52 100644 --- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children-4/_config.js @@ -10,7 +10,6 @@ export default test({ assert.deepEqual(logs, [ 'parent: $effect.pre 0', 'parent: nested $effect.pre 0', - 'parent: render 0', '1: $effect.pre 0', '1: nested $effect.pre 0', '1: render 0', @@ -20,6 +19,7 @@ export default test({ '3: $effect.pre 0', '3: nested $effect.pre 0', '3: render 0', + 'parent: render 0', '1: $effect 0', '2: $effect 0', '3: $effect 0', @@ -33,7 +33,6 @@ export default test({ assert.deepEqual(logs, [ 'parent: $effect.pre 1', 'parent: nested $effect.pre 1', - 'parent: render 1', '1: $effect.pre 1', '1: nested $effect.pre 1', '1: render 1', @@ -43,6 +42,7 @@ export default test({ '3: $effect.pre 1', '3: nested $effect.pre 1', '3: render 1', + 'parent: render 1', '1: $effect 1', '2: $effect 1', '3: $effect 1', diff --git a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/_config.js b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/_config.js index 19b8fb3938..3138ec7231 100644 --- a/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/lifecycle-render-order-for-children/_config.js @@ -9,13 +9,13 @@ export default test({ async test({ assert, component, logs }) { assert.deepEqual(logs, [ 'parent: $effect.pre 0', - 'parent: render 0', '1: $effect.pre 0', '1: render 0', '2: $effect.pre 0', '2: render 0', '3: $effect.pre 0', '3: render 0', + 'parent: render 0', '1: $effect 0', '2: $effect 0', '3: $effect 0', @@ -28,13 +28,13 @@ export default test({ assert.deepEqual(logs, [ 'parent: $effect.pre 1', - 'parent: render 1', '1: $effect.pre 1', '1: render 1', '2: $effect.pre 1', '2: render 1', '3: $effect.pre 1', '3: render 1', + 'parent: render 1', '1: $effect 1', '2: $effect 1', '3: $effect 1', diff --git a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js index ce77a27e19..d97a58bf40 100644 --- a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js +++ b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js @@ -11,23 +11,24 @@ export default function Main($$anchor) { var div = $.first_child(fragment); var svg = $.sibling(div, 2); var custom_element = $.sibling(svg, 2); - var div_1 = $.sibling(custom_element, 2); - $.template_effect(() => $.set_attribute(div_1, 'foobar', y())); + $.template_effect(() => $.set_custom_element_data(custom_element, 'fooBar', x)); + var div_1 = $.sibling(custom_element, 2); var svg_1 = $.sibling(div_1, 2); - - $.template_effect(() => $.set_attribute(svg_1, 'viewBox', y())); - var custom_element_1 = $.sibling(svg_1, 2); $.template_effect(() => $.set_custom_element_data(custom_element_1, 'fooBar', y())); - $.template_effect(() => { - $.set_attribute(div, 'foobar', x); - $.set_attribute(svg, 'viewBox', x); - $.set_custom_element_data(custom_element, 'fooBar', x); - }); + $.template_effect( + ($0) => { + $.set_attribute(div, 'foobar', x); + $.set_attribute(svg, 'viewBox', x); + $.set_attribute(div_1, 'foobar', $0); + $.set_attribute(svg_1, 'viewBox', $0); + }, + [y] + ); $.append($$anchor, fragment); } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js index 2e0bd651da..d520d1ef24 100644 --- a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js @@ -16,11 +16,9 @@ export default function Text_nodes_deriveds($$anchor) { } var p = root(); - const expression = $.derived(() => text1() ?? ''); - const expression_1 = $.derived(() => text2() ?? ''); var text = $.child(p); - $.template_effect(() => $.set_text(text, `${$.get(expression)}${$.get(expression_1)}`)); $.reset(p); + $.template_effect(($0, $1) => $.set_text(text, `${$0 ?? ''}${$1 ?? ''}`), [text1, text2]); $.append($$anchor, p); } \ No newline at end of file From 5f6525f4169f097bdad1b2b276c2d94f55fdbc01 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Mon, 20 Jan 2025 02:20:09 -0800 Subject: [PATCH 0101/1429] fix: omit unnecessary nullish coallescing in template expressions (#15056) * omit unnecessary nullish coallescing in template expressions * add tests --- .changeset/cuddly-walls-pretend.md | 5 +++ .../client/visitors/shared/utils.js | 17 +++++++++- .../nullish-coallescence-omittance/_config.js | 3 ++ .../_expected/client/index.svelte.js | 34 +++++++++++++++++++ .../_expected/server/index.svelte.js | 8 +++++ .../index.svelte | 8 +++++ 6 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 .changeset/cuddly-walls-pretend.md create mode 100644 packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_config.js create mode 100644 packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/index.svelte diff --git a/.changeset/cuddly-walls-pretend.md b/.changeset/cuddly-walls-pretend.md new file mode 100644 index 0000000000..f51147a30c --- /dev/null +++ b/.changeset/cuddly-walls-pretend.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: omit unnecessary nullish coallescing in template expressions diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index 9dd2984329..c4f81274d9 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -119,7 +119,22 @@ export function build_template_chunk( // extra work in the template_effect (instead we do the work in set_text). return { value, has_state }; } else { - expressions.push(b.logical('??', value, b.literal(''))); + let expression = value; + // only add nullish coallescence if it hasn't been added already + if (value.type === 'LogicalExpression' && value.operator === '??') { + const { right } = value; + // `undefined` isn't a Literal (due to pre-ES5 shenanigans), so the only nullish literal is `null` + // however, you _can_ make a variable called `undefined` in a Svelte component, so we can't just treat it the same way + if (right.type !== 'Literal') { + expression = b.logical('??', value, b.literal('')); + } else if (right.value === null) { + // if they do something weird like `stuff ?? null`, replace `null` with empty string + value.right = b.literal(''); + } + } else { + expression = b.logical('??', value, b.literal('')); + } + expressions.push(expression); } quasi = b.quasi('', i + 1 === values.length); diff --git a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_config.js b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_config.js new file mode 100644 index 0000000000..f47bee71df --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({}); diff --git a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js new file mode 100644 index 0000000000..332c909ebe --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js @@ -0,0 +1,34 @@ +import 'svelte/internal/disclose-version'; +import * as $ from 'svelte/internal/client'; + +var on_click = (_, count) => $.update(count); +var root = $.template(`<h1></h1> <b></b> <button> </button> <h1></h1>`, 1); + +export default function Nullish_coallescence_omittance($$anchor) { + let name = 'world'; + let count = $.state(0); + var fragment = root(); + var h1 = $.first_child(fragment); + + h1.textContent = `Hello, ${name ?? ''}!`; + + var b = $.sibling(h1, 2); + + b.textContent = `${1 ?? 'stuff'}${2 ?? 'more stuff'}${3 ?? 'even more stuff'}`; + + var button = $.sibling(b, 2); + + button.__click = [on_click, count]; + + var text = $.child(button); + + $.reset(button); + + var h1_1 = $.sibling(button, 2); + + h1_1.textContent = `Hello, ${name ?? 'earth' ?? ''}`; + $.template_effect(() => $.set_text(text, `Count is ${$.get(count) ?? ''}`)); + $.append($$anchor, fragment); +} + +$.delegate(['click']); \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js new file mode 100644 index 0000000000..8181bfd98e --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js @@ -0,0 +1,8 @@ +import * as $ from 'svelte/internal/server'; + +export default function Nullish_coallescence_omittance($$payload) { + let name = 'world'; + let count = 0; + + $$payload.out += `<h1>Hello, ${$.escape(name)}!</h1> <b>${$.escape(1 ?? 'stuff')}${$.escape(2 ?? 'more stuff')}${$.escape(3 ?? 'even more stuff')}</b> <button>Count is ${$.escape(count)}</button> <h1>Hello, ${$.escape(name ?? 'earth' ?? null)}</h1>`; +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/index.svelte b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/index.svelte new file mode 100644 index 0000000000..a67c574fee --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/index.svelte @@ -0,0 +1,8 @@ +<script> + let name = 'world'; + let count = $state(0); +</script> +<h1>Hello, {null}{name}!</h1> +<b>{1 ?? 'stuff'}{2 ?? 'more stuff'}{3 ?? 'even more stuff'}</b> +<button onclick={()=>count++}>Count is {count}</button> +<h1>Hello, {name ?? 'earth' ?? null}</h1> \ No newline at end of file From de94159238080076150d28fbea5f399690821bf1 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Mon, 20 Jan 2025 02:28:57 -0800 Subject: [PATCH 0102/1429] docs: update README banner (#15063) * fix * update banner image this is an empty commit whose name will be shown in the commit history (if someone looks at the commit history they should be able to understand what commit does what). * chore: fix spacing * chore: fix spacing --------- Co-authored-by: Paolo Ricciuti <ricciutipaolo@gmail.com> --- README.md | 7 ++++++- assets/banner.png | Bin 0 -> 99696 bytes assets/banner_dark.png | Bin 0 -> 104966 bytes packages/svelte/README.md | 7 ++++++- 4 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 assets/banner.png create mode 100644 assets/banner_dark.png diff --git a/README.md b/README.md index cfb1328495..7d2f718da7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ -[![Cybernetically enhanced web apps: Svelte](https://sveltejs.github.io/assets/banner.png)](https://svelte.dev) +<a href="https://svelte.dev"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="assets/banner_dark.png"> + <img src="assets/banner.png" alt="Svelte - web development for the rest of us" /> + </picture> +</a> [![license](https://img.shields.io/npm/l/svelte.svg)](LICENSE.md) [![Chat](https://img.shields.io/discord/457912077277855764?label=chat&logo=discord)](https://svelte.dev/chat) diff --git a/assets/banner.png b/assets/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..3428b278bfa38cbdd2f6cc63f6b15f19a80c34d2 GIT binary patch literal 99696 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 M>O|dz2tf7mQ&)xQ1TMahFg2Q4j=PP4z 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 literal 0 HcmV?d00001 diff --git a/assets/banner_dark.png b/assets/banner_dark.png new file mode 100644 index 0000000000000000000000000000000000000000..1adba40d8e2ff08d3f95847717ee8c5fabfacb20 GIT binary patch literal 104966 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�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 aO74|E7yBs*v$)->*3&k;PSU*n<o^I>e+wc2 literal 0 HcmV?d00001 diff --git a/packages/svelte/README.md b/packages/svelte/README.md index 23e35fd4a7..c37617da30 100644 --- a/packages/svelte/README.md +++ b/packages/svelte/README.md @@ -1,4 +1,9 @@ -[![Cybernetically enhanced web apps: Svelte](https://sveltejs.github.io/assets/banner.png)](https://svelte.dev) +<a href="https://svelte.dev"> + <picture> + <source media="(prefers-color-scheme: dark)" srcset="../../assets/banner_dark.png"> + <img src="../../assets/banner.png" alt="Svelte - web development for the rest of us" /> + </picture> +</a> [![npm version](https://img.shields.io/npm/v/svelte.svg)](https://www.npmjs.com/package/svelte) [![license](https://img.shields.io/npm/l/svelte.svg)](LICENSE.md) [![Chat](https://img.shields.io/discord/457912077277855764?label=chat&logo=discord)](https://svelte.dev/chat) From 2ad519542dcba2d7802394068d412bb73d9cb803 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Tue, 21 Jan 2025 11:02:34 +0000 Subject: [PATCH 0103/1429] fix: ensure untrack correctly retains the active reaction (#15065) * fix: ensure untrack correctly retains the active reaction * fix: ensure untrack correctly retains the active reaction --- .changeset/fresh-cycles-sneeze.md | 5 +++ .../src/internal/client/reactivity/effects.js | 8 ++-- .../src/internal/client/reactivity/sources.js | 4 +- .../svelte/src/internal/client/runtime.js | 13 ++++-- packages/svelte/tests/signals/test.ts | 40 +++++++++++++++++++ 5 files changed, 61 insertions(+), 9 deletions(-) create mode 100644 .changeset/fresh-cycles-sneeze.md diff --git a/.changeset/fresh-cycles-sneeze.md b/.changeset/fresh-cycles-sneeze.md new file mode 100644 index 0000000000..e91168d7fd --- /dev/null +++ b/.changeset/fresh-cycles-sneeze.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure untrack correctly retains the active reaction diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index e854633150..1faf9a47a0 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -16,7 +16,8 @@ import { set_is_flushing_effect, set_signal_status, untrack, - skip_reaction + skip_reaction, + untracking } from '../runtime.js'; import { DIRTY, @@ -43,8 +44,7 @@ import * as e from '../errors.js'; import { DEV } from 'esm-env'; import { define_property } from '../../shared/utils.js'; import { get_next_sibling } from '../dom/operations.js'; -import { derived, derived_safe_equal, destroy_derived } from './deriveds.js'; -import { legacy_mode_flag } from '../../flags/index.js'; +import { derived, destroy_derived } from './deriveds.js'; /** * @param {'$effect' | '$effect.pre' | '$inspect'} rune @@ -166,7 +166,7 @@ function create_effect(type, fn, sync, push = true) { * @returns {boolean} */ export function effect_tracking() { - if (active_reaction === null) { + if (active_reaction === null || untracking) { return false; } diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 4500a7c5a8..d10008dae2 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -17,7 +17,8 @@ import { set_derived_sources, check_dirtiness, set_is_flushing_effect, - is_flushing_effect + is_flushing_effect, + untracking } from '../runtime.js'; import { equals, safe_equals } from './equality.js'; import { @@ -148,6 +149,7 @@ export function mutate(source, value) { export function set(source, value) { if ( active_reaction !== null && + !untracking && is_runes() && (active_reaction.f & (DERIVED | BLOCK_EFFECT)) !== 0 && // If the source was created locally within the current derived, then diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index eca5ee94f9..1d695e1fee 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -78,6 +78,8 @@ let dev_effect_stack = []; /** @type {null | Reaction} */ export let active_reaction = null; +export let untracking = false; + /** @param {null | Reaction} reaction */ export function set_active_reaction(reaction) { active_reaction = reaction; @@ -423,6 +425,7 @@ export function update_reaction(reaction) { var previous_skip_reaction = skip_reaction; var prev_derived_sources = derived_sources; var previous_component_context = component_context; + var previous_untracking = untracking; var flags = reaction.f; new_deps = /** @type {null | Value[]} */ (null); @@ -432,6 +435,7 @@ export function update_reaction(reaction) { skip_reaction = !is_flushing_effect && (flags & UNOWNED) !== 0; derived_sources = null; component_context = reaction.ctx; + untracking = false; read_version++; try { @@ -495,6 +499,7 @@ export function update_reaction(reaction) { skip_reaction = previous_skip_reaction; derived_sources = prev_derived_sources; component_context = previous_component_context; + untracking = previous_untracking; } } @@ -934,7 +939,7 @@ export function get(signal) { } // Register the dependency on the current reaction signal. - if (active_reaction !== null) { + if (active_reaction !== null && !untracking) { if (derived_sources !== null && derived_sources.includes(signal)) { e.state_unsafe_local_read(); } @@ -1085,12 +1090,12 @@ export function invalidate_inner_signals(fn) { * @returns {T} */ export function untrack(fn) { - const previous_reaction = active_reaction; + var previous_untracking = untracking; try { - active_reaction = null; + untracking = true; return fn(); } finally { - active_reaction = previous_reaction; + untracking = previous_untracking; } } diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index a9d29920cf..e147fd1d0d 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -803,6 +803,46 @@ describe('signals', () => { }; }); + test('deriveds containing effects work correctly when used with untrack', () => { + return () => { + let a = render_effect(() => {}); + let b = state(0); + let c; + let effects = []; + + const destroy = effect_root(() => { + a = render_effect(() => { + c = derived(() => { + $.untrack(() => { + effects.push( + effect(() => { + $.get(b); + }) + ); + }); + $.get(b); + }); + $.get(c); + }); + }); + + assert.deepEqual(c!.children?.length, 1); + assert.deepEqual(a.first, a.last); + + set(b, 1); + + flushSync(); + + assert.deepEqual(c!.children?.length, 1); + assert.deepEqual(a.first, a.last); + + destroy(); + + assert.deepEqual(a.deriveds, null); + assert.deepEqual(a.first, null); + }; + }); + test('bigint states update correctly', () => { return () => { const count = state(0n); From 5ad6a0cdc37413e2c31994e7402d24ec4853fad7 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Tue, 21 Jan 2025 12:04:48 +0100 Subject: [PATCH 0104/1429] fix: initialize `files` bind on hydration (#15059) --- .changeset/happy-ants-grin.md | 5 +++++ .../src/internal/client/dom/elements/bindings/input.js | 9 +++++++++ 2 files changed, 14 insertions(+) create mode 100644 .changeset/happy-ants-grin.md diff --git a/.changeset/happy-ants-grin.md b/.changeset/happy-ants-grin.md new file mode 100644 index 0000000000..c80b2eb40c --- /dev/null +++ b/.changeset/happy-ants-grin.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: initialize `files` bind on hydration diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/input.js b/packages/svelte/src/internal/client/dom/elements/bindings/input.js index ec123d3968..3ea1a24d7e 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/input.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/input.js @@ -259,6 +259,15 @@ export function bind_files(input, get, set = get) { set(input.files); }); + if ( + // If we are hydrating and the value has since changed, + // then use the updated value from the input instead. + hydrating && + input.files + ) { + set(input.files); + } + render_effect(() => { input.files = get(); }); From c75f1f5f2f4883fc45ff9c66b294a7c59f417945 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Jan 2025 11:06:50 +0000 Subject: [PATCH 0105/1429] Version Packages (#15052) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/cuddly-walls-pretend.md | 5 ----- .changeset/cyan-games-cheat.md | 5 ----- .changeset/fresh-cycles-sneeze.md | 5 ----- .changeset/happy-ants-grin.md | 5 ----- packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 7 files changed, 14 insertions(+), 22 deletions(-) delete mode 100644 .changeset/cuddly-walls-pretend.md delete mode 100644 .changeset/cyan-games-cheat.md delete mode 100644 .changeset/fresh-cycles-sneeze.md delete mode 100644 .changeset/happy-ants-grin.md diff --git a/.changeset/cuddly-walls-pretend.md b/.changeset/cuddly-walls-pretend.md deleted file mode 100644 index f51147a30c..0000000000 --- a/.changeset/cuddly-walls-pretend.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: omit unnecessary nullish coallescing in template expressions diff --git a/.changeset/cyan-games-cheat.md b/.changeset/cyan-games-cheat.md deleted file mode 100644 index d90901783b..0000000000 --- a/.changeset/cyan-games-cheat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: more efficient template effect grouping diff --git a/.changeset/fresh-cycles-sneeze.md b/.changeset/fresh-cycles-sneeze.md deleted file mode 100644 index e91168d7fd..0000000000 --- a/.changeset/fresh-cycles-sneeze.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure untrack correctly retains the active reaction diff --git a/.changeset/happy-ants-grin.md b/.changeset/happy-ants-grin.md deleted file mode 100644 index c80b2eb40c..0000000000 --- a/.changeset/happy-ants-grin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: initialize `files` bind on hydration diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 59ac9f2864..d16bdbc327 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.19.1 + +### Patch Changes + +- fix: omit unnecessary nullish coallescing in template expressions ([#15056](https://github.com/sveltejs/svelte/pull/15056)) + +- fix: more efficient template effect grouping ([#15050](https://github.com/sveltejs/svelte/pull/15050)) + +- fix: ensure untrack correctly retains the active reaction ([#15065](https://github.com/sveltejs/svelte/pull/15065)) + +- fix: initialize `files` bind on hydration ([#15059](https://github.com/sveltejs/svelte/pull/15059)) + ## 5.19.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 95e7a7319d..b725e52fed 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.19.0", + "version": "5.19.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 7347e7546d..b0ea99b6f4 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.19.0'; +export const VERSION = '5.19.1'; export const PUBLIC_VERSION = '5'; From 09510c8c5c0888578446781e54f2229428e21bfc Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Tue, 21 Jan 2025 20:01:14 +0000 Subject: [PATCH 0106/1429] fix: address regression with untrack (#15079) * fix: address regression with untrack * add test --- .changeset/ninety-chefs-know.md | 5 ++ .../src/internal/client/reactivity/sources.js | 2 +- .../untracked-derived-local/_config.js | 5 ++ .../untracked-derived-local/main.svelte | 47 +++++++++++++++++++ 4 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 .changeset/ninety-chefs-know.md create mode 100644 packages/svelte/tests/runtime-runes/samples/untracked-derived-local/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/untracked-derived-local/main.svelte diff --git a/.changeset/ninety-chefs-know.md b/.changeset/ninety-chefs-know.md new file mode 100644 index 0000000000..b5eac6a07a --- /dev/null +++ b/.changeset/ninety-chefs-know.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: address regression with untrack diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index d10008dae2..c2448c9ee5 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -116,7 +116,7 @@ export function mutable_state(v, immutable = false) { */ /*#__NO_SIDE_EFFECTS__*/ function push_derived_source(source) { - if (active_reaction !== null && (active_reaction.f & DERIVED) !== 0) { + if (active_reaction !== null && !untracking && (active_reaction.f & DERIVED) !== 0) { if (derived_sources === null) { set_derived_sources([source]); } else { diff --git a/packages/svelte/tests/runtime-runes/samples/untracked-derived-local/_config.js b/packages/svelte/tests/runtime-runes/samples/untracked-derived-local/_config.js new file mode 100644 index 0000000000..1fe92ed2d7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/untracked-derived-local/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: `3` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/untracked-derived-local/main.svelte b/packages/svelte/tests/runtime-runes/samples/untracked-derived-local/main.svelte new file mode 100644 index 0000000000..0873eb741d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/untracked-derived-local/main.svelte @@ -0,0 +1,47 @@ +<script module> + import { untrack } from 'svelte' + import { SvelteMap } from 'svelte/reactivity' + + class Foo { + id + updateTime = $state(Date.now()) + + constructor(id) { + this.id = id + } + } + + class Store { + cache = new SvelteMap() + ids = $state([1, 2, 3]) + + getOrDefault(id) { + let ret = this.cache.get(id) + + if (ret) { + return ret + } + + ret = untrack(() => { + ret = new Foo(id) + this.cache.set(id, ret) + return ret + }) + this.cache.get(id) + + return ret + } + + get values() { + return this.ids.map(id => this.getOrDefault(id)).sort((a, b) => b.updateTime - a.updateTime) + } + } + + const store = new Store() +</script> + +<script> + const test = $derived.by(() => store.values.length) +</script> + +{test} From ee024ffd0e57dd5eca90d0c7aca5dc0e83d55449 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 13:30:18 +0000 Subject: [PATCH 0107/1429] Version Packages (#15080) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/ninety-chefs-know.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/ninety-chefs-know.md diff --git a/.changeset/ninety-chefs-know.md b/.changeset/ninety-chefs-know.md deleted file mode 100644 index b5eac6a07a..0000000000 --- a/.changeset/ninety-chefs-know.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: address regression with untrack diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index d16bdbc327..e15c0e0a09 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.19.2 + +### Patch Changes + +- fix: address regression with untrack ([#15079](https://github.com/sveltejs/svelte/pull/15079)) + ## 5.19.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index b725e52fed..48287cf901 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.19.1", + "version": "5.19.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b0ea99b6f4..7a3f016aaa 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.19.1'; +export const VERSION = '5.19.2'; export const PUBLIC_VERSION = '5'; From 7da86ef80cd3503a14e26393cbbb2e6a2a933ed9 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Wed, 22 Jan 2025 14:34:57 +0100 Subject: [PATCH 0108/1429] fix: don't throw for `undefined` non delegated event handlers (#15087) * fix: don't throw for `undefined` non delegated event handlers * chore: update typings --- .changeset/clever-cherries-hear.md | 5 +++++ .../src/internal/client/dom/elements/events.js | 12 ++++++------ .../samples/undefined-event-handler/_config.js | 9 +++++++++ .../samples/undefined-event-handler/main.svelte | 1 + 4 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 .changeset/clever-cherries-hear.md create mode 100644 packages/svelte/tests/runtime-runes/samples/undefined-event-handler/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/undefined-event-handler/main.svelte diff --git a/.changeset/clever-cherries-hear.md b/.changeset/clever-cherries-hear.md new file mode 100644 index 0000000000..df37943b46 --- /dev/null +++ b/.changeset/clever-cherries-hear.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't throw for `undefined` non delegated event handlers diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js index f2038f96ad..363b8e1ed5 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -49,10 +49,10 @@ export function replay_events(dom) { /** * @param {string} event_name * @param {EventTarget} dom - * @param {EventListener} handler - * @param {AddEventListenerOptions} options + * @param {EventListener} [handler] + * @param {AddEventListenerOptions} [options] */ -export function create_event(event_name, dom, handler, options) { +export function create_event(event_name, dom, handler, options = {}) { /** * @this {EventTarget} */ @@ -63,7 +63,7 @@ export function create_event(event_name, dom, handler, options) { } if (!event.cancelBubble) { return without_reactive_context(() => { - return handler.call(this, event); + return handler?.call(this, event); }); } } @@ -108,8 +108,8 @@ export function on(element, type, handler, options = {}) { /** * @param {string} event_name * @param {Element} dom - * @param {EventListener} handler - * @param {boolean} capture + * @param {EventListener} [handler] + * @param {boolean} [capture] * @param {boolean} [passive] * @returns {void} */ diff --git a/packages/svelte/tests/runtime-runes/samples/undefined-event-handler/_config.js b/packages/svelte/tests/runtime-runes/samples/undefined-event-handler/_config.js new file mode 100644 index 0000000000..012fedb160 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/undefined-event-handler/_config.js @@ -0,0 +1,9 @@ +import { ok, test } from '../../test'; + +export default test({ + async test({ target }) { + const button = target.querySelector('button'); + ok(button); + button.dispatchEvent(new window.MouseEvent('mouseenter')); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/undefined-event-handler/main.svelte b/packages/svelte/tests/runtime-runes/samples/undefined-event-handler/main.svelte new file mode 100644 index 0000000000..ea4b4443e9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/undefined-event-handler/main.svelte @@ -0,0 +1 @@ +<button onmouseenter={undefined}></button> From eee808fb79cfb182df7285b7107d5f7d2bee44d1 Mon Sep 17 00:00:00 2001 From: "Trevor N. Suarez" <Rican7@gmail.com> Date: Wed, 22 Jan 2025 06:35:26 -0700 Subject: [PATCH 0109/1429] fix: correctly handle `novalidate` attribute casing (#15083) This PR fixes #15082 by correctly handling the HTML element novalidate attribute casing through noValidate DOM property mapping. --- .changeset/pink-wolves-search.md | 5 +++++ packages/svelte/src/utils.js | 3 ++- .../samples/form-novalidate-casing/_config.js | 8 ++++++++ .../samples/form-novalidate-casing/main.svelte | 6 ++++++ 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 .changeset/pink-wolves-search.md create mode 100644 packages/svelte/tests/runtime-runes/samples/form-novalidate-casing/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/form-novalidate-casing/main.svelte diff --git a/.changeset/pink-wolves-search.md b/.changeset/pink-wolves-search.md new file mode 100644 index 0000000000..cd734062c6 --- /dev/null +++ b/.changeset/pink-wolves-search.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly handle `novalidate` attribute casing diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index 76486d32ac..b16c0551f1 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -196,7 +196,8 @@ const ATTRIBUTE_ALIASES = { readonly: 'readOnly', defaultvalue: 'defaultValue', defaultchecked: 'defaultChecked', - srcobject: 'srcObject' + srcobject: 'srcObject', + novalidate: 'noValidate' }; /** diff --git a/packages/svelte/tests/runtime-runes/samples/form-novalidate-casing/_config.js b/packages/svelte/tests/runtime-runes/samples/form-novalidate-casing/_config.js new file mode 100644 index 0000000000..4fdf3632d6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/form-novalidate-casing/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + html: ` + <form novalidate></form> + <form novalidate></form> +` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/form-novalidate-casing/main.svelte b/packages/svelte/tests/runtime-runes/samples/form-novalidate-casing/main.svelte new file mode 100644 index 0000000000..1e8115ff62 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/form-novalidate-casing/main.svelte @@ -0,0 +1,6 @@ +<script> + let noValidate = $state(true); +</script> + +<form novalidate={true}></form> +<form {noValidate}></form> From 8bba70b8e30a824218f909a36f9dd255fae9710f Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 22 Jan 2025 10:37:43 -0500 Subject: [PATCH 0110/1429] fix: avoid double deriveds in component props (#15089) * fix: prevent double deriveds in component props * unused * reuse mechanism * move code to where it is used * changeset --- .changeset/tasty-pigs-greet.md | 5 ++ .../phases/3-transform/client/utils.js | 35 -------------- .../3-transform/client/visitors/AwaitBlock.js | 41 +++++++++++++++- .../client/visitors/RegularElement.js | 8 ++-- .../client/visitors/SlotElement.js | 6 ++- .../client/visitors/shared/component.js | 47 +++++++++---------- .../client/visitors/shared/element.js | 23 ++++----- .../client/visitors/shared/utils.js | 15 +++--- 8 files changed, 93 insertions(+), 87 deletions(-) create mode 100644 .changeset/tasty-pigs-greet.md diff --git a/.changeset/tasty-pigs-greet.md b/.changeset/tasty-pigs-greet.md new file mode 100644 index 0000000000..63decceed8 --- /dev/null +++ b/.changeset/tasty-pigs-greet.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: avoid double deriveds in component props diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index c59a5544df..421118cf68 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -269,41 +269,6 @@ export function should_proxy(node, scope) { return true; } -/** - * @param {Pattern} node - * @param {import('zimmerframe').Context<AST.SvelteNode, ComponentClientTransformState>} context - * @returns {{ id: Pattern, declarations: null | Statement[] }} - */ -export function create_derived_block_argument(node, context) { - if (node.type === 'Identifier') { - context.state.transform[node.name] = { read: get_value }; - return { id: node, declarations: null }; - } - - const pattern = /** @type {Pattern} */ (context.visit(node)); - const identifiers = extract_identifiers(node); - - const id = b.id('$$source'); - const value = b.id('$$value'); - - const block = b.block([ - b.var(pattern, b.call('$.get', id)), - b.return(b.object(identifiers.map((identifier) => b.prop('init', identifier, identifier)))) - ]); - - const declarations = [b.var(value, create_derived(context.state, b.thunk(block)))]; - - for (const id of identifiers) { - context.state.transform[id.name] = { read: get_value }; - - declarations.push( - b.var(id, create_derived(context.state, b.thunk(b.member(b.call('$.get', value), id)))) - ); - } - - return { id, declarations }; -} - /** * Svelte legacy mode should use safe equals in most places, runes mode shouldn't * @param {ComponentClientTransformState} state diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js index 146f75d405..e0aef2d316 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js @@ -1,8 +1,10 @@ /** @import { BlockStatement, Expression, Pattern, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ -/** @import { ComponentContext } from '../types' */ +/** @import { ComponentClientTransformState, ComponentContext } from '../types' */ +import { extract_identifiers } from '../../../../utils/ast.js'; import * as b from '../../../../utils/builders.js'; -import { create_derived_block_argument } from '../utils.js'; +import { create_derived } from '../utils.js'; +import { get_value } from './shared/declarations.js'; /** * @param {AST.AwaitBlock} node @@ -65,3 +67,38 @@ export function AwaitBlock(node, context) { ) ); } + +/** + * @param {Pattern} node + * @param {import('zimmerframe').Context<AST.SvelteNode, ComponentClientTransformState>} context + * @returns {{ id: Pattern, declarations: null | Statement[] }} + */ +function create_derived_block_argument(node, context) { + if (node.type === 'Identifier') { + context.state.transform[node.name] = { read: get_value }; + return { id: node, declarations: null }; + } + + const pattern = /** @type {Pattern} */ (context.visit(node)); + const identifiers = extract_identifiers(node); + + const id = b.id('$$source'); + const value = b.id('$$value'); + + const block = b.block([ + b.var(pattern, b.call('$.get', id)), + b.return(b.object(identifiers.map((identifier) => b.prop('init', identifier, identifier)))) + ]); + + const declarations = [b.var(value, create_derived(context.state, b.thunk(block)))]; + + for (const id of identifiers) { + context.state.transform[id.name] = { read: get_value }; + + declarations.push( + b.var(id, create_derived(context.state, b.thunk(b.member(b.call('$.get', value), id)))) + ); + } + + return { id, declarations }; +} diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 21a78de032..d0a148f216 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -537,8 +537,8 @@ function build_element_attribute_update_assignment( const is_svg = context.state.metadata.namespace === 'svg' || element.name === 'svg'; const is_mathml = context.state.metadata.namespace === 'mathml'; - let { value, has_state } = build_attribute_value(attribute.value, context, (value) => - get_expression_id(state, value) + let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => + metadata.has_call ? get_expression_id(state, value) : value ); if (name === 'autofocus') { @@ -665,8 +665,8 @@ function build_custom_element_attribute_update_assignment(node_id, attribute, co */ function build_element_special_value_attribute(element, node_id, attribute, context) { const state = context.state; - const { value, has_state } = build_attribute_value(attribute.value, context, (value) => - get_expression_id(state, value) + const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => + metadata.has_call ? get_expression_id(state, value) : value ); const inner_assignment = b.assignment( diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js index f1b08acbc6..fdd705e32e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js @@ -30,8 +30,10 @@ export function SlotElement(node, context) { if (attribute.type === 'SpreadAttribute') { spreads.push(b.thunk(/** @type {Expression} */ (context.visit(attribute)))); } else if (attribute.type === 'Attribute') { - const { value, has_state } = build_attribute_value(attribute.value, context, (value) => - memoize_expression(context.state, value) + const { value, has_state } = build_attribute_value( + attribute.value, + context, + (value, metadata) => (metadata.has_call ? memoize_expression(context.state, value) : value) ); if (attribute.name === 'name') { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js index 9ac0bac120..15e4f68e9e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js @@ -4,7 +4,6 @@ import { dev, is_ignored } from '../../../../../state.js'; import { get_attribute_chunks, object } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; -import { create_derived } from '../../utils.js'; import { build_bind_this, memoize_expression, validate_binding } from '../shared/utils.js'; import { build_attribute_value } from '../shared/element.js'; import { build_event_handler } from './events.js'; @@ -134,9 +133,9 @@ export function build_component(node, component_name, context, anchor = context. custom_css_props.push( b.init( attribute.name, - build_attribute_value(attribute.value, context, (value) => + build_attribute_value(attribute.value, context, (value, metadata) => // TODO put the derived in the local block - memoize_expression(context.state, value) + metadata.has_call ? memoize_expression(context.state, value) : value ).value ) ); @@ -151,31 +150,29 @@ export function build_component(node, component_name, context, anchor = context. has_children_prop = true; } - const { value, has_state } = build_attribute_value(attribute.value, context, (value) => - memoize_expression(context.state, value) - ); - - if (has_state) { - let arg = value; - - // When we have a non-simple computation, anything other than an Identifier or Member expression, - // then there's a good chance it needs to be memoized to avoid over-firing when read within the - // child component. - const should_wrap_in_derived = get_attribute_chunks(attribute.value).some((n) => { - return ( - n.type === 'ExpressionTag' && - n.expression.type !== 'Identifier' && - n.expression.type !== 'MemberExpression' - ); - }); + const { value, has_state } = build_attribute_value( + attribute.value, + context, + (value, metadata) => { + if (!metadata.has_state) return value; + + // When we have a non-simple computation, anything other than an Identifier or Member expression, + // then there's a good chance it needs to be memoized to avoid over-firing when read within the + // child component (e.g. `active={i === index}`) + const should_wrap_in_derived = get_attribute_chunks(attribute.value).some((n) => { + return ( + n.type === 'ExpressionTag' && + n.expression.type !== 'Identifier' && + n.expression.type !== 'MemberExpression' + ); + }); - if (should_wrap_in_derived) { - const id = b.id(context.state.scope.generate(attribute.name)); - context.state.init.push(b.var(id, create_derived(context.state, b.thunk(value)))); - arg = b.call('$.get', id); + return should_wrap_in_derived ? memoize_expression(context.state, value) : value; } + ); - push_prop(b.get(attribute.name, [b.return(arg)])); + if (has_state) { + push_prop(b.get(attribute.name, [b.return(value)])); } else { push_prop(b.init(attribute.name, value)); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index 8fb6b8bdde..abffad0ff7 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -1,11 +1,11 @@ /** @import { Expression, Identifier, ObjectExpression } from 'estree' */ -/** @import { AST } from '#compiler' */ +/** @import { AST, ExpressionMetadata } from '#compiler' */ /** @import { ComponentClientTransformState, ComponentContext } from '../../types' */ import { normalize_attribute } from '../../../../../../utils.js'; import { is_ignored } from '../../../../../state.js'; import { is_event_attribute } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; -import { build_getter, create_derived } from '../../utils.js'; +import { build_getter } from '../../utils.js'; import { build_template_chunk, get_expression_id } from './utils.js'; /** @@ -35,8 +35,10 @@ export function build_set_attributes( for (const attribute of attributes) { if (attribute.type === 'Attribute') { - const { value, has_state } = build_attribute_value(attribute.value, context, (value) => - get_expression_id(context.state, value) + const { value, has_state } = build_attribute_value( + attribute.value, + context, + (value, metadata) => (metadata.has_call ? get_expression_id(context.state, value) : value) ); if ( @@ -59,10 +61,9 @@ export function build_set_attributes( let value = /** @type {Expression} */ (context.visit(attribute)); if (attribute.metadata.expression.has_call) { - const id = b.id(state.scope.generate('spread_with_call')); - state.init.push(b.const(id, create_derived(state, b.thunk(value)))); - value = b.call('$.get', id); + value = get_expression_id(context.state, value); } + values.push(b.spread(value)); } } @@ -111,8 +112,8 @@ export function build_style_directives( let value = directive.value === true ? build_getter({ name: directive.name, type: 'Identifier' }, context.state) - : build_attribute_value(directive.value, context, (value) => - get_expression_id(context.state, value) + : build_attribute_value(directive.value, context, (value, metadata) => + metadata.has_call ? get_expression_id(context.state, value) : value ).value; const update = b.stmt( @@ -169,7 +170,7 @@ export function build_class_directives( /** * @param {AST.Attribute['value']} value * @param {ComponentContext} context - * @param {(value: Expression) => Expression} memoize + * @param {(value: Expression, metadata: ExpressionMetadata) => Expression} memoize * @returns {{ value: Expression, has_state: boolean }} */ export function build_attribute_value(value, context, memoize = (value) => value) { @@ -187,7 +188,7 @@ export function build_attribute_value(value, context, memoize = (value) => value let expression = /** @type {Expression} */ (context.visit(chunk.expression)); return { - value: chunk.metadata.expression.has_call ? memoize(expression) : expression, + value: memoize(expression, chunk.metadata.expression), has_state: chunk.metadata.expression.has_state }; } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index c4f81274d9..4c28616665 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -1,5 +1,5 @@ /** @import { Expression, ExpressionStatement, Identifier, MemberExpression, SequenceExpression, Statement, Super } from 'estree' */ -/** @import { AST } from '#compiler' */ +/** @import { AST, ExpressionMetadata } from '#compiler' */ /** @import { ComponentClientTransformState } from '../../types' */ import { walk } from 'zimmerframe'; import { object } from '../../../../../utils/ast.js'; @@ -79,14 +79,14 @@ function compare_expressions(a, b) { * @param {Array<AST.Text | AST.ExpressionTag>} values * @param {(node: AST.SvelteNode, state: any) => any} visit * @param {ComponentClientTransformState} state - * @param {(value: Expression) => Expression} memoize + * @param {(value: Expression, metadata: ExpressionMetadata) => Expression} memoize * @returns {{ value: Expression, has_state: boolean }} */ export function build_template_chunk( values, visit, state, - memoize = (value) => get_expression_id(state, value) + memoize = (value, metadata) => (metadata.has_call ? get_expression_id(state, value) : value) ) { /** @type {Expression[]} */ const expressions = []; @@ -106,14 +106,13 @@ export function build_template_chunk( quasi.value.cooked += node.expression.value + ''; } } else { - let value = /** @type {Expression} */ (visit(node.expression, state)); + let value = memoize( + /** @type {Expression} */ (visit(node.expression, state)), + node.metadata.expression + ); has_state ||= node.metadata.expression.has_state; - if (node.metadata.expression.has_call) { - value = memoize(value); - } - if (values.length === 1) { // If we have a single expression, then pass that in directly to possibly avoid doing // extra work in the template_effect (instead we do the work in set_text). From c2e805f05cb652ed3e7fa29f4a7c3f2912a79678 Mon Sep 17 00:00:00 2001 From: Theodore Brown <theodorejb@outlook.com> Date: Wed, 22 Jan 2025 13:54:12 -0600 Subject: [PATCH 0111/1429] fix: consistently set value to blank string when value attribute is undefined (#15057) Fixes #4467 --- .changeset/dirty-sloths-move.md | 5 ++ .../client/dom/elements/attributes.js | 4 +- .../value-attribute-null-undefined/_config.js | 49 +++++++++++++++++++ .../main.svelte | 9 ++++ 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 .changeset/dirty-sloths-move.md create mode 100644 packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/main.svelte diff --git a/.changeset/dirty-sloths-move.md b/.changeset/dirty-sloths-move.md new file mode 100644 index 0000000000..07436a0b2e --- /dev/null +++ b/.changeset/dirty-sloths-move.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: consistently set value to blank string when value attribute is undefined diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index a2fffe8696..eab27e6c02 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -68,14 +68,14 @@ export function set_value(element, value) { // treat null and undefined the same for the initial value value ?? undefined) || // @ts-expect-error - // `progress` elements always need their value set when its `0` + // `progress` elements always need their value set when it's `0` (element.value === value && (value !== 0 || element.nodeName !== 'PROGRESS')) ) { return; } // @ts-expect-error - element.value = value; + element.value = value ?? ''; } /** diff --git a/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/_config.js b/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/_config.js new file mode 100644 index 0000000000..08f0c5aec7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/_config.js @@ -0,0 +1,49 @@ +import { test, ok } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + mode: ['client'], + + async test({ assert, target }) { + /** + * @type {HTMLInputElement | null} + */ + const input = target.querySelector('input[type=text]'); + /** + * @type {HTMLButtonElement | null} + */ + const setString = target.querySelector('#setString'); + /** + * @type {HTMLButtonElement | null} + */ + const setNull = target.querySelector('#setNull'); + /** + * @type {HTMLButtonElement | null} + */ + const setUndefined = target.querySelector('#setUndefined'); + + ok(input); + ok(setString); + ok(setNull); + ok(setUndefined); + + // value should always be blank string when value attribute is set to null or undefined + + assert.equal(input.value, ''); + setString.click(); + flushSync(); + assert.equal(input.value, 'foo'); + + setNull.click(); + flushSync(); + assert.equal(input.value, ''); + + setString.click(); + flushSync(); + assert.equal(input.value, 'foo'); + + setUndefined.click(); + flushSync(); + assert.equal(input.value, ''); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/main.svelte b/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/main.svelte new file mode 100644 index 0000000000..9f944923c2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/value-attribute-null-undefined/main.svelte @@ -0,0 +1,9 @@ +<script> + let value = $state(); +</script> + +<input type="text" {value} /> + +<button id="setString" onclick={() => {value = "foo";}}></button> +<button id="setNull" onclick={() => {value = null;}}></button> +<button id="setUndefined" onclick={() => {value = undefined;}}></button> From 7740d4576b95f2e4e4e95a9df2d5504cb12a9461 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 22 Jan 2025 15:48:41 -0500 Subject: [PATCH 0112/1429] fix: optimise || expressions in template (#15092) --- .changeset/large-islands-cover.md | 5 ++++ .../client/visitors/shared/utils.js | 26 +++++++++---------- 2 files changed, 18 insertions(+), 13 deletions(-) create mode 100644 .changeset/large-islands-cover.md diff --git a/.changeset/large-islands-cover.md b/.changeset/large-islands-cover.md new file mode 100644 index 0000000000..969d2fc254 --- /dev/null +++ b/.changeset/large-islands-cover.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: optimise || expressions in template diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index 4c28616665..00634f229e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -118,22 +118,22 @@ export function build_template_chunk( // extra work in the template_effect (instead we do the work in set_text). return { value, has_state }; } else { - let expression = value; - // only add nullish coallescence if it hasn't been added already - if (value.type === 'LogicalExpression' && value.operator === '??') { - const { right } = value; - // `undefined` isn't a Literal (due to pre-ES5 shenanigans), so the only nullish literal is `null` - // however, you _can_ make a variable called `undefined` in a Svelte component, so we can't just treat it the same way - if (right.type !== 'Literal') { - expression = b.logical('??', value, b.literal('')); - } else if (right.value === null) { - // if they do something weird like `stuff ?? null`, replace `null` with empty string - value.right = b.literal(''); + // add `?? ''` where necessary (TODO optimise more cases) + if ( + value.type === 'LogicalExpression' && + value.right.type === 'Literal' && + (value.operator === '??' || value.operator === '||') + ) { + // `foo ?? null` -=> `foo ?? ''` + // otherwise leave the expression untouched + if (value.right.value === null) { + value = { ...value, right: b.literal('') }; } } else { - expression = b.logical('??', value, b.literal('')); + value = b.logical('??', value, b.literal('')); } - expressions.push(expression); + + expressions.push(value); } quasi = b.quasi('', i + 1 === values.length); From d17f5c748d4b842e63d0017461ccde49b00b78e0 Mon Sep 17 00:00:00 2001 From: Edoardo Cavazza <edoardo.cavazza@chialab.it> Date: Wed, 22 Jan 2025 21:51:26 +0100 Subject: [PATCH 0113/1429] fix: add check for is attribute (#15086) Fixes #15085 Add a check for is attribute in is_custom_element_node function. --- .changeset/two-doors-exercise.md | 5 +++++ .../client/visitors/RegularElement.js | 2 +- .../client/visitors/shared/fragment.js | 3 ++- packages/svelte/src/compiler/phases/nodes.js | 8 ++++++-- .../custom-element-attributes/_config.js | 19 +++++++++++++++++++ .../custom-element-attributes/main.svelte | 2 ++ 6 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 .changeset/two-doors-exercise.md create mode 100644 packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte diff --git a/.changeset/two-doors-exercise.md b/.changeset/two-doors-exercise.md new file mode 100644 index 0000000000..b41e582ae7 --- /dev/null +++ b/.changeset/two-doors-exercise.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: add check for `is` attribute to correctly detect custom elements diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index d0a148f216..c7e218d521 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -227,7 +227,7 @@ export function RegularElement(node, context) { node_id, attributes_id, (node.metadata.svg || node.metadata.mathml || is_custom_element_node(node)) && b.true, - node.name.includes('-') && b.true, + is_custom_element_node(node) && b.true, context.state ); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js index 0b4ac87342..5bc3041ca4 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js @@ -4,6 +4,7 @@ import { cannot_be_set_statically } from '../../../../../../utils.js'; import { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; +import { is_custom_element_node } from '../../../../nodes.js'; import { build_template_chunk } from './utils.js'; /** @@ -128,7 +129,7 @@ export function process_children(nodes, initial, is_element, { visit, state }) { function is_static_element(node, state) { if (node.type !== 'RegularElement') return false; if (node.fragment.metadata.dynamic) return false; - if (node.name.includes('-')) return false; // we're setting all attributes on custom elements through properties + if (is_custom_element_node(node)) return false; // we're setting all attributes on custom elements through properties for (const attribute of node.attributes) { if (attribute.type !== 'Attribute') { diff --git a/packages/svelte/src/compiler/phases/nodes.js b/packages/svelte/src/compiler/phases/nodes.js index 5ca4ce3380..003c3a2c49 100644 --- a/packages/svelte/src/compiler/phases/nodes.js +++ b/packages/svelte/src/compiler/phases/nodes.js @@ -23,10 +23,14 @@ export function is_element_node(node) { /** * @param {AST.RegularElement | AST.SvelteElement} node - * @returns {node is AST.RegularElement} + * @returns {boolean} */ export function is_custom_element_node(node) { - return node.type === 'RegularElement' && node.name.includes('-'); + return ( + node.type === 'RegularElement' && + (node.name.includes('-') || + node.attributes.some((attr) => attr.type === 'Attribute' && attr.name === 'is')) + ); } /** diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js new file mode 100644 index 0000000000..118a51157e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js @@ -0,0 +1,19 @@ +import { test } from '../../test'; + +export default test({ + mode: ['client', 'server'], + async test({ assert, target }) { + const my_element = /** @type HTMLElement & { object: { test: true }; } */ ( + target.querySelector('my-element') + ); + const my_link = /** @type HTMLAnchorElement & { object: { test: true }; } */ ( + target.querySelector('a') + ); + assert.equal(my_element.getAttribute('string'), 'test'); + assert.equal(my_element.hasAttribute('object'), false); + assert.deepEqual(my_element.object, { test: true }); + assert.equal(my_link.getAttribute('string'), 'test'); + assert.equal(my_link.hasAttribute('object'), false); + assert.deepEqual(my_link.object, { test: true }); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte new file mode 100644 index 0000000000..ff94a9484c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte @@ -0,0 +1,2 @@ +<my-element string="test" object={{ test: true }}></my-element> +<a is="my-link" string="test" object={{ test: true }}></a> From d47f4f59084a26d8b47d864e961ddf76336a0ed8 Mon Sep 17 00:00:00 2001 From: "Trevor N. Suarez" <Rican7@gmail.com> Date: Thu, 23 Jan 2025 00:55:12 -0700 Subject: [PATCH 0114/1429] fix: expand boolean attribute support (#15095) * Adding test for expected boolean attribute support * Improving support for more boolean attributes: Added: - `defer` - `disablepictureinpicture` - `disableremoteplayback` Improved: - `allowfullscreen` - `novalidate` * Skipping JSDOM version of boolean attribute test JSDOM lacks support for some attributes, so we'll skip it for now. See: - `async`: https://github.com/jsdom/jsdom/issues/1564 - `nomodule`: https://github.com/jsdom/jsdom/issues/2475 - `autofocus`: https://github.com/jsdom/jsdom/issues/3041 - `inert`: https://github.com/jsdom/jsdom/issues/3605 - etc...: https://github.com/jestjs/jest/issues/139#issuecomment-592673550 * Adding changeset --- .changeset/strange-hairs-trade.md | 5 ++ packages/svelte/src/utils.js | 16 ++++- .../_config.js | 60 +++++++++++++++++++ .../main.svelte | 22 +++++++ 4 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 .changeset/strange-hairs-trade.md create mode 100644 packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/main.svelte diff --git a/.changeset/strange-hairs-trade.md b/.changeset/strange-hairs-trade.md new file mode 100644 index 0000000000..192e5ef3d1 --- /dev/null +++ b/.changeset/strange-hairs-trade.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: expand boolean attribute support diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index b16c0551f1..e8e1bc224c 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -170,7 +170,10 @@ const DOM_BOOLEAN_ATTRIBUTES = [ 'reversed', 'seamless', 'selected', - 'webkitdirectory' + 'webkitdirectory', + 'defer', + 'disablepictureinpicture', + 'disableremoteplayback' ]; /** @@ -197,7 +200,10 @@ const ATTRIBUTE_ALIASES = { defaultvalue: 'defaultValue', defaultchecked: 'defaultChecked', srcobject: 'srcObject', - novalidate: 'noValidate' + novalidate: 'noValidate', + allowfullscreen: 'allowFullscreen', + disablepictureinpicture: 'disablePictureInPicture', + disableremoteplayback: 'disableRemotePlayback' }; /** @@ -219,7 +225,11 @@ const DOM_PROPERTIES = [ 'volume', 'defaultValue', 'defaultChecked', - 'srcObject' + 'srcObject', + 'noValidate', + 'allowFullscreen', + 'disablePictureInPicture', + 'disableRemotePlayback' ]; /** diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/_config.js b/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/_config.js new file mode 100644 index 0000000000..d0b8a421b3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/_config.js @@ -0,0 +1,60 @@ +import { test } from '../../test'; + +export default test({ + // JSDOM lacks support for some of these attributes, so we'll skip it for now. + // + // See: + // - `async`: https://github.com/jsdom/jsdom/issues/1564 + // - `nomodule`: https://github.com/jsdom/jsdom/issues/2475 + // - `autofocus`: https://github.com/jsdom/jsdom/issues/3041 + // - `inert`: https://github.com/jsdom/jsdom/issues/3605 + // - etc...: https://github.com/jestjs/jest/issues/139#issuecomment-592673550 + skip_mode: ['client'], + + html: ` + <script nomodule async defer></script> + <form novalidate></form> + <input readonly required checked webkitdirectory> + <select multiple disabled></select> + <button formnovalidate></button> + <img ismap> + <video autoplay controls loop muted playsinline disablepictureinpicture disableremoteplayback></video> + <audio disableremoteplayback></audio> + <track default> + <iframe allowfullscreen></iframe> + <details open></details> + <ol reversed></ol> + <div autofocus></div> + <span inert></span> + + <script nomodule async defer></script> + <form novalidate></form> + <input readonly required checked webkitdirectory> + <select multiple disabled></select> + <button formnovalidate></button> + <img ismap> + <video autoplay controls loop muted playsinline disablepictureinpicture disableremoteplayback></video> + <audio disableremoteplayback></audio> + <track default> + <iframe allowfullscreen></iframe> + <details open></details> + <ol reversed></ol> + <div autofocus></div> + <span inert></span> + + <script></script> + <form></form> + <input> + <select></select> + <button></button> + <img> + <video></video> + <audio></audio> + <track> + <iframe></iframe> + <details></details> + <ol></ol> + <div></div> + <span></span> +` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/main.svelte b/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/main.svelte new file mode 100644 index 0000000000..e9e5a16168 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attribute-boolean-case-insensitivity/main.svelte @@ -0,0 +1,22 @@ +<script> + let runesMode = $state('using a rune so that we trigger runes mode'); + + const attributeValues = [true, 'test', false]; +</script> + +{#each attributeValues as val} + <script NOMODULE={val} ASYNC={val} DEFER={val}></script> + <form NOVALIDATE={val}></form> + <input READONLY={val} REQUIRED={val} CHECKED={val} WEBKITDIRECTORY={val} /> + <select MULTIPLE={val} DISABLED={val}></select> + <button FORMNOVALIDATE={val}></button> + <img ISMAP={val} /> + <video AUTOPLAY={val} CONTROLS={val} LOOP={val} MUTED={val} PLAYSINLINE={val} DISABLEPICTUREINPICTURE={val} DISABLEREMOTEPLAYBACK={val}></video> + <audio DISABLEREMOTEPLAYBACK={val}></audio> + <track DEFAULT={val} /> + <iframe ALLOWFULLSCREEN={val}></iframe> + <details OPEN={val}></details> + <ol REVERSED={val}></ol> + <div AUTOFOCUS={val}></div> + <span INERT={val}></span> +{/each} From 3d828b46b24964163cbe184e683e1db9f4b0ddfd Mon Sep 17 00:00:00 2001 From: Elliott Johnson <sejohnson@torchcloudconsulting.com> Date: Thu, 23 Jan 2025 16:45:14 -0700 Subject: [PATCH 0115/1429] feat: Remove repository-sync workflows in favor of github-docs-bot (#15097) * Revert "fix: ensure untrack correctly retains the active reaction (#15065)" This reverts commit 2ad519542dcba2d7802394068d412bb73d9cb803. * feat: Remove repository-sync workflows in favor of github-docs-bot * Revert "Revert "fix: ensure untrack correctly retains the active reaction (#15065)"" This reverts commit f4e98bf89f80cc332703c1da3c2bd3d1451d1dce. --- .../workflows/docs-preview-create-request.yml | 26 ------------------ .../workflows/docs-preview-delete-request.yml | 27 ------------------- .github/workflows/sync-request.yml | 22 --------------- 3 files changed, 75 deletions(-) delete mode 100644 .github/workflows/docs-preview-create-request.yml delete mode 100644 .github/workflows/docs-preview-delete-request.yml delete mode 100644 .github/workflows/sync-request.yml diff --git a/.github/workflows/docs-preview-create-request.yml b/.github/workflows/docs-preview-create-request.yml deleted file mode 100644 index f57766dc36..0000000000 --- a/.github/workflows/docs-preview-create-request.yml +++ /dev/null @@ -1,26 +0,0 @@ -# https://github.com/sveltejs/svelte.dev/blob/main/apps/svelte.dev/scripts/sync-docs/README.md -name: Docs preview create request - -on: - pull_request_target: - branches: - - main - -jobs: - dispatch: - runs-on: ubuntu-latest - steps: - - name: Repository Dispatch - uses: peter-evans/repository-dispatch@v3 - with: - token: ${{ secrets.SYNC_REQUEST_TOKEN }} - repository: sveltejs/svelte.dev - event-type: docs-preview-create - client-payload: |- - { - "package": "svelte", - "repo": "${{ github.repository }}", - "owner": "${{ github.event.pull_request.head.repo.owner.login }}", - "branch": "${{ github.event.pull_request.head.ref }}", - "pr": ${{ github.event.pull_request.number }} - } diff --git a/.github/workflows/docs-preview-delete-request.yml b/.github/workflows/docs-preview-delete-request.yml deleted file mode 100644 index 4eb0e996a6..0000000000 --- a/.github/workflows/docs-preview-delete-request.yml +++ /dev/null @@ -1,27 +0,0 @@ -# https://github.com/sveltejs/svelte.dev/blob/main/apps/svelte.dev/scripts/sync-docs/README.md -name: Docs preview delete request - -on: - pull_request_target: - branches: - - main - types: [closed] - -jobs: - dispatch: - runs-on: ubuntu-latest - steps: - - name: Repository Dispatch - uses: peter-evans/repository-dispatch@v3 - with: - token: ${{ secrets.SYNC_REQUEST_TOKEN }} - repository: sveltejs/svelte.dev - event-type: docs-preview-delete - client-payload: |- - { - "package": "svelte", - "repo": "${{ github.repository }}", - "owner": "${{ github.event.pull_request.head.repo.owner.login }}", - "branch": "${{ github.event.pull_request.head.ref }}", - "pr": ${{ github.event.pull_request.number }} - } diff --git a/.github/workflows/sync-request.yml b/.github/workflows/sync-request.yml deleted file mode 100644 index de2ce77692..0000000000 --- a/.github/workflows/sync-request.yml +++ /dev/null @@ -1,22 +0,0 @@ -# https://github.com/sveltejs/svelte.dev/blob/main/apps/svelte.dev/scripts/sync-docs/README.md -name: Sync request - -on: - push: - branches: - - main - -jobs: - dispatch: - runs-on: ubuntu-latest - steps: - - name: Repository Dispatch - uses: peter-evans/repository-dispatch@v3 - with: - token: ${{ secrets.SYNC_REQUEST_TOKEN }} - repository: sveltejs/svelte.dev - event-type: sync-request - client-payload: |- - { - "package": "svelte" - } From e9cc7dc9a17392651512223c2fde7fe6d69eda72 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Thu, 23 Jan 2025 22:22:14 -0500 Subject: [PATCH 0116/1429] chore: tweak each block code (#15094) --- .../3-transform/client/visitors/EachBlock.js | 36 ++++++++++++------- packages/svelte/src/compiler/phases/scope.js | 11 ------ .../svelte/src/compiler/types/template.d.ts | 2 -- .../src/internal/client/dom/blocks/each.js | 6 ++-- 4 files changed, 26 insertions(+), 29 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js index 9f70981205..24a696e7d2 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js @@ -35,10 +35,6 @@ export function EachBlock(node, context) { context.state.template.push('<!>'); } - if (each_node_meta.array_name !== null) { - context.state.init.push(b.const(each_node_meta.array_name, b.thunk(collection))); - } - let flags = 0; if (node.metadata.keyed && node.index) { @@ -120,8 +116,21 @@ export function EachBlock(node, context) { return [array, ...transitive_dependencies]; }); - if (each_node_meta.array_name) { - indirect_dependencies.push(b.call(each_node_meta.array_name)); + /** @type {Identifier | null} */ + let collection_id = null; + + // Check if inner scope shadows something from outer scope. + // This is necessary because we need access to the array expression of the each block + // in the inner scope if bindings are used, in order to invalidate the array. + for (const [name] of context.state.scope.declarations) { + if (context.state.scope.parent?.get(name) != null) { + collection_id = context.state.scope.root.unique('$$array'); + break; + } + } + + if (collection_id) { + indirect_dependencies.push(b.call(collection_id)); } else { indirect_dependencies.push(collection); @@ -195,7 +204,7 @@ export function EachBlock(node, context) { // TODO 6.0 this only applies in legacy mode, reassignments are // forbidden in runes mode return b.member( - each_node_meta.array_name ? b.call(each_node_meta.array_name) : collection, + collection_id ? b.call(collection_id) : collection, (flags & EACH_INDEX_REACTIVE) !== 0 ? get_value(index) : index, true ); @@ -207,7 +216,7 @@ export function EachBlock(node, context) { uses_index = true; const left = b.member( - each_node_meta.array_name ? b.call(each_node_meta.array_name) : collection, + collection_id ? b.call(collection_id) : collection, (flags & EACH_INDEX_REACTIVE) !== 0 ? get_value(index) : index, true ); @@ -283,16 +292,17 @@ export function EachBlock(node, context) { ); } + const render_args = [b.id('$$anchor'), item]; + if (uses_index || collection_id) render_args.push(index); + if (collection_id) render_args.push(collection_id); + /** @type {Expression[]} */ const args = [ context.state.node, b.literal(flags), - each_node_meta.array_name ? each_node_meta.array_name : b.thunk(collection), + b.thunk(collection), key_function, - b.arrow( - uses_index ? [b.id('$$anchor'), item, index] : [b.id('$$anchor'), item], - b.block(declarations.concat(block.body)) - ) + b.arrow(render_args, b.block(declarations.concat(block.body))) ]; if (node.fallback) { diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 3536dd6a18..51e9eb088d 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -575,21 +575,10 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) { } if (node.fallback) visit(node.fallback, { scope }); - // Check if inner scope shadows something from outer scope. - // This is necessary because we need access to the array expression of the each block - // in the inner scope if bindings are used, in order to invalidate the array. - let needs_array_deduplication = false; - for (const [name] of scope.declarations) { - if (state.scope.get(name) !== null) { - needs_array_deduplication = true; - } - } - node.metadata = { expression: create_expression_metadata(), keyed: false, contains_group_binding: false, - array_name: needs_array_deduplication ? state.scope.root.unique('$$array') : null, index: scope.root.unique('$$index'), declarations: scope.declarations, is_controlled: false diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index fb60966895..1f266c5248 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -414,8 +414,6 @@ export namespace AST { expression: ExpressionMetadata; keyed: boolean; contains_group_binding: boolean; - /** Set if something in the array expression is shadowed within the each block */ - array_name: Identifier | null; index: Identifier; declarations: Map<string, Binding>; /** diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index b17090948a..040e585215 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -271,7 +271,7 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f * @param {Array<V>} array * @param {EachState} state * @param {Element | Comment | Text} anchor - * @param {(anchor: Node, item: MaybeSource<V>, index: number | Source<number>) => void} render_fn + * @param {(anchor: Node, item: MaybeSource<V>, index: number | Source<number>, collection: () => V[]) => void} render_fn * @param {number} flags * @param {boolean} is_inert * @param {(value: V, index: number) => any} get_key @@ -510,7 +510,7 @@ function update_item(item, value, index, type) { * @param {V} value * @param {unknown} key * @param {number} index - * @param {(anchor: Node, item: V | Source<V>, index: number | Value<number>) => void} render_fn + * @param {(anchor: Node, item: V | Source<V>, index: number | Value<number>, collection: () => V[]) => void} render_fn * @param {number} flags * @param {() => V[]} get_collection * @returns {EachItem} @@ -559,7 +559,7 @@ function create_item( current_each_item = item; try { - item.e = branch(() => render_fn(anchor, v, i), hydrating); + item.e = branch(() => render_fn(anchor, v, i, get_collection), hydrating); item.e.prev = prev && prev.e; item.e.next = next && next.e; From 24b6fab58b92e02703f9200d727d124493a020c9 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 24 Jan 2025 18:49:11 -0500 Subject: [PATCH 0117/1429] chore: construct analysis before module walk (#15108) --- .../src/compiler/phases/2-analyze/index.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 76c1e94277..c65eb5f955 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -243,7 +243,15 @@ export function analyze_module(ast, options) { } } - const analysis = { runes: true, tracing: false }; + /** @type {Analysis} */ + const analysis = { + module: { ast, scope, scopes }, + name: options.filename, + accessors: false, + runes: true, + immutable: true, + tracing: false + }; walk( /** @type {Node} */ (ast), @@ -256,14 +264,7 @@ export function analyze_module(ast, options) { visitors ); - return { - module: { ast, scope, scopes }, - name: options.filename, - accessors: false, - runes: true, - immutable: true, - tracing: analysis.tracing - }; + return analysis; } /** From e3d4a0fa8eb76c4f63d3f7c62fe768001972d3c8 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 24 Jan 2025 19:02:43 -0500 Subject: [PATCH 0118/1429] chore: tweak render tag logic (#15109) --- .../src/compiler/phases/1-parse/state/tag.js | 2 +- .../src/compiler/phases/2-analyze/index.js | 2 -- .../src/compiler/phases/2-analyze/types.d.ts | 2 -- .../2-analyze/visitors/CallExpression.js | 12 ------------ .../phases/2-analyze/visitors/RenderTag.js | 16 ++++++++++++++-- .../3-transform/client/visitors/RenderTag.js | 18 +++++++++++------- .../svelte/src/compiler/types/template.d.ts | 2 +- 7 files changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/svelte/src/compiler/phases/1-parse/state/tag.js b/packages/svelte/src/compiler/phases/1-parse/state/tag.js index 95d7d00677..0eb98c27e8 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -706,7 +706,7 @@ function special(parser) { expression: /** @type {AST.RenderTag['expression']} */ (expression), metadata: { dynamic: false, - args_with_call_expression: new Set(), + arguments: [], path: [], snippets: new Set() } diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index c65eb5f955..bce6672505 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -605,7 +605,6 @@ export function analyze_component(root, source, options) { has_props_rune: false, component_slots: new Set(), expression: null, - render_tag: null, private_derived_state: [], function_depth: scope.function_depth, instance_scope: instance.scope, @@ -677,7 +676,6 @@ export function analyze_component(root, source, options) { reactive_statements: analysis.reactive_statements, component_slots: new Set(), expression: null, - render_tag: null, private_derived_state: [], function_depth: scope.function_depth }; diff --git a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts index b4ca4dc262..1e71accb9f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts +++ b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts @@ -19,8 +19,6 @@ export interface AnalysisState { component_slots: Set<string>; /** Information about the current expression/directive/block value */ expression: ExpressionMetadata | null; - /** The current {@render ...} tag, if any */ - render_tag: null | AST.RenderTag; private_derived_state: string[]; function_depth: number; diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index 9f51cd61de..96788d9f93 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -186,18 +186,6 @@ export function CallExpression(node, context) { break; } - if (context.state.render_tag) { - // Find out which of the render tag arguments contains this call expression - const arg_idx = unwrap_optional(context.state.render_tag.expression).arguments.findIndex( - (arg) => arg === node || context.path.includes(arg) - ); - - // -1 if this is the call expression of the render tag itself - if (arg_idx !== -1) { - context.state.render_tag.metadata.args_with_call_expression.add(arg_idx); - } - } - if (node.callee.type === 'Identifier') { const binding = context.state.scope.get(node.callee.name); diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/RenderTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/RenderTag.js index 045224276a..a8c9d408bd 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/RenderTag.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/RenderTag.js @@ -5,6 +5,7 @@ import * as e from '../../../errors.js'; import { validate_opening_tag } from './shared/utils.js'; import { mark_subtree_dynamic } from './shared/fragment.js'; import { is_resolved_snippet } from './shared/snippets.js'; +import { create_expression_metadata } from '../../nodes.js'; /** * @param {AST.RenderTag} node @@ -15,7 +16,8 @@ export function RenderTag(node, context) { node.metadata.path = [...context.path]; - const callee = unwrap_optional(node.expression).callee; + const expression = unwrap_optional(node.expression); + const callee = expression.callee; const binding = callee.type === 'Identifier' ? context.state.scope.get(callee.name) : null; @@ -52,5 +54,15 @@ export function RenderTag(node, context) { mark_subtree_dynamic(context.path); - context.next({ ...context.state, render_tag: node }); + context.visit(callee); + + for (const arg of expression.arguments) { + const metadata = create_expression_metadata(); + node.metadata.arguments.push(metadata); + + context.visit(arg, { + ...context.state, + expression: metadata + }); + } } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js index 7da987f6cc..33ae6d4d2b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js @@ -10,20 +10,24 @@ import * as b from '../../../../utils/builders.js'; */ export function RenderTag(node, context) { context.state.template.push('<!>'); - const callee = unwrap_optional(node.expression).callee; - const raw_args = unwrap_optional(node.expression).arguments; + + const expression = unwrap_optional(node.expression); + + const callee = expression.callee; + const raw_args = expression.arguments; /** @type {Expression[]} */ let args = []; for (let i = 0; i < raw_args.length; i++) { - const raw = raw_args[i]; - const arg = /** @type {Expression} */ (context.visit(raw)); - if (node.metadata.args_with_call_expression.has(i)) { + let thunk = b.thunk(/** @type {Expression} */ (context.visit(raw_args[i]))); + const { has_call } = node.metadata.arguments[i]; + + if (has_call) { const id = b.id(context.state.scope.generate('render_arg')); - context.state.init.push(b.var(id, b.call('$.derived_safe_equal', b.thunk(arg)))); + context.state.init.push(b.var(id, b.call('$.derived_safe_equal', thunk))); args.push(b.thunk(b.call('$.get', id))); } else { - args.push(b.thunk(arg)); + args.push(thunk); } } diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index 1f266c5248..a544cd1dec 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -166,7 +166,7 @@ export namespace AST { /** @internal */ metadata: { dynamic: boolean; - args_with_call_expression: Set<number>; + arguments: ExpressionMetadata[]; path: SvelteNode[]; /** The set of locally-defined snippets that this render tag could correspond to, * used for CSS pruning purposes */ From 357e1a74b4af25c3e3839d303cc3d8d239d43087 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 19:03:51 -0500 Subject: [PATCH 0119/1429] Version Packages (#15088) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/clever-cherries-hear.md | 5 ----- .changeset/dirty-sloths-move.md | 5 ----- .changeset/large-islands-cover.md | 5 ----- .changeset/pink-wolves-search.md | 5 ----- .changeset/strange-hairs-trade.md | 5 ----- .changeset/tasty-pigs-greet.md | 5 ----- .changeset/two-doors-exercise.md | 5 ----- packages/svelte/CHANGELOG.md | 18 ++++++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 10 files changed, 20 insertions(+), 37 deletions(-) delete mode 100644 .changeset/clever-cherries-hear.md delete mode 100644 .changeset/dirty-sloths-move.md delete mode 100644 .changeset/large-islands-cover.md delete mode 100644 .changeset/pink-wolves-search.md delete mode 100644 .changeset/strange-hairs-trade.md delete mode 100644 .changeset/tasty-pigs-greet.md delete mode 100644 .changeset/two-doors-exercise.md diff --git a/.changeset/clever-cherries-hear.md b/.changeset/clever-cherries-hear.md deleted file mode 100644 index df37943b46..0000000000 --- a/.changeset/clever-cherries-hear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: don't throw for `undefined` non delegated event handlers diff --git a/.changeset/dirty-sloths-move.md b/.changeset/dirty-sloths-move.md deleted file mode 100644 index 07436a0b2e..0000000000 --- a/.changeset/dirty-sloths-move.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"svelte": patch ---- - -fix: consistently set value to blank string when value attribute is undefined diff --git a/.changeset/large-islands-cover.md b/.changeset/large-islands-cover.md deleted file mode 100644 index 969d2fc254..0000000000 --- a/.changeset/large-islands-cover.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: optimise || expressions in template diff --git a/.changeset/pink-wolves-search.md b/.changeset/pink-wolves-search.md deleted file mode 100644 index cd734062c6..0000000000 --- a/.changeset/pink-wolves-search.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly handle `novalidate` attribute casing diff --git a/.changeset/strange-hairs-trade.md b/.changeset/strange-hairs-trade.md deleted file mode 100644 index 192e5ef3d1..0000000000 --- a/.changeset/strange-hairs-trade.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: expand boolean attribute support diff --git a/.changeset/tasty-pigs-greet.md b/.changeset/tasty-pigs-greet.md deleted file mode 100644 index 63decceed8..0000000000 --- a/.changeset/tasty-pigs-greet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: avoid double deriveds in component props diff --git a/.changeset/two-doors-exercise.md b/.changeset/two-doors-exercise.md deleted file mode 100644 index b41e582ae7..0000000000 --- a/.changeset/two-doors-exercise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"svelte": patch ---- - -fix: add check for `is` attribute to correctly detect custom elements diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index e15c0e0a09..ba912c1dd8 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,23 @@ # svelte +## 5.19.3 + +### Patch Changes + +- fix: don't throw for `undefined` non delegated event handlers ([#15087](https://github.com/sveltejs/svelte/pull/15087)) + +- fix: consistently set value to blank string when value attribute is undefined ([#15057](https://github.com/sveltejs/svelte/pull/15057)) + +- fix: optimise || expressions in template ([#15092](https://github.com/sveltejs/svelte/pull/15092)) + +- fix: correctly handle `novalidate` attribute casing ([#15083](https://github.com/sveltejs/svelte/pull/15083)) + +- fix: expand boolean attribute support ([#15095](https://github.com/sveltejs/svelte/pull/15095)) + +- fix: avoid double deriveds in component props ([#15089](https://github.com/sveltejs/svelte/pull/15089)) + +- fix: add check for `is` attribute to correctly detect custom elements ([#15086](https://github.com/sveltejs/svelte/pull/15086)) + ## 5.19.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 48287cf901..76ce768cb5 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.19.2", + "version": "5.19.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 7a3f016aaa..0e5fec3b81 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.19.2'; +export const VERSION = '5.19.3'; export const PUBLIC_VERSION = '5'; From 94d80c6e2c456d79a283164ae11f3991745be97d Mon Sep 17 00:00:00 2001 From: Shay Molcho <152275799+shaymolcho@users.noreply.github.com> Date: Mon, 27 Jan 2025 01:59:58 +0200 Subject: [PATCH 0120/1429] Added missing period for consistency and readability (#15114) Added a missing period in a specific part of the text to maintain consistency across the document. This ensures a uniform writing style, improves readability, and aligns with the formatting used throughout the content. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f7d15f905e..0e2628f84f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -51,7 +51,7 @@ We use [GitHub issues](https://github.com/sveltejs/svelte/issues) for our public If you have questions about using Svelte, contact us on Discord at [svelte.dev/chat](https://svelte.dev/chat), and we will do our best to answer your questions. -If you see anything you'd like to be implemented, create a [feature request issue](https://github.com/sveltejs/svelte/issues/new?template=feature_request.yml) +If you see anything you'd like to be implemented, create a [feature request issue](https://github.com/sveltejs/svelte/issues/new?template=feature_request.yml). ### Reporting new issues From 0e0f01ee1cfe11461f799c034030840a1b7cd763 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Mon, 27 Jan 2025 08:13:13 -0500 Subject: [PATCH 0121/1429] fix: disallow $state/$derived in const tags (#15115) --- .changeset/strong-shoes-whisper.md | 5 +++++ .../phases/2-analyze/visitors/CallExpression.js | 3 ++- .../const-tag-invalid-rune-usage/errors.json | 14 ++++++++++++++ .../const-tag-invalid-rune-usage/input.svelte | 3 +++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .changeset/strong-shoes-whisper.md create mode 100644 packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/errors.json create mode 100644 packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/input.svelte diff --git a/.changeset/strong-shoes-whisper.md b/.changeset/strong-shoes-whisper.md new file mode 100644 index 0000000000..ea7f8d6c5e --- /dev/null +++ b/.changeset/strong-shoes-whisper.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: disallow $state/$derived in const tags diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index 96788d9f93..0a6b3f3ee5 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -79,7 +79,8 @@ export function CallExpression(node, context) { case '$derived': case '$derived.by': if ( - parent.type !== 'VariableDeclarator' && + (parent.type !== 'VariableDeclarator' || + get_parent(context.path, -3).type === 'ConstTag') && !(parent.type === 'PropertyDefinition' && !parent.static && !parent.computed) ) { e.state_invalid_placement(node, rune); diff --git a/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/errors.json b/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/errors.json new file mode 100644 index 0000000000..32594e4268 --- /dev/null +++ b/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "state_invalid_placement", + "message": "`$derived(...)` can only be used as a variable declaration initializer or a class field", + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 26 + } + } +] diff --git a/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/input.svelte b/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/input.svelte new file mode 100644 index 0000000000..a056058cc5 --- /dev/null +++ b/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/input.svelte @@ -0,0 +1,3 @@ +{#snippet test()} + {@const der = $derived(0)} +{/snippet} \ No newline at end of file From 3f8ce21f4709fe555fcc5ffc63728c6e8d139781 Mon Sep 17 00:00:00 2001 From: Elliott Johnson <sejohnson@torchcloudconsulting.com> Date: Mon, 27 Jan 2025 13:55:49 -0700 Subject: [PATCH 0122/1429] fix: Add `bind:focused` to `HTMLAttributes` (#15122) * Revert "fix: ensure untrack correctly retains the active reaction (#15065)" This reverts commit 2ad519542dcba2d7802394068d412bb73d9cb803. * feat: Add `bind:focused` to `HTMLAttributes` * Revert "Revert "fix: ensure untrack correctly retains the active reaction (#15065)"" This reverts commit f4e98bf89f80cc332703c1da3c2bd3d1451d1dce. * changeset * tests --- .changeset/eleven-scissors-cheat.md | 5 +++++ packages/svelte/elements.d.ts | 1 + packages/svelte/tests/types/bindings.svelte | 8 ++++++++ 3 files changed, 14 insertions(+) create mode 100644 .changeset/eleven-scissors-cheat.md create mode 100644 packages/svelte/tests/types/bindings.svelte diff --git a/.changeset/eleven-scissors-cheat.md b/.changeset/eleven-scissors-cheat.md new file mode 100644 index 0000000000..5aa577d95d --- /dev/null +++ b/.changeset/eleven-scissors-cheat.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: Add `bind:focused` property to `HTMLAttributes` type diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts index 96f1589800..6d256b5620 100644 --- a/packages/svelte/elements.d.ts +++ b/packages/svelte/elements.d.ts @@ -839,6 +839,7 @@ export interface HTMLAttributes<T extends EventTarget> extends AriaAttributes, D readonly 'bind:contentBoxSize'?: Array<ResizeObserverSize> | undefined | null; readonly 'bind:borderBoxSize'?: Array<ResizeObserverSize> | undefined | null; readonly 'bind:devicePixelContentBoxSize'?: Array<ResizeObserverSize> | undefined | null; + readonly 'bind:focused'?: boolean | undefined | null; // SvelteKit 'data-sveltekit-keepfocus'?: true | '' | 'off' | undefined | null; diff --git a/packages/svelte/tests/types/bindings.svelte b/packages/svelte/tests/types/bindings.svelte new file mode 100644 index 0000000000..ce99b2c296 --- /dev/null +++ b/packages/svelte/tests/types/bindings.svelte @@ -0,0 +1,8 @@ +<script lang="ts"> + let focused = $state(false); +</script> + +<input bind:focused={focused} type="text" /> +<textarea bind:focused={focused}></textarea> +<select bind:focused={focused}></select> +<div bind:focused={focused}></div> From fc4dd2dec4387933579762f1cf4113a26cc1e22b Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Tue, 28 Jan 2025 13:15:12 +0000 Subject: [PATCH 0123/1429] fix: lazily connect derievds (in deriveds) to their parent (#15129) --- .changeset/few-beans-bow.md | 5 ++++ .../internal/client/reactivity/deriveds.js | 4 --- .../svelte/src/internal/client/runtime.js | 24 ++++++++++------- packages/svelte/tests/signals/test.ts | 27 +++++++++++++++++++ 4 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 .changeset/few-beans-bow.md diff --git a/.changeset/few-beans-bow.md b/.changeset/few-beans-bow.md new file mode 100644 index 0000000000..5c17c242a8 --- /dev/null +++ b/.changeset/few-beans-bow.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: lazily connect derievds (in deriveds) to their parent diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 7ec1ed30bd..da5a75eb6e 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -68,10 +68,6 @@ export function derived(fn) { signal.created = get_stack('CreatedAt'); } - if (parent_derived !== null) { - (parent_derived.children ??= []).push(signal); - } - return signal; } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 1d695e1fee..a002665b51 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -957,26 +957,30 @@ export function get(signal) { new_deps.push(signal); } } - } else if (is_derived && /** @type {Derived} */ (signal).deps === null) { + } + + if ( + is_derived && + /** @type {Derived} */ (signal).deps === null && + (active_reaction === null || untracking || (active_reaction.f & DERIVED) !== 0) + ) { var derived = /** @type {Derived} */ (signal); var parent = derived.parent; - var target = derived; - while (parent !== null) { - // Attach the derived to the nearest parent effect, if there are deriveds - // in between then we also need to attach them too + if (parent !== null) { + // Attach the derived to the nearest parent effect or derived if ((parent.f & DERIVED) !== 0) { var parent_derived = /** @type {Derived} */ (parent); - target = parent_derived; - parent = parent_derived.parent; + if (!parent_derived.children?.includes(derived)) { + (parent_derived.children ??= []).push(derived); + } } else { var parent_effect = /** @type {Effect} */ (parent); - if (!parent_effect.deriveds?.includes(target)) { - (parent_effect.deriveds ??= []).push(target); + if (!parent_effect.deriveds?.includes(derived)) { + (parent_effect.deriveds ??= []).push(derived); } - break; } } } diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index e147fd1d0d..5f0b93e136 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -803,6 +803,33 @@ describe('signals', () => { }; }); + test('nested deriveds do not connect inside parent deriveds if unused', () => { + return () => { + let a = render_effect(() => {}); + let b: Derived<void> | undefined; + + const destroy = effect_root(() => { + a = render_effect(() => { + $.untrack(() => { + b = derived(() => { + derived(() => {}); + derived(() => {}); + derived(() => {}); + }); + $.get(b); + }); + }); + }); + + assert.deepEqual(a.deriveds?.length, 1); + assert.deepEqual(b?.children, null); + + destroy(); + + assert.deepEqual(a.deriveds, null); + }; + }); + test('deriveds containing effects work correctly when used with untrack', () => { return () => { let a = render_effect(() => {}); From b2c8224a734511ad33e2fb7f4ce8309d4e722554 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 28 Jan 2025 13:29:21 +0000 Subject: [PATCH 0124/1429] Version Packages (#15118) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/eleven-scissors-cheat.md | 5 ----- .changeset/few-beans-bow.md | 5 ----- .changeset/strong-shoes-whisper.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 .changeset/eleven-scissors-cheat.md delete mode 100644 .changeset/few-beans-bow.md delete mode 100644 .changeset/strong-shoes-whisper.md diff --git a/.changeset/eleven-scissors-cheat.md b/.changeset/eleven-scissors-cheat.md deleted file mode 100644 index 5aa577d95d..0000000000 --- a/.changeset/eleven-scissors-cheat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: Add `bind:focused` property to `HTMLAttributes` type diff --git a/.changeset/few-beans-bow.md b/.changeset/few-beans-bow.md deleted file mode 100644 index 5c17c242a8..0000000000 --- a/.changeset/few-beans-bow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: lazily connect derievds (in deriveds) to their parent diff --git a/.changeset/strong-shoes-whisper.md b/.changeset/strong-shoes-whisper.md deleted file mode 100644 index ea7f8d6c5e..0000000000 --- a/.changeset/strong-shoes-whisper.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: disallow $state/$derived in const tags diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index ba912c1dd8..3b42ad6927 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.19.4 + +### Patch Changes + +- fix: Add `bind:focused` property to `HTMLAttributes` type ([#15122](https://github.com/sveltejs/svelte/pull/15122)) + +- fix: lazily connect derievds (in deriveds) to their parent ([#15129](https://github.com/sveltejs/svelte/pull/15129)) + +- fix: disallow $state/$derived in const tags ([#15115](https://github.com/sveltejs/svelte/pull/15115)) + ## 5.19.3 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 76ce768cb5..f94992e42e 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.19.3", + "version": "5.19.4", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 0e5fec3b81..b45197aa9c 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.19.3'; +export const VERSION = '5.19.4'; export const PUBLIC_VERSION = '5'; From 5e9b29c351294e84d8e652ac871fbe3e7d620351 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Tue, 28 Jan 2025 15:57:26 -0500 Subject: [PATCH 0125/1429] chore: move context code into new module (#15132) --- packages/svelte/src/index-client.js | 15 +- .../svelte/src/internal/client/context.js | 214 +++++++++++++++++ .../svelte/src/internal/client/dev/legacy.js | 2 +- .../src/internal/client/dev/ownership.js | 2 +- .../src/internal/client/dom/blocks/await.js | 13 +- .../internal/client/dom/blocks/boundary.js | 3 +- .../src/internal/client/dom/blocks/html.js | 2 +- .../src/internal/client/dom/blocks/snippet.js | 2 +- .../client/dom/blocks/svelte-element.js | 3 +- .../internal/client/dom/legacy/lifecycle.js | 3 +- packages/svelte/src/internal/client/index.js | 9 +- packages/svelte/src/internal/client/proxy.js | 5 +- .../internal/client/reactivity/deriveds.js | 4 +- .../src/internal/client/reactivity/effects.js | 3 +- .../src/internal/client/reactivity/sources.js | 2 +- packages/svelte/src/internal/client/render.js | 3 +- .../svelte/src/internal/client/runtime.js | 220 +----------------- .../svelte/src/internal/client/validate.js | 2 +- packages/svelte/src/legacy/legacy-client.js | 10 +- packages/svelte/tests/signals/test.ts | 5 +- packages/svelte/types/index.d.ts | 56 ++--- 21 files changed, 288 insertions(+), 290 deletions(-) create mode 100644 packages/svelte/src/internal/client/context.js diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index 587d766233..bd2b353395 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -1,12 +1,13 @@ /** @import { ComponentContext, ComponentContextLegacy } from '#client' */ /** @import { EventDispatcher } from './index.js' */ /** @import { NotFunction } from './internal/types.js' */ -import { component_context, flush_sync, untrack } from './internal/client/runtime.js'; +import { flush_sync, untrack } from './internal/client/runtime.js'; import { is_array } from './internal/shared/utils.js'; import { user_effect } from './internal/client/index.js'; import * as e from './internal/client/errors.js'; import { lifecycle_outside_component } from './internal/shared/errors.js'; import { legacy_mode_flag } from './internal/flags/index.js'; +import { component_context } from './internal/client/context.js'; /** * The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. @@ -179,15 +180,7 @@ export function flushSync(fn) { flush_sync(fn); } +export { getContext, getAllContexts, hasContext, setContext } from './internal/client/context.js'; export { hydrate, mount, unmount } from './internal/client/render.js'; - -export { - getContext, - getAllContexts, - hasContext, - setContext, - tick, - untrack -} from './internal/client/runtime.js'; - +export { tick, untrack } from './internal/client/runtime.js'; export { createRawSnippet } from './internal/client/dom/blocks/snippet.js'; diff --git a/packages/svelte/src/internal/client/context.js b/packages/svelte/src/internal/client/context.js new file mode 100644 index 0000000000..1d7f0747cd --- /dev/null +++ b/packages/svelte/src/internal/client/context.js @@ -0,0 +1,214 @@ +/** @import { ComponentContext } from '#client' */ + +import { DEV } from 'esm-env'; +import { add_owner } from './dev/ownership.js'; +import { lifecycle_outside_component } from '../shared/errors.js'; +import { source } from './reactivity/sources.js'; +import { + active_effect, + active_reaction, + set_active_effect, + set_active_reaction +} from './runtime.js'; +import { effect } from './reactivity/effects.js'; +import { legacy_mode_flag } from '../flags/index.js'; + +/** @type {ComponentContext | null} */ +export let component_context = null; + +/** @param {ComponentContext | null} context */ +export function set_component_context(context) { + component_context = context; +} + +/** + * The current component function. Different from current component context: + * ```html + * <!-- App.svelte --> + * <Foo> + * <Bar /> <!-- context == Foo.svelte, function == App.svelte --> + * </Foo> + * ``` + * @type {ComponentContext['function']} + */ +export let dev_current_component_function = null; + +/** @param {ComponentContext['function']} fn */ +export function set_dev_current_component_function(fn) { + dev_current_component_function = fn; +} + +/** + * Retrieves the context that belongs to the closest parent component with the specified `key`. + * Must be called during component initialisation. + * + * @template T + * @param {any} key + * @returns {T} + */ +export function getContext(key) { + const context_map = get_or_init_context_map('getContext'); + const result = /** @type {T} */ (context_map.get(key)); + + if (DEV) { + const fn = /** @type {ComponentContext} */ (component_context).function; + if (fn) { + add_owner(result, fn, true); + } + } + + return result; +} + +/** + * Associates an arbitrary `context` object with the current component and the specified `key` + * and returns that object. The context is then available to children of the component + * (including slotted content) with `getContext`. + * + * Like lifecycle functions, this must be called during component initialisation. + * + * @template T + * @param {any} key + * @param {T} context + * @returns {T} + */ +export function setContext(key, context) { + const context_map = get_or_init_context_map('setContext'); + context_map.set(key, context); + return context; +} + +/** + * Checks whether a given `key` has been set in the context of a parent component. + * Must be called during component initialisation. + * + * @param {any} key + * @returns {boolean} + */ +export function hasContext(key) { + const context_map = get_or_init_context_map('hasContext'); + return context_map.has(key); +} + +/** + * Retrieves the whole context map that belongs to the closest parent component. + * Must be called during component initialisation. Useful, for example, if you + * programmatically create a component and want to pass the existing context to it. + * + * @template {Map<any, any>} [T=Map<any, any>] + * @returns {T} + */ +export function getAllContexts() { + const context_map = get_or_init_context_map('getAllContexts'); + + if (DEV) { + const fn = component_context?.function; + if (fn) { + for (const value of context_map.values()) { + add_owner(value, fn, true); + } + } + } + + return /** @type {T} */ (context_map); +} + +/** + * @param {Record<string, unknown>} props + * @param {any} runes + * @param {Function} [fn] + * @returns {void} + */ +export function push(props, runes = false, fn) { + component_context = { + p: component_context, + c: null, + e: null, + m: false, + s: props, + x: null, + l: null + }; + + if (legacy_mode_flag && !runes) { + component_context.l = { + s: null, + u: null, + r1: [], + r2: source(false) + }; + } + + if (DEV) { + // component function + component_context.function = fn; + dev_current_component_function = fn; + } +} + +/** + * @template {Record<string, any>} T + * @param {T} [component] + * @returns {T} + */ +export function pop(component) { + const context_stack_item = component_context; + if (context_stack_item !== null) { + if (component !== undefined) { + context_stack_item.x = component; + } + const component_effects = context_stack_item.e; + if (component_effects !== null) { + var previous_effect = active_effect; + var previous_reaction = active_reaction; + context_stack_item.e = null; + try { + for (var i = 0; i < component_effects.length; i++) { + var component_effect = component_effects[i]; + set_active_effect(component_effect.effect); + set_active_reaction(component_effect.reaction); + effect(component_effect.fn); + } + } finally { + set_active_effect(previous_effect); + set_active_reaction(previous_reaction); + } + } + component_context = context_stack_item.p; + if (DEV) { + dev_current_component_function = context_stack_item.p?.function ?? null; + } + context_stack_item.m = true; + } + // Micro-optimization: Don't set .a above to the empty object + // so it can be garbage-collected when the return here is unused + return component || /** @type {T} */ ({}); +} + +/** + * @param {string} name + * @returns {Map<unknown, unknown>} + */ +function get_or_init_context_map(name) { + if (component_context === null) { + lifecycle_outside_component(name); + } + + return (component_context.c ??= new Map(get_parent_context(component_context) || undefined)); +} + +/** + * @param {ComponentContext} component_context + * @returns {Map<unknown, unknown> | null} + */ +function get_parent_context(component_context) { + let parent = component_context.p; + while (parent !== null) { + const context_map = parent.c; + if (context_map !== null) { + return context_map; + } + parent = parent.p; + } + return null; +} diff --git a/packages/svelte/src/internal/client/dev/legacy.js b/packages/svelte/src/internal/client/dev/legacy.js index a1d2fc8823..138213c551 100644 --- a/packages/svelte/src/internal/client/dev/legacy.js +++ b/packages/svelte/src/internal/client/dev/legacy.js @@ -1,5 +1,5 @@ import * as e from '../errors.js'; -import { component_context } from '../runtime.js'; +import { component_context } from '../context.js'; import { FILENAME } from '../../../constants.js'; import { get_component } from './ownership.js'; diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index d113d9ae90..70cfbb47f3 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -3,7 +3,7 @@ import { STATE_SYMBOL_METADATA } from '../constants.js'; import { render_effect, user_pre_effect } from '../reactivity/effects.js'; -import { dev_current_component_function } from '../runtime.js'; +import { dev_current_component_function } from '../context.js'; import { get_prototype_of } from '../../shared/utils.js'; import * as w from '../warnings.js'; import { FILENAME } from '../../../constants.js'; diff --git a/packages/svelte/src/internal/client/dom/blocks/await.js b/packages/svelte/src/internal/client/dom/blocks/await.js index 62b2e4dd0c..633a6e5bab 100644 --- a/packages/svelte/src/internal/client/dom/blocks/await.js +++ b/packages/svelte/src/internal/client/dom/blocks/await.js @@ -3,18 +3,15 @@ import { DEV } from 'esm-env'; import { is_promise } from '../../../shared/utils.js'; import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js'; import { internal_set, mutable_source, source } from '../../reactivity/sources.js'; +import { flush_sync, is_runes, set_active_effect, set_active_reaction } from '../../runtime.js'; +import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; +import { queue_micro_task } from '../task.js'; +import { UNINITIALIZED } from '../../../../constants.js'; import { component_context, - flush_sync, - is_runes, - set_active_effect, - set_active_reaction, set_component_context, set_dev_current_component_function -} from '../../runtime.js'; -import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; -import { queue_micro_task } from '../task.js'; -import { UNINITIALIZED } from '../../../../constants.js'; +} from '../../context.js'; const PENDING = 0; const THEN = 1; diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index 7f4f000dce..c1ca7a9600 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -1,15 +1,14 @@ /** @import { Effect, TemplateNode, } from '#client' */ import { BOUNDARY_EFFECT, EFFECT_TRANSPARENT } from '../../constants.js'; +import { component_context, set_component_context } from '../../context.js'; import { block, branch, destroy_effect, pause_effect } from '../../reactivity/effects.js'; import { active_effect, active_reaction, - component_context, handle_error, set_active_effect, set_active_reaction, - set_component_context, reset_is_throwing_error } from '../../runtime.js'; import { diff --git a/packages/svelte/src/internal/client/dom/blocks/html.js b/packages/svelte/src/internal/client/dom/blocks/html.js index 04ab0aee87..b3fc5a9c72 100644 --- a/packages/svelte/src/internal/client/dom/blocks/html.js +++ b/packages/svelte/src/internal/client/dom/blocks/html.js @@ -7,7 +7,7 @@ import { assign_nodes } from '../template.js'; import * as w from '../../warnings.js'; import { hash, sanitize_location } from '../../../../utils.js'; import { DEV } from 'esm-env'; -import { dev_current_component_function } from '../../runtime.js'; +import { dev_current_component_function } from '../../context.js'; import { get_first_child, get_next_sibling } from '../operations.js'; /** diff --git a/packages/svelte/src/internal/client/dom/blocks/snippet.js b/packages/svelte/src/internal/client/dom/blocks/snippet.js index cec57f83b7..b916a02ce5 100644 --- a/packages/svelte/src/internal/client/dom/blocks/snippet.js +++ b/packages/svelte/src/internal/client/dom/blocks/snippet.js @@ -6,7 +6,7 @@ import { branch, block, destroy_effect, teardown } from '../../reactivity/effect import { dev_current_component_function, set_dev_current_component_function -} from '../../runtime.js'; +} from '../../context.js'; import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; import { create_fragment_from_html } from '../reconciler.js'; import { assign_nodes } from '../template.js'; diff --git a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js index 35d2f223ae..18641300e5 100644 --- a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js +++ b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js @@ -17,7 +17,8 @@ import { } from '../../reactivity/effects.js'; import { set_should_intro } from '../../render.js'; import { current_each_item, set_current_each_item } from './each.js'; -import { component_context, active_effect } from '../../runtime.js'; +import { active_effect } from '../../runtime.js'; +import { component_context } from '../../context.js'; import { DEV } from 'esm-env'; import { EFFECT_TRANSPARENT } from '../../constants.js'; import { assign_nodes } from '../template.js'; diff --git a/packages/svelte/src/internal/client/dom/legacy/lifecycle.js b/packages/svelte/src/internal/client/dom/legacy/lifecycle.js index 5ffbacc670..a564712f04 100644 --- a/packages/svelte/src/internal/client/dom/legacy/lifecycle.js +++ b/packages/svelte/src/internal/client/dom/legacy/lifecycle.js @@ -1,8 +1,9 @@ /** @import { ComponentContextLegacy } from '#client' */ import { run, run_all } from '../../../shared/utils.js'; +import { component_context } from '../../context.js'; import { derived } from '../../reactivity/deriveds.js'; import { user_pre_effect, user_effect } from '../../reactivity/effects.js'; -import { component_context, deep_read_state, get, untrack } from '../../runtime.js'; +import { deep_read_state, get, untrack } from '../../runtime.js'; /** * Legacy-mode only: Call `onMount` callbacks and set up `beforeUpdate`/`afterUpdate` effects diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 2bf58c51f7..877f3c59ee 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -1,4 +1,5 @@ export { FILENAME, HMR, NAMESPACE_SVG } from '../../constants.js'; +export { push, pop } from './context.js'; export { assign, assign_and, assign_or, assign_nullish } from './dev/assign.js'; export { cleanup_styles } from './dev/css.js'; export { add_locations } from './dev/elements.js'; @@ -141,14 +142,8 @@ export { update, update_pre, exclude_from_object, - pop, - push, deep_read, - deep_read_state, - getAllContexts, - getContext, - setContext, - hasContext + deep_read_state } from './runtime.js'; export { validate_binding, validate_each_keys } from './validate.js'; export { raf } from './timing.js'; diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index 6cbd6394df..4c262880f1 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -1,6 +1,7 @@ -/** @import { ProxyMetadata, ProxyStateObject, Source } from '#client' */ +/** @import { ProxyMetadata, Source } from '#client' */ import { DEV } from 'esm-env'; -import { get, component_context, active_effect } from './runtime.js'; +import { get, active_effect } from './runtime.js'; +import { component_context } from './context.js'; import { array_prototype, get_descriptor, diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index da5a75eb6e..e86963408c 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -17,8 +17,7 @@ import { skip_reaction, update_reaction, increment_write_version, - set_active_effect, - component_context + set_active_effect } from '../runtime.js'; import { equals, safe_equals } from './equality.js'; import * as e from '../errors.js'; @@ -26,6 +25,7 @@ import { destroy_effect } from './effects.js'; import { inspect_effects, set_inspect_effects } from './sources.js'; import { get_stack } from '../dev/tracing.js'; import { tracing_mode_flag } from '../../flags/index.js'; +import { component_context } from '../context.js'; /** * @template V diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 1faf9a47a0..8a7cffecd6 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -1,10 +1,8 @@ /** @import { ComponentContext, ComponentContextLegacy, Derived, Effect, TemplateNode, TransitionManager } from '#client' */ import { check_dirtiness, - component_context, active_effect, active_reaction, - dev_current_component_function, update_effect, get, is_destroying_effect, @@ -45,6 +43,7 @@ import { DEV } from 'esm-env'; import { define_property } from '../../shared/utils.js'; import { get_next_sibling } from '../dom/operations.js'; import { derived, destroy_derived } from './deriveds.js'; +import { component_context, dev_current_component_function } from '../context.js'; /** * @param {'$effect' | '$effect.pre' | '$inspect'} rune diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index c2448c9ee5..b3e66a3336 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -1,7 +1,6 @@ /** @import { Derived, Effect, Reaction, Source, Value } from '#client' */ import { DEV } from 'esm-env'; import { - component_context, active_reaction, active_effect, untracked_writes, @@ -35,6 +34,7 @@ import { import * as e from '../errors.js'; import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js'; import { get_stack } from '../dev/tracing.js'; +import { component_context } from '../context.js'; export let inspect_effects = new Set(); diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index 767b230131..bc74047f64 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -9,7 +9,8 @@ import { init_operations } from './dom/operations.js'; import { HYDRATION_END, HYDRATION_ERROR, HYDRATION_START } from '../../constants.js'; -import { push, pop, component_context, active_effect } from './runtime.js'; +import { active_effect } from './runtime.js'; +import { push, pop, component_context } from './context.js'; import { component_root, branch } from './reactivity/effects.js'; import { hydrate_next, diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index a002665b51..859925186f 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -5,7 +5,6 @@ import { destroy_block_effect_children, destroy_effect_children, destroy_effect_deriveds, - effect, execute_effect_teardown, unlink_effect } from './reactivity/effects.js'; @@ -28,14 +27,18 @@ import { BOUNDARY_EFFECT } from './constants.js'; import { flush_tasks } from './dom/task.js'; -import { add_owner } from './dev/ownership.js'; -import { internal_set, set, source } from './reactivity/sources.js'; +import { internal_set, set } from './reactivity/sources.js'; import { destroy_derived, execute_derived, update_derived } from './reactivity/deriveds.js'; import * as e from './errors.js'; -import { lifecycle_outside_component } from '../shared/errors.js'; import { FILENAME } from '../../constants.js'; import { legacy_mode_flag, tracing_mode_flag } from '../flags/index.js'; import { tracing_expressions, get_stack } from './dev/tracing.js'; +import { + component_context, + dev_current_component_function, + set_component_context, + set_dev_current_component_function +} from './context.js'; const FLUSH_MICROTASK = 0; const FLUSH_SYNC = 1; @@ -150,32 +153,6 @@ export function set_captured_signals(value) { captured_signals = value; } -// Handling runtime component context -/** @type {ComponentContext | null} */ -export let component_context = null; - -/** @param {ComponentContext | null} context */ -export function set_component_context(context) { - component_context = context; -} - -/** - * The current component function. Different from current component context: - * ```html - * <!-- App.svelte --> - * <Foo> - * <Bar /> <!-- context == Foo.svelte, function == App.svelte --> - * </Foo> - * ``` - * @type {ComponentContext['function']} - */ -export let dev_current_component_function = null; - -/** @param {ComponentContext['function']} fn */ -export function set_dev_current_component_function(fn) { - dev_current_component_function = fn; -} - export function increment_write_version() { return ++write_version; } @@ -434,7 +411,7 @@ export function update_reaction(reaction) { active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; skip_reaction = !is_flushing_effect && (flags & UNOWNED) !== 0; derived_sources = null; - component_context = reaction.ctx; + set_component_context(reaction.ctx); untracking = false; read_version++; @@ -498,7 +475,7 @@ export function update_reaction(reaction) { active_reaction = previous_reaction; skip_reaction = previous_skip_reaction; derived_sources = prev_derived_sources; - component_context = previous_component_context; + set_component_context(previous_component_context); untracking = previous_untracking; } } @@ -578,7 +555,7 @@ export function update_effect(effect) { if (DEV) { var previous_component_fn = dev_current_component_function; - dev_current_component_function = effect.component_function; + set_dev_current_component_function(effect.component_function); } try { @@ -620,7 +597,7 @@ export function update_effect(effect) { active_effect = previous_effect; if (DEV) { - dev_current_component_function = previous_component_fn; + set_dev_current_component_function(previous_component_fn); } } } @@ -1114,109 +1091,6 @@ export function set_signal_status(signal, status) { signal.f = (signal.f & STATUS_MASK) | status; } -/** - * Retrieves the context that belongs to the closest parent component with the specified `key`. - * Must be called during component initialisation. - * - * @template T - * @param {any} key - * @returns {T} - */ -export function getContext(key) { - const context_map = get_or_init_context_map('getContext'); - const result = /** @type {T} */ (context_map.get(key)); - - if (DEV) { - const fn = /** @type {ComponentContext} */ (component_context).function; - if (fn) { - add_owner(result, fn, true); - } - } - - return result; -} - -/** - * Associates an arbitrary `context` object with the current component and the specified `key` - * and returns that object. The context is then available to children of the component - * (including slotted content) with `getContext`. - * - * Like lifecycle functions, this must be called during component initialisation. - * - * @template T - * @param {any} key - * @param {T} context - * @returns {T} - */ -export function setContext(key, context) { - const context_map = get_or_init_context_map('setContext'); - context_map.set(key, context); - return context; -} - -/** - * Checks whether a given `key` has been set in the context of a parent component. - * Must be called during component initialisation. - * - * @param {any} key - * @returns {boolean} - */ -export function hasContext(key) { - const context_map = get_or_init_context_map('hasContext'); - return context_map.has(key); -} - -/** - * Retrieves the whole context map that belongs to the closest parent component. - * Must be called during component initialisation. Useful, for example, if you - * programmatically create a component and want to pass the existing context to it. - * - * @template {Map<any, any>} [T=Map<any, any>] - * @returns {T} - */ -export function getAllContexts() { - const context_map = get_or_init_context_map('getAllContexts'); - - if (DEV) { - const fn = component_context?.function; - if (fn) { - for (const value of context_map.values()) { - add_owner(value, fn, true); - } - } - } - - return /** @type {T} */ (context_map); -} - -/** - * @param {string} name - * @returns {Map<unknown, unknown>} - */ -function get_or_init_context_map(name) { - if (component_context === null) { - lifecycle_outside_component(name); - } - - return (component_context.c ??= new Map(get_parent_context(component_context) || undefined)); -} - -/** - * @param {ComponentContext} component_context - * @returns {Map<unknown, unknown> | null} - */ -function get_parent_context(component_context) { - let parent = component_context.p; - while (parent !== null) { - const context_map = parent.c; - if (context_map !== null) { - return context_map; - } - parent = parent.p; - } - return null; -} - /** * @template {number | bigint} T * @param {Value<T>} signal @@ -1264,78 +1138,6 @@ export function exclude_from_object(obj, keys) { return result; } -/** - * @param {Record<string, unknown>} props - * @param {any} runes - * @param {Function} [fn] - * @returns {void} - */ -export function push(props, runes = false, fn) { - component_context = { - p: component_context, - c: null, - e: null, - m: false, - s: props, - x: null, - l: null - }; - - if (legacy_mode_flag && !runes) { - component_context.l = { - s: null, - u: null, - r1: [], - r2: source(false) - }; - } - - if (DEV) { - // component function - component_context.function = fn; - dev_current_component_function = fn; - } -} - -/** - * @template {Record<string, any>} T - * @param {T} [component] - * @returns {T} - */ -export function pop(component) { - const context_stack_item = component_context; - if (context_stack_item !== null) { - if (component !== undefined) { - context_stack_item.x = component; - } - const component_effects = context_stack_item.e; - if (component_effects !== null) { - var previous_effect = active_effect; - var previous_reaction = active_reaction; - context_stack_item.e = null; - try { - for (var i = 0; i < component_effects.length; i++) { - var component_effect = component_effects[i]; - set_active_effect(component_effect.effect); - set_active_reaction(component_effect.reaction); - effect(component_effect.fn); - } - } finally { - set_active_effect(previous_effect); - set_active_reaction(previous_reaction); - } - } - component_context = context_stack_item.p; - if (DEV) { - dev_current_component_function = context_stack_item.p?.function ?? null; - } - context_stack_item.m = true; - } - // Micro-optimization: Don't set .a above to the empty object - // so it can be garbage-collected when the return here is unused - return component || /** @type {T} */ ({}); -} - /** * Possibly traverse an object and read all its properties so that they're all reactive in case this is `$state`. * Does only check first level of an object for performance reasons (heuristic should be good for 99% of all cases). diff --git a/packages/svelte/src/internal/client/validate.js b/packages/svelte/src/internal/client/validate.js index 24e280edf8..ec3d805447 100644 --- a/packages/svelte/src/internal/client/validate.js +++ b/packages/svelte/src/internal/client/validate.js @@ -1,4 +1,4 @@ -import { dev_current_component_function } from './runtime.js'; +import { dev_current_component_function } from './context.js'; import { is_array } from '../shared/utils.js'; import * as e from './errors.js'; import { FILENAME } from '../../constants.js'; diff --git a/packages/svelte/src/legacy/legacy-client.js b/packages/svelte/src/legacy/legacy-client.js index 3715617f4c..3a05bc0496 100644 --- a/packages/svelte/src/legacy/legacy-client.js +++ b/packages/svelte/src/legacy/legacy-client.js @@ -3,19 +3,13 @@ import { DIRTY, LEGACY_PROPS, MAYBE_DIRTY } from '../internal/client/constants.j import { user_pre_effect } from '../internal/client/reactivity/effects.js'; import { mutable_source, set } from '../internal/client/reactivity/sources.js'; import { hydrate, mount, unmount } from '../internal/client/render.js'; -import { - active_effect, - component_context, - dev_current_component_function, - flush_sync, - get, - set_signal_status -} from '../internal/client/runtime.js'; +import { active_effect, flush_sync, get, set_signal_status } from '../internal/client/runtime.js'; import { lifecycle_outside_component } from '../internal/shared/errors.js'; import { define_property, is_array } from '../internal/shared/utils.js'; import * as w from '../internal/client/warnings.js'; import { DEV } from 'esm-env'; import { FILENAME } from '../constants.js'; +import { component_context, dev_current_component_function } from '../internal/client/context.js'; /** * Takes the same options as a Svelte 4 component and the component function and returns a Svelte 4 compatible component. diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index 5f0b93e136..aa1dbf3132 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -1,6 +1,7 @@ import { describe, assert, it } from 'vitest'; import { flushSync } from '../../src/index-client'; import * as $ from '../../src/internal/client/runtime'; +import { push, pop } from '../../src/internal/client/context'; import { effect, effect_root, @@ -22,13 +23,13 @@ import { SvelteSet } from '../../src/reactivity/set'; function run_test(runes: boolean, fn: (runes: boolean) => () => void) { return () => { // Create a component context to test runes vs legacy mode - $.push({}, runes); + push({}, runes); // Create a render context so that effect validations etc don't fail let execute: any; const destroy = effect_root(() => { execute = fn(runes); }); - $.pop(); + pop(); execute(); destroy(); }; diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index d00b2b01ed..eb3e93e4b5 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -421,6 +421,34 @@ declare module 'svelte' { }): Snippet<Params>; /** Anything except a function */ type NotFunction<T> = T extends Function ? never : T; + /** + * Retrieves the context that belongs to the closest parent component with the specified `key`. + * Must be called during component initialisation. + * + * */ + export function getContext<T>(key: any): T; + /** + * Associates an arbitrary `context` object with the current component and the specified `key` + * and returns that object. The context is then available to children of the component + * (including slotted content) with `getContext`. + * + * Like lifecycle functions, this must be called during component initialisation. + * + * */ + export function setContext<T>(key: any, context: T): T; + /** + * Checks whether a given `key` has been set in the context of a parent component. + * Must be called during component initialisation. + * + * */ + export function hasContext(key: any): boolean; + /** + * Retrieves the whole context map that belongs to the closest parent component. + * Must be called during component initialisation. Useful, for example, if you + * programmatically create a component and want to pass the existing context to it. + * + * */ + export function getAllContexts<T extends Map<any, any> = Map<any, any>>(): T; /** * Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component. * Transitions will play during the initial render unless the `intro` option is set to `false`. @@ -484,34 +512,6 @@ declare module 'svelte' { * ``` * */ export function untrack<T>(fn: () => T): T; - /** - * Retrieves the context that belongs to the closest parent component with the specified `key`. - * Must be called during component initialisation. - * - * */ - export function getContext<T>(key: any): T; - /** - * Associates an arbitrary `context` object with the current component and the specified `key` - * and returns that object. The context is then available to children of the component - * (including slotted content) with `getContext`. - * - * Like lifecycle functions, this must be called during component initialisation. - * - * */ - export function setContext<T>(key: any, context: T): T; - /** - * Checks whether a given `key` has been set in the context of a parent component. - * Must be called during component initialisation. - * - * */ - export function hasContext(key: any): boolean; - /** - * Retrieves the whole context map that belongs to the closest parent component. - * Must be called during component initialisation. Useful, for example, if you - * programmatically create a component and want to pass the existing context to it. - * - * */ - export function getAllContexts<T extends Map<any, any> = Map<any, any>>(): T; type Getters<T> = { [K in keyof T]: () => T[K]; }; From 9410ad0318587c338c539ca7cbca1ff9d61db6a0 Mon Sep 17 00:00:00 2001 From: 7nik <kfiiranet@gmail.com> Date: Wed, 29 Jan 2025 13:50:07 +0200 Subject: [PATCH 0126/1429] fix: correctly look for sibling elements inside blocks and components (#15106) Fixes #15027 and fixes #14995 When the target element is inside a block or component, get_following_sibling_elements was looking only for sibling elements after the block/component. This PR fixes it by starting the search from the begging of the block/component and skipping nodes that go before the target element. --- .changeset/curly-balloons-relate.md | 5 + .../phases/2-analyze/css/css-prune.js | 25 ++-- .../svelte/tests/css/samples/has/_config.js | 118 ++++++++++-------- .../svelte/tests/css/samples/has/expected.css | 10 ++ .../svelte/tests/css/samples/has/input.svelte | 12 ++ 5 files changed, 109 insertions(+), 61 deletions(-) create mode 100644 .changeset/curly-balloons-relate.md diff --git a/.changeset/curly-balloons-relate.md b/.changeset/curly-balloons-relate.md new file mode 100644 index 0000000000..69d13b8692 --- /dev/null +++ b/.changeset/curly-balloons-relate.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly look for sibling elements inside blocks and components when pruning CSS diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index ca7476ef7f..109010e88c 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -638,19 +638,30 @@ function get_following_sibling_elements(element, include_self) { /** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */ const siblings = []; - // ...then walk them, starting from the node after the one - // containing the element in question + // ...then walk them, starting from the node containing the element in question + // skipping nodes that appears before the element const seen = new Set(); + let skip = true; /** @param {Compiler.AST.SvelteNode} node */ function get_siblings(node) { walk(node, null, { RegularElement(node) { - siblings.push(node); + if (node === element) { + skip = false; + if (include_self) siblings.push(node); + } else if (!skip) { + siblings.push(node); + } }, SvelteElement(node) { - siblings.push(node); + if (node === element) { + skip = false; + if (include_self) siblings.push(node); + } else if (!skip) { + siblings.push(node); + } }, RenderTag(node) { for (const snippet of node.metadata.snippets) { @@ -663,14 +674,10 @@ function get_following_sibling_elements(element, include_self) { }); } - for (const node of nodes.slice(nodes.indexOf(start) + 1)) { + for (const node of nodes.slice(nodes.indexOf(start))) { get_siblings(node); } - if (include_self) { - siblings.push(element); - } - return siblings; } diff --git a/packages/svelte/tests/css/samples/has/_config.js b/packages/svelte/tests/css/samples/has/_config.js index e5dc5f3459..33bbe74949 100644 --- a/packages/svelte/tests/css/samples/has/_config.js +++ b/packages/svelte/tests/css/samples/has/_config.js @@ -6,182 +6,196 @@ export default test({ code: 'css_unused_selector', message: 'Unused CSS selector ".unused:has(y)"', start: { - line: 31, + line: 33, column: 1, - character: 308 + character: 330 }, end: { - line: 31, + line: 33, column: 15, - character: 322 + character: 344 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ".unused:has(:global(y))"', start: { - line: 34, + line: 36, column: 1, - character: 343 + character: 365 }, end: { - line: 34, + line: 36, column: 24, - character: 366 + character: 388 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "x:has(.unused)"', start: { - line: 37, + line: 39, column: 1, - character: 387 + character: 409 }, end: { - line: 37, + line: 39, column: 15, - character: 401 + character: 423 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ":global(.foo):has(.unused)"', start: { - line: 40, + line: 42, column: 1, - character: 422 + character: 444 }, end: { - line: 40, + line: 42, column: 27, - character: 448 + character: 470 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "x:has(y):has(.unused)"', start: { - line: 50, + line: 52, column: 1, - character: 556 + character: 578 }, end: { - line: 50, + line: 52, column: 22, - character: 577 + character: 599 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ".unused"', start: { - line: 69, + line: 71, column: 2, - character: 782 + character: 804 }, end: { - line: 69, + line: 71, column: 9, - character: 789 + character: 811 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ".unused x:has(y)"', start: { - line: 85, + line: 87, column: 1, - character: 936 + character: 958 }, end: { - line: 85, + line: 87, column: 17, - character: 952 + character: 974 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ".unused:has(.unused)"', start: { - line: 88, + line: 90, column: 1, - character: 973 + character: 995 }, end: { - line: 88, + line: 90, column: 21, - character: 993 + character: 1015 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "x:has(> z)"', start: { - line: 98, + line: 100, column: 1, - character: 1093 + character: 1115 }, end: { - line: 98, + line: 100, column: 11, - character: 1103 + character: 1125 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "x:has(> d)"', start: { - line: 101, + line: 103, column: 1, - character: 1124 + character: 1146 }, end: { - line: 101, + line: 103, column: 11, - character: 1134 + character: 1156 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "x:has(~ y)"', start: { - line: 121, + line: 123, column: 1, - character: 1326 + character: 1348 }, end: { - line: 121, + line: 123, column: 11, - character: 1336 + character: 1358 + } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector "f:has(~ d)"', + start: { + line: 133, + column: 1, + character: 1446 + }, + end: { + line: 133, + column: 11, + character: 1456 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ":has(.unused)"', start: { - line: 129, + line: 141, column: 2, - character: 1409 + character: 1529 }, end: { - line: 129, + line: 141, column: 15, - character: 1422 + character: 1542 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "&:has(.unused)"', start: { - line: 135, + line: 147, column: 2, - character: 1480 + character: 1600 }, end: { - line: 135, + line: 147, column: 16, - character: 1494 + character: 1614 } } ] diff --git a/packages/svelte/tests/css/samples/has/expected.css b/packages/svelte/tests/css/samples/has/expected.css index 68d6aad68a..9627bf730c 100644 --- a/packages/svelte/tests/css/samples/has/expected.css +++ b/packages/svelte/tests/css/samples/has/expected.css @@ -112,6 +112,16 @@ color: red; }*/ + d.svelte-xyz:has(+ e:where(.svelte-xyz)) { + color: green; + } + d.svelte-xyz:has(~ f:where(.svelte-xyz)) { + color: green; + } + /* (unused) f:has(~ d) { + color: red; + }*/ + .foo { .svelte-xyz:has(x:where(.svelte-xyz)) { color: green; diff --git a/packages/svelte/tests/css/samples/has/input.svelte b/packages/svelte/tests/css/samples/has/input.svelte index 3487b64e8c..946cf2df90 100644 --- a/packages/svelte/tests/css/samples/has/input.svelte +++ b/packages/svelte/tests/css/samples/has/input.svelte @@ -3,6 +3,8 @@ <z></z> {#if foo} <d></d> + <e></e> + <f></f> {/if} </y> </x> @@ -122,6 +124,16 @@ color: red; } + d:has(+ e) { + color: green; + } + d:has(~ f) { + color: green; + } + f:has(~ d) { + color: red; + } + :global(.foo) { :has(x) { color: green; From 13a6d555c05f0833d828a518f70722dba351fabc Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Wed, 29 Jan 2025 13:17:12 +0000 Subject: [PATCH 0127/1429] fix: improve derived connection to ownership graph (#15137) * fix: improve derived connection to ownership graph * revised * revised * revised * revised * feedback * feedback * invasive change * fix bugs * fix other bug --- .changeset/blue-rocks-play.md | 5 + .../internal/client/reactivity/deriveds.js | 42 ++-- .../src/internal/client/reactivity/effects.js | 22 +- .../src/internal/client/reactivity/types.d.ts | 6 +- .../svelte/src/internal/client/runtime.js | 46 ++-- packages/svelte/tests/signals/test.ts | 201 +++++++++++++----- 6 files changed, 199 insertions(+), 123 deletions(-) create mode 100644 .changeset/blue-rocks-play.md diff --git a/.changeset/blue-rocks-play.md b/.changeset/blue-rocks-play.md new file mode 100644 index 0000000000..31e9991148 --- /dev/null +++ b/.changeset/blue-rocks-play.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve derived connection to ownership graph diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index e86963408c..1f65ff38c3 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -35,8 +35,12 @@ import { component_context } from '../context.js'; /*#__NO_SIDE_EFFECTS__*/ export function derived(fn) { var flags = DERIVED | DIRTY; + var parent_derived = + active_reaction !== null && (active_reaction.f & DERIVED) !== 0 + ? /** @type {Derived} */ (active_reaction) + : null; - if (active_effect === null) { + if (active_effect === null || (parent_derived !== null && (parent_derived.f & UNOWNED) !== 0)) { flags |= UNOWNED; } else { // Since deriveds are evaluated lazily, any effects created inside them are @@ -44,16 +48,11 @@ export function derived(fn) { active_effect.f |= EFFECT_HAS_DERIVED; } - var parent_derived = - active_reaction !== null && (active_reaction.f & DERIVED) !== 0 - ? /** @type {Derived} */ (active_reaction) - : null; - /** @type {Derived<V>} */ const signal = { - children: null, ctx: component_context, deps: null, + effects: null, equals, f: flags, fn, @@ -87,19 +86,14 @@ export function derived_safe_equal(fn) { * @param {Derived} derived * @returns {void} */ -function destroy_derived_children(derived) { - var children = derived.children; - - if (children !== null) { - derived.children = null; - - for (var i = 0; i < children.length; i += 1) { - var child = children[i]; - if ((child.f & DERIVED) !== 0) { - destroy_derived(/** @type {Derived} */ (child)); - } else { - destroy_effect(/** @type {Effect} */ (child)); - } +export function destroy_derived_effects(derived) { + var effects = derived.effects; + + if (effects !== null) { + derived.effects = null; + + for (var i = 0; i < effects.length; i += 1) { + destroy_effect(/** @type {Effect} */ (effects[i])); } } } @@ -147,7 +141,7 @@ export function execute_derived(derived) { stack.push(derived); - destroy_derived_children(derived); + destroy_derived_effects(derived); value = update_reaction(derived); } finally { set_active_effect(prev_active_effect); @@ -156,7 +150,7 @@ export function execute_derived(derived) { } } else { try { - destroy_derived_children(derived); + destroy_derived_effects(derived); value = update_reaction(derived); } finally { set_active_effect(prev_active_effect); @@ -188,9 +182,9 @@ export function update_derived(derived) { * @returns {void} */ export function destroy_derived(derived) { - destroy_derived_children(derived); + destroy_derived_effects(derived); remove_reactions(derived, 0); set_signal_status(derived, DESTROYED); - derived.v = derived.children = derived.deps = derived.ctx = derived.reactions = null; + derived.v = derived.deps = derived.ctx = derived.reactions = null; } diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 8a7cffecd6..d014ff793d 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -53,7 +53,7 @@ export function validate_effect(rune) { e.effect_orphan(rune); } - if (active_reaction !== null && (active_reaction.f & UNOWNED) !== 0) { + if (active_reaction !== null && (active_reaction.f & UNOWNED) !== 0 && active_effect === null) { e.effect_in_unowned_derived(); } @@ -99,7 +99,6 @@ function create_effect(type, fn, sync, push = true) { var effect = { ctx: component_context, deps: null, - deriveds: null, nodes_start: null, nodes_end: null, f: type | DIRTY, @@ -153,7 +152,7 @@ function create_effect(type, fn, sync, push = true) { // if we're in a derived, add the effect there too if (active_reaction !== null && (active_reaction.f & DERIVED) !== 0) { var derived = /** @type {Derived} */ (active_reaction); - (derived.children ??= []).push(effect); + (derived.effects ??= []).push(effect); } } @@ -395,22 +394,6 @@ export function execute_effect_teardown(effect) { } } -/** - * @param {Effect} signal - * @returns {void} - */ -export function destroy_effect_deriveds(signal) { - var deriveds = signal.deriveds; - - if (deriveds !== null) { - signal.deriveds = null; - - for (var i = 0; i < deriveds.length; i += 1) { - destroy_derived(deriveds[i]); - } - } -} - /** * @param {Effect} signal * @param {boolean} remove_dom @@ -468,7 +451,6 @@ export function destroy_effect(effect, remove_dom = true) { } destroy_effect_children(effect, remove_dom && !removed); - destroy_effect_deriveds(effect); remove_reactions(effect, 0); set_signal_status(effect, DESTROYED); diff --git a/packages/svelte/src/internal/client/reactivity/types.d.ts b/packages/svelte/src/internal/client/reactivity/types.d.ts index 3a76a3ff83..5ef0097649 100644 --- a/packages/svelte/src/internal/client/reactivity/types.d.ts +++ b/packages/svelte/src/internal/client/reactivity/types.d.ts @@ -36,8 +36,8 @@ export interface Reaction extends Signal { export interface Derived<V = unknown> extends Value<V>, Reaction { /** The derived function */ fn: () => V; - /** Reactions created inside this signal */ - children: null | Reaction[]; + /** Effects created inside this signal */ + effects: null | Effect[]; /** Parent effect or derived */ parent: Effect | Derived | null; } @@ -51,8 +51,6 @@ export interface Effect extends Reaction { */ nodes_start: null | TemplateNode; nodes_end: null | TemplateNode; - /** Reactions created inside this signal */ - deriveds: null | Derived[]; /** The effect function */ fn: null | (() => void | (() => void)); /** The teardown function returned from the effect function */ diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 859925186f..2c53ac8f7b 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -4,7 +4,6 @@ import { define_property, get_descriptors, get_prototype_of, index_of } from '.. import { destroy_block_effect_children, destroy_effect_children, - destroy_effect_deriveds, execute_effect_teardown, unlink_effect } from './reactivity/effects.js'; @@ -28,7 +27,12 @@ import { } from './constants.js'; import { flush_tasks } from './dom/task.js'; import { internal_set, set } from './reactivity/sources.js'; -import { destroy_derived, execute_derived, update_derived } from './reactivity/deriveds.js'; +import { + destroy_derived, + destroy_derived_effects, + execute_derived, + update_derived +} from './reactivity/deriveds.js'; import * as e from './errors.js'; import { FILENAME } from '../../constants.js'; import { legacy_mode_flag, tracing_mode_flag } from '../flags/index.js'; @@ -409,7 +413,16 @@ export function update_reaction(reaction) { skipped_deps = 0; untracked_writes = null; active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; - skip_reaction = !is_flushing_effect && (flags & UNOWNED) !== 0; + // prettier-ignore + skip_reaction = + (flags & UNOWNED) !== 0 && + (!is_flushing_effect || + // If we were previously not in a reactive context and we're reading an unowned derived + // that was created inside another reaction, then we don't fully know the real owner and thus + // we need to skip adding any reactions for this unowned + ((previous_reaction === null || previous_untracking) && + /** @type {Derived} */ (reaction).parent !== null)); + derived_sources = null; set_component_context(reaction.ctx); untracking = false; @@ -517,6 +530,8 @@ function remove_reaction(signal, dependency) { if ((dependency.f & (UNOWNED | DISCONNECTED)) === 0) { dependency.f ^= DISCONNECTED; } + // Disconnect any reactions owned by this reaction + destroy_derived_effects(/** @type {Derived} **/ (dependency)); remove_reactions(/** @type {Derived} **/ (dependency), 0); } } @@ -564,7 +579,6 @@ export function update_effect(effect) { } else { destroy_effect_children(effect); } - destroy_effect_deriveds(effect); execute_effect_teardown(effect); var teardown = update_reaction(effect); @@ -934,30 +948,20 @@ export function get(signal) { new_deps.push(signal); } } - } - - if ( + } else if ( is_derived && /** @type {Derived} */ (signal).deps === null && - (active_reaction === null || untracking || (active_reaction.f & DERIVED) !== 0) + /** @type {Derived} */ (signal).effects === null ) { var derived = /** @type {Derived} */ (signal); var parent = derived.parent; if (parent !== null) { - // Attach the derived to the nearest parent effect or derived - if ((parent.f & DERIVED) !== 0) { - var parent_derived = /** @type {Derived} */ (parent); - - if (!parent_derived.children?.includes(derived)) { - (parent_derived.children ??= []).push(derived); - } - } else { - var parent_effect = /** @type {Effect} */ (parent); - - if (!parent_effect.deriveds?.includes(derived)) { - (parent_effect.deriveds ??= []).push(derived); - } + // If the derived is owned by another derived then mark it as unowned + // as the derived value might have been referenced in a different context + // since and thus its parent might not be its true owner anymore + if ((parent.f & UNOWNED) === 0) { + derived.f ^= UNOWNED; } } } diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index aa1dbf3132..2ce624a777 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -9,11 +9,12 @@ import { user_effect } from '../../src/internal/client/reactivity/effects'; import { state, set } from '../../src/internal/client/reactivity/sources'; -import type { Derived, Value } from '../../src/internal/client/types'; +import type { Derived, Effect, Value } from '../../src/internal/client/types'; import { proxy } from '../../src/internal/client/proxy'; import { derived } from '../../src/internal/client/reactivity/deriveds'; import { snapshot } from '../../src/internal/shared/clone.js'; import { SvelteSet } from '../../src/reactivity/set'; +import { DESTROYED } from '../../src/internal/client/constants'; /** * @param runes runes mode @@ -68,7 +69,7 @@ describe('signals', () => { }; }); - test('multiple effects with state and derived in it#1', () => { + test('multiple effects with state and derived in it #1', () => { const log: string[] = []; let count = state(0); @@ -89,7 +90,7 @@ describe('signals', () => { }; }); - test('multiple effects with state and derived in it#2', () => { + test('multiple effects with state and derived in it #2', () => { const log: string[] = []; let count = state(0); @@ -256,12 +257,16 @@ describe('signals', () => { const a = state(0); const b = state(0); - const c = derived(() => { - const a_2 = derived(() => $.get(a) + '!'); - const b_2 = derived(() => $.get(b) + '?'); - nested.push(a_2, b_2); + let c: any; + + const destroy = effect_root(() => { + c = derived(() => { + const a_2 = derived(() => $.get(a) + '!'); + const b_2 = derived(() => $.get(b) + '?'); + nested.push(a_2, b_2); - return { a: $.get(a_2), b: $.get(b_2) }; + return { a: $.get(a_2), b: $.get(b_2) }; + }); }); $.get(c); @@ -274,11 +279,10 @@ describe('signals', () => { $.get(c); - // Ensure we're not leaking dependencies - assert.deepEqual( - nested.slice(0, -2).map((s) => s.deps), - [null, null, null, null] - ); + destroy(); + + assert.equal(a.reactions, null); + assert.equal(b.reactions, null); }; }); @@ -477,6 +481,7 @@ describe('signals', () => { effect(() => { log.push('inner', $.get(inner)); }); + return $.get(outer); }); }); }); @@ -530,6 +535,103 @@ describe('signals', () => { }; }); + test('mixed nested deriveds correctly cleanup when no longer connected to graph #1', () => { + let a: Derived<unknown>; + let b: Derived<unknown>; + let s = state(0); + + const destroy = effect_root(() => { + render_effect(() => { + a = derived(() => { + b = derived(() => { + $.get(s); + }); + $.untrack(() => { + $.get(b); + }); + $.get(s); + }); + $.get(a); + }); + }); + + return () => { + flushSync(); + assert.equal(a?.deps?.length, 1); + assert.equal(s?.reactions?.length, 1); + destroy(); + assert.equal(s?.reactions, null); + }; + }); + + test('mixed nested deriveds correctly cleanup when no longer connected to graph #2', () => { + let a: Derived<unknown>; + let b: Derived<unknown>; + let s = state(0); + + const destroy = effect_root(() => { + render_effect(() => { + a = derived(() => { + b = derived(() => { + $.get(s); + }); + effect_root(() => { + $.get(b); + }); + $.get(s); + }); + $.get(a); + }); + }); + + return () => { + flushSync(); + assert.equal(a?.deps?.length, 1); + assert.equal(s?.reactions?.length, 1); + destroy(); + assert.equal(s?.reactions, null); + }; + }); + + test('mixed nested deriveds correctly cleanup when no longer connected to graph #3', () => { + let a: Derived<unknown>; + let b: Derived<unknown>; + let s = state(0); + let logs: any[] = []; + + const destroy = effect_root(() => { + render_effect(() => { + a = derived(() => { + b = derived(() => { + return $.get(s); + }); + effect_root(() => { + $.get(b); + }); + render_effect(() => { + logs.push($.get(b)); + }); + $.get(s); + }); + $.get(a); + }); + }); + + return () => { + flushSync(); + assert.equal(a?.deps?.length, 1); + assert.equal(s?.reactions?.length, 2); + + set(s, 1); + flushSync(); + + assert.deepEqual(logs, [0, 1]); + + destroy(); + assert.equal(s?.reactions, null); + }; + }); + test('deriveds update upon reconnection #1', () => { let a = state(false); let b = state(false); @@ -778,56 +880,44 @@ describe('signals', () => { }; }); - test('nested deriveds clean up the relationships when used with untrack', () => { + test('deriveds containing effects work correctly', () => { return () => { let a = render_effect(() => {}); + let b = state(0); + let c; + let effects: Effect[] = []; const destroy = effect_root(() => { a = render_effect(() => { - $.untrack(() => { - const b = derived(() => { - const c = derived(() => {}); - $.untrack(() => { - $.get(c); - }); - }); + c = derived(() => { + effects.push( + effect(() => { + $.get(b); + }) + ); $.get(b); }); + $.get(c); }); }); - assert.deepEqual(a.deriveds?.length, 1); - - destroy(); + assert.equal(c!.effects?.length, 1); + assert.equal(a.first, a.last); - assert.deepEqual(a.deriveds, null); - }; - }); - - test('nested deriveds do not connect inside parent deriveds if unused', () => { - return () => { - let a = render_effect(() => {}); - let b: Derived<void> | undefined; + set(b, 1); - const destroy = effect_root(() => { - a = render_effect(() => { - $.untrack(() => { - b = derived(() => { - derived(() => {}); - derived(() => {}); - derived(() => {}); - }); - $.get(b); - }); - }); - }); + flushSync(); - assert.deepEqual(a.deriveds?.length, 1); - assert.deepEqual(b?.children, null); + assert.equal(c!.effects?.length, 1); + assert.equal(a.first, a.last); destroy(); - assert.deepEqual(a.deriveds, null); + assert.equal(a.first, null); + + assert.equal(effects.length, 2); + assert.equal((effects[0].f & DESTROYED) !== 0, true); + assert.equal((effects[1].f & DESTROYED) !== 0, true); }; }); @@ -836,7 +926,7 @@ describe('signals', () => { let a = render_effect(() => {}); let b = state(0); let c; - let effects = []; + let effects: Effect[] = []; const destroy = effect_root(() => { a = render_effect(() => { @@ -854,20 +944,23 @@ describe('signals', () => { }); }); - assert.deepEqual(c!.children?.length, 1); - assert.deepEqual(a.first, a.last); + assert.equal(c!.effects?.length, 1); + assert.equal(a.first, a.last); set(b, 1); flushSync(); - assert.deepEqual(c!.children?.length, 1); - assert.deepEqual(a.first, a.last); + assert.equal(c!.effects?.length, 1); + assert.equal(a.first, a.last); destroy(); - assert.deepEqual(a.deriveds, null); - assert.deepEqual(a.first, null); + assert.equal(a.first, null); + + assert.equal(effects.length, 2); + assert.equal((effects[0].f & DESTROYED) !== 0, true); + assert.equal((effects[1].f & DESTROYED) !== 0, true); }; }); From c8bbb15693c59a3a5b3d7478d7adc1b0fa089ba7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 29 Jan 2025 13:18:43 +0000 Subject: [PATCH 0128/1429] Version Packages (#15139) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/blue-rocks-play.md | 5 ----- .changeset/curly-balloons-relate.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/blue-rocks-play.md delete mode 100644 .changeset/curly-balloons-relate.md diff --git a/.changeset/blue-rocks-play.md b/.changeset/blue-rocks-play.md deleted file mode 100644 index 31e9991148..0000000000 --- a/.changeset/blue-rocks-play.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: improve derived connection to ownership graph diff --git a/.changeset/curly-balloons-relate.md b/.changeset/curly-balloons-relate.md deleted file mode 100644 index 69d13b8692..0000000000 --- a/.changeset/curly-balloons-relate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly look for sibling elements inside blocks and components when pruning CSS diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 3b42ad6927..bb48391e0e 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.19.5 + +### Patch Changes + +- fix: improve derived connection to ownership graph ([#15137](https://github.com/sveltejs/svelte/pull/15137)) + +- fix: correctly look for sibling elements inside blocks and components when pruning CSS ([#15106](https://github.com/sveltejs/svelte/pull/15106)) + ## 5.19.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index f94992e42e..a44ba43364 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.19.4", + "version": "5.19.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b45197aa9c..a509ef3fec 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.19.4'; +export const VERSION = '5.19.5'; export const PUBLIC_VERSION = '5'; From 8e83127e1a84f173855dd14d46efb402ce3523dc Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 29 Jan 2025 09:22:35 -0500 Subject: [PATCH 0129/1429] chore: move more code (#15133) * move more code * lint --- packages/svelte/src/index-client.js | 35 +++++++++ .../svelte/src/internal/client/context.js | 5 ++ .../src/internal/client/dom/blocks/await.js | 3 +- .../src/internal/client/dom/blocks/key.js | 2 +- .../client/dom/elements/bindings/input.js | 3 +- packages/svelte/src/internal/client/index.js | 4 +- .../src/internal/client/reactivity/props.js | 11 +-- .../src/internal/client/reactivity/sources.js | 32 +++++++- .../svelte/src/internal/client/runtime.js | 73 +------------------ packages/svelte/tests/signals/test.ts | 10 +-- 10 files changed, 86 insertions(+), 92 deletions(-) diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index bd2b353395..ca29d5bfbe 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -8,6 +8,41 @@ import * as e from './internal/client/errors.js'; import { lifecycle_outside_component } from './internal/shared/errors.js'; import { legacy_mode_flag } from './internal/flags/index.js'; import { component_context } from './internal/client/context.js'; +import { DEV } from 'esm-env'; + +if (DEV) { + /** + * @param {string} rune + */ + function throw_rune_error(rune) { + if (!(rune in globalThis)) { + // TODO if people start adjusting the "this can contain runes" config through v-p-s more, adjust this message + /** @type {any} */ + let value; // let's hope noone modifies this global, but belts and braces + Object.defineProperty(globalThis, rune, { + configurable: true, + // eslint-disable-next-line getter-return + get: () => { + if (value !== undefined) { + return value; + } + + e.rune_outside_svelte(rune); + }, + set: (v) => { + value = v; + } + }); + } + } + + throw_rune_error('$state'); + throw_rune_error('$effect'); + throw_rune_error('$derived'); + throw_rune_error('$inspect'); + throw_rune_error('$props'); + throw_rune_error('$bindable'); +} /** * The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. diff --git a/packages/svelte/src/internal/client/context.js b/packages/svelte/src/internal/client/context.js index 1d7f0747cd..e1088edf30 100644 --- a/packages/svelte/src/internal/client/context.js +++ b/packages/svelte/src/internal/client/context.js @@ -185,6 +185,11 @@ export function pop(component) { return component || /** @type {T} */ ({}); } +/** @returns {boolean} */ +export function is_runes() { + return !legacy_mode_flag || (component_context !== null && component_context.l === null); +} + /** * @param {string} name * @returns {Map<unknown, unknown>} diff --git a/packages/svelte/src/internal/client/dom/blocks/await.js b/packages/svelte/src/internal/client/dom/blocks/await.js index 633a6e5bab..c8c7c1c0ea 100644 --- a/packages/svelte/src/internal/client/dom/blocks/await.js +++ b/packages/svelte/src/internal/client/dom/blocks/await.js @@ -3,12 +3,13 @@ import { DEV } from 'esm-env'; import { is_promise } from '../../../shared/utils.js'; import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js'; import { internal_set, mutable_source, source } from '../../reactivity/sources.js'; -import { flush_sync, is_runes, set_active_effect, set_active_reaction } from '../../runtime.js'; +import { flush_sync, set_active_effect, set_active_reaction } from '../../runtime.js'; import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; import { queue_micro_task } from '../task.js'; import { UNINITIALIZED } from '../../../../constants.js'; import { component_context, + is_runes, set_component_context, set_dev_current_component_function } from '../../context.js'; diff --git a/packages/svelte/src/internal/client/dom/blocks/key.js b/packages/svelte/src/internal/client/dom/blocks/key.js index 4a8b7b94fc..a697163548 100644 --- a/packages/svelte/src/internal/client/dom/blocks/key.js +++ b/packages/svelte/src/internal/client/dom/blocks/key.js @@ -2,7 +2,7 @@ import { UNINITIALIZED } from '../../../../constants.js'; import { block, branch, pause_effect } from '../../reactivity/effects.js'; import { not_equal, safe_not_equal } from '../../reactivity/equality.js'; -import { is_runes } from '../../runtime.js'; +import { is_runes } from '../../context.js'; import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; /** diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/input.js b/packages/svelte/src/internal/client/dom/elements/bindings/input.js index 3ea1a24d7e..f1992007ed 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/input.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/input.js @@ -5,7 +5,8 @@ import * as e from '../../../errors.js'; import { is } from '../../../proxy.js'; import { queue_micro_task } from '../../task.js'; import { hydrating } from '../../hydration.js'; -import { is_runes, untrack } from '../../../runtime.js'; +import { untrack } from '../../../runtime.js'; +import { is_runes } from '../../../context.js'; /** * @param {HTMLInputElement} input diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 877f3c59ee..3f3b29b029 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -110,7 +110,7 @@ export { user_effect, user_pre_effect } from './reactivity/effects.js'; -export { mutable_state, mutate, set, state } from './reactivity/sources.js'; +export { mutable_state, mutate, set, state, update, update_pre } from './reactivity/sources.js'; export { prop, rest_props, @@ -139,8 +139,6 @@ export { flush_sync, tick, untrack, - update, - update_pre, exclude_from_object, deep_read, deep_read_state diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index 3e5a0258c7..d157642d5d 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -8,16 +8,9 @@ import { PROPS_IS_UPDATED } from '../../../constants.js'; import { get_descriptor, is_function } from '../../shared/utils.js'; -import { mutable_source, set, source } from './sources.js'; +import { mutable_source, set, source, update } from './sources.js'; import { derived, derived_safe_equal } from './deriveds.js'; -import { - active_effect, - get, - captured_signals, - set_active_effect, - untrack, - update -} from '../runtime.js'; +import { active_effect, get, captured_signals, set_active_effect, untrack } from '../runtime.js'; import { safe_equals } from './equality.js'; import * as e from '../errors.js'; import { diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index b3e66a3336..ded0ca0584 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -5,7 +5,6 @@ import { active_effect, untracked_writes, get, - is_runes, schedule_effect, set_untracked_writes, set_signal_status, @@ -34,7 +33,7 @@ import { import * as e from '../errors.js'; import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js'; import { get_stack } from '../dev/tracing.js'; -import { component_context } from '../context.js'; +import { component_context, is_runes } from '../context.js'; export let inspect_effects = new Set(); @@ -226,6 +225,35 @@ export function internal_set(source, value) { return value; } +/** + * @template {number | bigint} T + * @param {Source<T>} source + * @param {1 | -1} [d] + * @returns {T} + */ +export function update(source, d = 1) { + var value = get(source); + var result = d === 1 ? value++ : value--; + + set(source, value); + + // @ts-expect-error + return result; +} + +/** + * @template {number | bigint} T + * @param {Source<T>} source + * @param {1 | -1} [d] + * @returns {T} + */ +export function update_pre(source, d = 1) { + var value = get(source); + + // @ts-expect-error + return set(source, d === 1 ? ++value : --value); +} + /** * @param {Value} signal * @param {number} status should be DIRTY or MAYBE_DIRTY diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 2c53ac8f7b..bf9d17fa23 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -26,7 +26,7 @@ import { BOUNDARY_EFFECT } from './constants.js'; import { flush_tasks } from './dom/task.js'; -import { internal_set, set } from './reactivity/sources.js'; +import { internal_set } from './reactivity/sources.js'; import { destroy_derived, destroy_derived_effects, @@ -35,11 +35,12 @@ import { } from './reactivity/deriveds.js'; import * as e from './errors.js'; import { FILENAME } from '../../constants.js'; -import { legacy_mode_flag, tracing_mode_flag } from '../flags/index.js'; +import { tracing_mode_flag } from '../flags/index.js'; import { tracing_expressions, get_stack } from './dev/tracing.js'; import { component_context, dev_current_component_function, + is_runes, set_component_context, set_dev_current_component_function } from './context.js'; @@ -161,11 +162,6 @@ export function increment_write_version() { return ++write_version; } -/** @returns {boolean} */ -export function is_runes() { - return !legacy_mode_flag || (component_context !== null && component_context.l === null); -} - /** * Determines whether a derived or effect is dirty. * If it is MAYBE_DIRTY, will set the status to CLEAN @@ -1095,35 +1091,6 @@ export function set_signal_status(signal, status) { signal.f = (signal.f & STATUS_MASK) | status; } -/** - * @template {number | bigint} T - * @param {Value<T>} signal - * @param {1 | -1} [d] - * @returns {T} - */ -export function update(signal, d = 1) { - var value = get(signal); - var result = d === 1 ? value++ : value--; - - set(signal, value); - - // @ts-expect-error - return result; -} - -/** - * @template {number | bigint} T - * @param {Value<T>} signal - * @param {1 | -1} [d] - * @returns {T} - */ -export function update_pre(signal, d = 1) { - var value = get(signal); - - // @ts-expect-error - return set(signal, d === 1 ? ++value : --value); -} - /** * @param {Record<string, unknown>} obj * @param {string[]} keys @@ -1215,37 +1182,3 @@ export function deep_read(value, visited = new Set()) { } } } - -if (DEV) { - /** - * @param {string} rune - */ - function throw_rune_error(rune) { - if (!(rune in globalThis)) { - // TODO if people start adjusting the "this can contain runes" config through v-p-s more, adjust this message - /** @type {any} */ - let value; // let's hope noone modifies this global, but belts and braces - Object.defineProperty(globalThis, rune, { - configurable: true, - // eslint-disable-next-line getter-return - get: () => { - if (value !== undefined) { - return value; - } - - e.rune_outside_svelte(rune); - }, - set: (v) => { - value = v; - } - }); - } - } - - throw_rune_error('$state'); - throw_rune_error('$effect'); - throw_rune_error('$derived'); - throw_rune_error('$inspect'); - throw_rune_error('$props'); - throw_rune_error('$bindable'); -} diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index 2ce624a777..bd9bc50ae3 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -8,7 +8,7 @@ import { render_effect, user_effect } from '../../src/internal/client/reactivity/effects'; -import { state, set } from '../../src/internal/client/reactivity/sources'; +import { state, set, update, update_pre } from '../../src/internal/client/reactivity/sources'; import type { Derived, Effect, Value } from '../../src/internal/client/types'; import { proxy } from '../../src/internal/client/proxy'; import { derived } from '../../src/internal/client/reactivity/deriveds'; @@ -968,14 +968,14 @@ describe('signals', () => { return () => { const count = state(0n); - assert.doesNotThrow(() => $.update(count)); + assert.doesNotThrow(() => update(count)); assert.equal($.get(count), 1n); - assert.doesNotThrow(() => $.update(count, -1)); + assert.doesNotThrow(() => update(count, -1)); assert.equal($.get(count), 0n); - assert.doesNotThrow(() => $.update_pre(count)); + assert.doesNotThrow(() => update_pre(count)); assert.equal($.get(count), 1n); - assert.doesNotThrow(() => $.update_pre(count, -1)); + assert.doesNotThrow(() => update_pre(count, -1)); assert.equal($.get(count), 0n); }; }); From 522557559f22b47cbe2b1c782e5a363b72ea146a Mon Sep 17 00:00:00 2001 From: 7nik <kfiiranet@gmail.com> Date: Thu, 30 Jan 2025 16:51:25 +0200 Subject: [PATCH 0130/1429] fix: do not prune selectors like `:global(.foo):has(.scoped)` (#15140) Fixes #14910 The issue occurs only when :has() targets at a component's root element and because include_self is false. I came to the conclusion that this is the same case as :root:has(.scoped). --- .changeset/famous-bulldogs-tan.md | 5 +++++ .../src/compiler/phases/2-analyze/css/css-prune.js | 9 +++++++-- packages/svelte/tests/css/samples/has/_config.js | 14 ++++++++++++++ packages/svelte/tests/css/samples/has/expected.css | 7 +++++++ packages/svelte/tests/css/samples/has/input.svelte | 7 +++++++ 5 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 .changeset/famous-bulldogs-tan.md diff --git a/.changeset/famous-bulldogs-tan.md b/.changeset/famous-bulldogs-tan.md new file mode 100644 index 0000000000..a5cc14b7f2 --- /dev/null +++ b/.changeset/famous-bulldogs-tan.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: do not prune selectors like `:global(.foo):has(.scoped)` diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index 109010e88c..e719895798 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -339,13 +339,18 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element) let sibling_elements; // do them lazy because it's rarely used and expensive to calculate // If this is a :has inside a global selector, we gotta include the element itself, too, - // because the global selector might be for an element that's outside the component (e.g. :root). + // because the global selector might be for an element that's outside the component, + // e.g. :root:has(.scoped), :global(.foo):has(.scoped), or :root { &:has(.scoped) {} } const rules = get_parent_rules(rule); const include_self = rules.some((r) => r.prelude.children.some((c) => c.children.some((s) => is_global(s, r)))) || rules[rules.length - 1].prelude.children.some((c) => c.children.some((r) => - r.selectors.some((s) => s.type === 'PseudoClassSelector' && s.name === 'root') + r.selectors.some( + (s) => + s.type === 'PseudoClassSelector' && + (s.name === 'root' || (s.name === 'global' && s.args)) + ) ) ); if (include_self) { diff --git a/packages/svelte/tests/css/samples/has/_config.js b/packages/svelte/tests/css/samples/has/_config.js index 33bbe74949..8d89d98cbd 100644 --- a/packages/svelte/tests/css/samples/has/_config.js +++ b/packages/svelte/tests/css/samples/has/_config.js @@ -197,6 +197,20 @@ export default test({ column: 16, character: 1614 } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector ":global(.foo):has(.unused)"', + start: { + line: 155, + column: 1, + character: 1684 + }, + end: { + line: 155, + column: 27, + character: 1710 + } } ] }); diff --git a/packages/svelte/tests/css/samples/has/expected.css b/packages/svelte/tests/css/samples/has/expected.css index 9627bf730c..b257370d61 100644 --- a/packages/svelte/tests/css/samples/has/expected.css +++ b/packages/svelte/tests/css/samples/has/expected.css @@ -136,3 +136,10 @@ color: red; }*/ } + + .foo:has(x.svelte-xyz) { + color: green; + } + /* (unused) :global(.foo):has(.unused) { + color: red; + }*/ diff --git a/packages/svelte/tests/css/samples/has/input.svelte b/packages/svelte/tests/css/samples/has/input.svelte index 946cf2df90..9b254996bf 100644 --- a/packages/svelte/tests/css/samples/has/input.svelte +++ b/packages/svelte/tests/css/samples/has/input.svelte @@ -148,4 +148,11 @@ color: red; } } + + :global(.foo):has(x) { + color: green; + } + :global(.foo):has(.unused) { + color: red; + } </style> From f5406c952ed446c131eeef3da152c345bd952568 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 30 Jan 2025 19:14:03 +0100 Subject: [PATCH 0131/1429] fix: widen ownership when calling setContext (#15153) Instead of doing ownership addition at each `getContext` call site, we do it once as the `setContext` call site and make the state basically global. - avoids potential false positives in edge cases - makes the whole thing more performant, especially around things like calling `getContext` inside a list item component - false negatives are unlikely from this fixes #15072 --- .changeset/unlucky-gorillas-hunt.md | 5 ++++ .../svelte/src/internal/client/context.js | 30 +++++++------------ .../src/internal/client/dev/ownership.js | 12 +++++--- 3 files changed, 24 insertions(+), 23 deletions(-) create mode 100644 .changeset/unlucky-gorillas-hunt.md diff --git a/.changeset/unlucky-gorillas-hunt.md b/.changeset/unlucky-gorillas-hunt.md new file mode 100644 index 0000000000..055fc120a6 --- /dev/null +++ b/.changeset/unlucky-gorillas-hunt.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: widen ownership when calling setContext diff --git a/packages/svelte/src/internal/client/context.js b/packages/svelte/src/internal/client/context.js index e1088edf30..bd94d5ad8a 100644 --- a/packages/svelte/src/internal/client/context.js +++ b/packages/svelte/src/internal/client/context.js @@ -8,7 +8,8 @@ import { active_effect, active_reaction, set_active_effect, - set_active_reaction + set_active_reaction, + untrack } from './runtime.js'; import { effect } from './reactivity/effects.js'; import { legacy_mode_flag } from '../flags/index.js'; @@ -49,14 +50,6 @@ export function set_dev_current_component_function(fn) { export function getContext(key) { const context_map = get_or_init_context_map('getContext'); const result = /** @type {T} */ (context_map.get(key)); - - if (DEV) { - const fn = /** @type {ComponentContext} */ (component_context).function; - if (fn) { - add_owner(result, fn, true); - } - } - return result; } @@ -74,6 +67,15 @@ export function getContext(key) { */ export function setContext(key, context) { const context_map = get_or_init_context_map('setContext'); + + if (DEV) { + // When state is put into context, we treat as if it's global from now on. + // We do for performance reasons (it's for example very expensive to call + // getContext on a big object many times when part of a list component) + // and danger of false positives. + untrack(() => add_owner(context, null, true)); + } + context_map.set(key, context); return context; } @@ -100,16 +102,6 @@ export function hasContext(key) { */ export function getAllContexts() { const context_map = get_or_init_context_map('getAllContexts'); - - if (DEV) { - const fn = component_context?.function; - if (fn) { - for (const value of context_map.values()) { - add_owner(value, fn, true); - } - } - } - return /** @type {T} */ (context_map); } diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index 70cfbb47f3..a9506cfdc0 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -109,7 +109,7 @@ export function mark_module_end(component) { /** * @param {any} object - * @param {any} owner + * @param {any | null} owner * @param {boolean} [global] * @param {boolean} [skip_warning] */ @@ -120,7 +120,7 @@ export function add_owner(object, owner, global = false, skip_warning = false) { if (metadata && !has_owner(metadata, component)) { let original = get_owner(metadata); - if (owner[FILENAME] !== component[FILENAME] && !skip_warning) { + if (owner && owner[FILENAME] !== component[FILENAME] && !skip_warning) { w.ownership_invalid_binding(component[FILENAME], owner[FILENAME], original[FILENAME]); } } @@ -165,7 +165,7 @@ export function widen_ownership(from, to) { /** * @param {any} object - * @param {Function} owner + * @param {Function | null} owner If `null`, then the object is globally owned and will not be checked * @param {Set<any>} seen */ function add_owner_to_object(object, owner, seen) { @@ -174,7 +174,11 @@ function add_owner_to_object(object, owner, seen) { if (metadata) { // this is a state proxy, add owner directly, if not globally shared if ('owners' in metadata && metadata.owners != null) { - metadata.owners.add(owner); + if (owner) { + metadata.owners.add(owner); + } else { + metadata.owners = null; + } } } else if (object && typeof object === 'object') { if (seen.has(object)) return; From b8607f876577614b1a5f798165343526bbb74b0b Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 30 Jan 2025 19:14:22 +0100 Subject: [PATCH 0132/1429] fix: silence a11y attribute warnings when spread attributes present (#15150) Fixes #15067 --- .changeset/odd-rules-hear.md | 5 +++ .../phases/2-analyze/visitors/shared/a11y.js | 27 +++++++++------ .../input.svelte | 1 + .../input.svelte | 18 +++++----- .../warnings.json | 34 +++---------------- 5 files changed, 36 insertions(+), 49 deletions(-) create mode 100644 .changeset/odd-rules-hear.md diff --git a/.changeset/odd-rules-hear.md b/.changeset/odd-rules-hear.md new file mode 100644 index 0000000000..325b8ddf96 --- /dev/null +++ b/.changeset/odd-rules-hear.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: silence a11y attribute warnings when spread attributes present diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js index a5ca8463a4..24a8e5122d 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js @@ -756,7 +756,8 @@ export function check_element(node, context) { name === 'aria-activedescendant' && !is_dynamic_element && !is_interactive_element(node.name, attribute_map) && - !attribute_map.has('tabindex') + !attribute_map.has('tabindex') && + !has_spread ) { w.a11y_aria_activedescendant_has_tabindex(attribute); } @@ -810,9 +811,9 @@ export function check_element(node, context) { const role = roles_map.get(current_role); if (role) { const required_role_props = Object.keys(role.requiredProps); - const has_missing_props = required_role_props.some( - (prop) => !attributes.find((a) => a.name === prop) - ); + const has_missing_props = + !has_spread && + required_role_props.some((prop) => !attributes.find((a) => a.name === prop)); if (has_missing_props) { w.a11y_role_has_required_aria_props( attribute, @@ -828,6 +829,7 @@ export function check_element(node, context) { // interactive-supports-focus if ( + !has_spread && !has_disabled_attribute(attribute_map) && !is_hidden_from_screen_reader(node.name, attribute_map) && !is_presentation_role(current_role) && @@ -845,6 +847,7 @@ export function check_element(node, context) { // no-interactive-element-to-noninteractive-role if ( + !has_spread && is_interactive_element(node.name, attribute_map) && (is_non_interactive_roles(current_role) || is_presentation_role(current_role)) ) { @@ -853,6 +856,7 @@ export function check_element(node, context) { // no-noninteractive-element-to-interactive-role if ( + !has_spread && is_non_interactive_element(node.name, attribute_map) && is_interactive_roles(current_role) && !a11y_non_interactive_element_to_interactive_role_exceptions[node.name]?.includes( @@ -947,6 +951,7 @@ export function check_element(node, context) { // no-noninteractive-element-interactions if ( + !has_spread && !has_contenteditable_attr && !is_hidden_from_screen_reader(node.name, attribute_map) && !is_presentation_role(role_static_value) && @@ -964,6 +969,7 @@ export function check_element(node, context) { // no-static-element-interactions if ( + !has_spread && (!role || role_static_value !== null) && !is_hidden_from_screen_reader(node.name, attribute_map) && !is_presentation_role(role_static_value) && @@ -981,11 +987,11 @@ export function check_element(node, context) { } } - if (handlers.has('mouseover') && !handlers.has('focus')) { + if (!has_spread && handlers.has('mouseover') && !handlers.has('focus')) { w.a11y_mouse_events_have_key_events(node, 'mouseover', 'focus'); } - if (handlers.has('mouseout') && !handlers.has('blur')) { + if (!has_spread && handlers.has('mouseout') && !handlers.has('blur')) { w.a11y_mouse_events_have_key_events(node, 'mouseout', 'blur'); } @@ -995,7 +1001,7 @@ export function check_element(node, context) { if (node.name === 'a' || node.name === 'button') { const is_hidden = get_static_value(attribute_map.get('aria-hidden')) === 'true'; - if (!is_hidden && !is_labelled && !has_content(node)) { + if (!has_spread && !is_hidden && !is_labelled && !has_content(node)) { w.a11y_consider_explicit_label(node); } } @@ -1054,7 +1060,7 @@ export function check_element(node, context) { if (node.name === 'img') { const alt_attribute = get_static_text_value(attribute_map.get('alt')); const aria_hidden = get_static_value(attribute_map.get('aria-hidden')); - if (alt_attribute && !aria_hidden) { + if (alt_attribute && !aria_hidden && !has_spread) { if (/\b(image|picture|photo)\b/i.test(alt_attribute)) { w.a11y_img_redundant_alt(node); } @@ -1087,7 +1093,7 @@ export function check_element(node, context) { ); return has; }; - if (!attribute_map.has('for') && !has_input_child(node)) { + if (!has_spread && !attribute_map.has('for') && !has_input_child(node)) { w.a11y_label_has_associated_control(node); } } @@ -1095,7 +1101,7 @@ export function check_element(node, context) { if (node.name === 'video') { const aria_hidden_attribute = attribute_map.get('aria-hidden'); const aria_hidden_exist = aria_hidden_attribute && get_static_value(aria_hidden_attribute); - if (attribute_map.has('muted') || aria_hidden_exist === 'true') { + if (attribute_map.has('muted') || aria_hidden_exist === 'true' || has_spread) { return; } let has_caption = false; @@ -1141,6 +1147,7 @@ export function check_element(node, context) { // Check content if ( + !has_spread && !is_labelled && !has_contenteditable_binding && a11y_required_content.includes(node.name) && diff --git a/packages/svelte/tests/validator/samples/a11y-label-has-associated-control/input.svelte b/packages/svelte/tests/validator/samples/a11y-label-has-associated-control/input.svelte index 124888c089..f47743b33b 100644 --- a/packages/svelte/tests/validator/samples/a11y-label-has-associated-control/input.svelte +++ b/packages/svelte/tests/validator/samples/a11y-label-has-associated-control/input.svelte @@ -10,3 +10,4 @@ <label>E <span></span></label> <label>F {#if true}<input type="text" />{/if}</label> <LabelComponent>G <input type="text" /></LabelComponent> +<label {...forMightBeInHere}>E <span></span></label> \ No newline at end of file diff --git a/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/input.svelte b/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/input.svelte index 613b80e6d9..f9fe4f15c1 100644 --- a/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/input.svelte +++ b/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/input.svelte @@ -1,21 +1,19 @@ <script> - // Even if otherProps contains onBlur and/or onFocus, the rule will still fail. - // Props should be passed down explicitly for rule to pass. const otherProps = { - onBlur: () => void 0, - onFocus: () => void 0 + onblur: () => {}, + onfocus: () => {} }; </script> <!-- svelte-ignore a11y_no_static_element_interactions --> -<div on:mouseover={() => void 0}></div> +<div onmouseover={() => {}}></div> <!-- svelte-ignore a11y_no_static_element_interactions --> -<div on:mouseover={() => void 0} on:focus={() => void 0}></div> +<div onmouseover={() => {}} onfocus={() => {}}></div> <!-- svelte-ignore a11y_no_static_element_interactions --> -<div on:mouseover={() => void 0} {...otherProps}></div> +<div onmouseover={() => {}} {...otherProps}></div> <!-- svelte-ignore a11y_no_static_element_interactions --> -<div on:mouseout={() => void 0}></div> +<div onmouseout={() => {}}></div> <!-- svelte-ignore a11y_no_static_element_interactions --> -<div on:mouseout={() => void 0} on:blur={() => void 0}></div> +<div onmouseout={() => {}} onblur={() => {}}></div> <!-- svelte-ignore a11y_no_static_element_interactions --> -<div on:mouseout={() => void 0} {...otherProps}></div> +<div onmouseout={() => {}} {...otherProps}></div> diff --git a/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/warnings.json b/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/warnings.json index 574b019e0f..3dee4e9673 100644 --- a/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/warnings.json +++ b/packages/svelte/tests/validator/samples/a11y-mouse-events-have-key-events/warnings.json @@ -2,49 +2,25 @@ { "code": "a11y_mouse_events_have_key_events", "end": { - "column": 39, - "line": 11 + "column": 34, + "line": 9 }, "message": "'mouseover' event must be accompanied by 'focus' event", "start": { "column": 0, - "line": 11 + "line": 9 } }, { "code": "a11y_mouse_events_have_key_events", "end": { - "column": 55, + "column": 33, "line": 15 }, - "message": "'mouseover' event must be accompanied by 'focus' event", - "start": { - "column": 0, - "line": 15 - } - }, - { - "code": "a11y_mouse_events_have_key_events", - "end": { - "column": 38, - "line": 17 - }, "message": "'mouseout' event must be accompanied by 'blur' event", "start": { "column": 0, - "line": 17 - } - }, - { - "code": "a11y_mouse_events_have_key_events", - "end": { - "column": 54, - "line": 21 - }, - "message": "'mouseout' event must be accompanied by 'blur' event", - "start": { - "column": 0, - "line": 21 + "line": 15 } } ] From 83f00ebbd6e3400bf059690d132b31afa177c85a Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 30 Jan 2025 19:14:46 +0100 Subject: [PATCH 0133/1429] fix: don't error on slot prop inside block inside other component (#15148) `slot` is treated as a regular prop if it is not used directly inside another component, but we were running validations on such regular props that should only be run on real slots. Fixes #15125 --- .changeset/good-rocks-talk.md | 5 +++ .../2-analyze/visitors/shared/attribute.js | 44 ++++++++++--------- .../slot-attribute-component/input.svelte | 6 +++ 3 files changed, 34 insertions(+), 21 deletions(-) create mode 100644 .changeset/good-rocks-talk.md diff --git a/.changeset/good-rocks-talk.md b/.changeset/good-rocks-talk.md new file mode 100644 index 0000000000..59af86c686 --- /dev/null +++ b/.changeset/good-rocks-talk.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't error on slot prop inside block inside other component diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/attribute.js index 198e464ac7..19bd7b6e54 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/attribute.js @@ -80,40 +80,42 @@ export function validate_slot_attribute(context, attribute, is_component = false } if (owner) { - if (!is_text_attribute(attribute)) { - e.slot_attribute_invalid(attribute); - } - if ( owner.type === 'Component' || owner.type === 'SvelteComponent' || owner.type === 'SvelteSelf' ) { if (owner !== parent) { - e.slot_attribute_invalid_placement(attribute); - } + if (!is_component) { + e.slot_attribute_invalid_placement(attribute); + } + } else { + if (!is_text_attribute(attribute)) { + e.slot_attribute_invalid(attribute); + } - const name = attribute.value[0].data; + const name = attribute.value[0].data; - if (context.state.component_slots.has(name)) { - e.slot_attribute_duplicate(attribute, name, owner.name); - } - - context.state.component_slots.add(name); + if (context.state.component_slots.has(name)) { + e.slot_attribute_duplicate(attribute, name, owner.name); + } - if (name === 'default') { - for (const node of owner.fragment.nodes) { - if (node.type === 'Text' && regex_only_whitespaces.test(node.data)) { - continue; - } + context.state.component_slots.add(name); - if (node.type === 'RegularElement' || node.type === 'SvelteFragment') { - if (node.attributes.some((a) => a.type === 'Attribute' && a.name === 'slot')) { + if (name === 'default') { + for (const node of owner.fragment.nodes) { + if (node.type === 'Text' && regex_only_whitespaces.test(node.data)) { continue; } - } - e.slot_default_duplicate(node); + if (node.type === 'RegularElement' || node.type === 'SvelteFragment') { + if (node.attributes.some((a) => a.type === 'Attribute' && a.name === 'slot')) { + continue; + } + } + + e.slot_default_duplicate(node); + } } } } diff --git a/packages/svelte/tests/validator/samples/slot-attribute-component/input.svelte b/packages/svelte/tests/validator/samples/slot-attribute-component/input.svelte index 5acb14e409..5d559e614e 100644 --- a/packages/svelte/tests/validator/samples/slot-attribute-component/input.svelte +++ b/packages/svelte/tests/validator/samples/slot-attribute-component/input.svelte @@ -1,2 +1,8 @@ <Foo slot="foo">valid</Foo> <Foo slot={foo}>valid</Foo> +<Foo> + {#if true} + <Foo slot="foo">valid</Foo> + <Foo slot={foo}>valid</Foo> + {/if} +</Foo> \ No newline at end of file From 970aa7cfaa70707da1b87c3ca0af264c7589db9d Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 30 Jan 2025 19:15:04 +0100 Subject: [PATCH 0134/1429] fix: prevent false-positive ownership validations due to hot reload (#15154) The component identity could change due to HMR, so we fall back to checking the filenames as well fixes #14746 --- .changeset/ten-cougars-look.md | 5 +++++ packages/svelte/src/internal/client/dev/ownership.js | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 .changeset/ten-cougars-look.md diff --git a/.changeset/ten-cougars-look.md b/.changeset/ten-cougars-look.md new file mode 100644 index 0000000000..fe20d057dd --- /dev/null +++ b/.changeset/ten-cougars-look.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent false-positive ownership validations due to hot reload diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index a9506cfdc0..2a2527803a 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -220,6 +220,10 @@ function has_owner(metadata, component) { return ( metadata.owners.has(component) || + // This helps avoid false positives when using HMR, where the component function is replaced + [...metadata.owners].some( + (owner) => /** @type {any} */ (owner)[FILENAME] === /** @type {any} */ (component)?.[FILENAME] + ) || (metadata.parent !== null && has_owner(metadata.parent, component)) ); } From 7bef5963bd3c5eb05f7722e09b4e6b806c2da337 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Thu, 30 Jan 2025 18:17:27 +0000 Subject: [PATCH 0135/1429] fix: ensure reactions are correctly attached for unowned deriveds (#15158) * fix: ensure reactions are correctly attached for unowned deriveds * tune --- .changeset/loud-cars-scream.md | 5 +++++ packages/svelte/src/internal/client/runtime.js | 13 +++++++++---- .../samples/derived-unowned-11/Child.svelte | 5 +++++ .../samples/derived-unowned-11/Child2.svelte | 5 +++++ .../samples/derived-unowned-11/_config.js | 16 ++++++++++++++++ .../samples/derived-unowned-11/main.svelte | 11 +++++++++++ 6 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 .changeset/loud-cars-scream.md create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-unowned-11/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-unowned-11/Child2.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-unowned-11/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-unowned-11/main.svelte diff --git a/.changeset/loud-cars-scream.md b/.changeset/loud-cars-scream.md new file mode 100644 index 0000000000..2b61bc4453 --- /dev/null +++ b/.changeset/loud-cars-scream.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure reactions are correctly attached for unowned deriveds diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index bf9d17fa23..a572e27bf4 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -802,12 +802,19 @@ function process_effects(effect, collected_effects) { if (is_branch) { current_effect.f ^= CLEAN; } else { + // Ensure we set the effect to be the active reaction + // to ensure that unowned deriveds are correctly tracked + // because we're flushing the current effect + var previous_active_reaction = active_reaction; try { + active_reaction = current_effect; if (check_dirtiness(current_effect)) { update_effect(current_effect); } } catch (error) { handle_error(error, current_effect, null, current_effect.ctx); + } finally { + active_reaction = previous_active_reaction; } } @@ -952,13 +959,11 @@ export function get(signal) { var derived = /** @type {Derived} */ (signal); var parent = derived.parent; - if (parent !== null) { + if (parent !== null && (parent.f & UNOWNED) === 0) { // If the derived is owned by another derived then mark it as unowned // as the derived value might have been referenced in a different context // since and thus its parent might not be its true owner anymore - if ((parent.f & UNOWNED) === 0) { - derived.f ^= UNOWNED; - } + derived.f ^= UNOWNED; } } diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/Child.svelte b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/Child.svelte new file mode 100644 index 0000000000..cd215304a3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/Child.svelte @@ -0,0 +1,5 @@ +<script> + let { value = $bindable() } = $props() +</script> + +<button onclick={() => value = 'a'}>change</button> diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/Child2.svelte b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/Child2.svelte new file mode 100644 index 0000000000..a1d9f93bec --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/Child2.svelte @@ -0,0 +1,5 @@ +<script> + let {disabled = false} = $props() +</script> + +{disabled} diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/_config.js new file mode 100644 index 0000000000..9948f91966 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/_config.js @@ -0,0 +1,16 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + let [btn1, btn2] = target.querySelectorAll('button'); + + btn1?.click(); + flushSync(); + + btn2?.click(); + flushSync(); + + assert.htmlEqual(target.innerHTML, `<button>change</button><button>change</button>\nfalse`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/main.svelte b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/main.svelte new file mode 100644 index 0000000000..0219acdf7f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-11/main.svelte @@ -0,0 +1,11 @@ +<script> + import Child2 from './Child2.svelte' + import Child from './Child.svelte' + let loginname = $state('') + let password = $state('') +</script> + +<Child bind:value={loginname} /> +<Child bind:value={password} /> +<Child2 disabled={!loginname || !password} /> + From 6df59055e7f67328b3b3c3495120a14db4d8544b Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 30 Jan 2025 19:46:53 +0100 Subject: [PATCH 0136/1429] docs: more examples on what is outside the rendering process (#15157) closes #15151 --- documentation/docs/05-special-elements/01-svelte-boundary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/05-special-elements/01-svelte-boundary.md b/documentation/docs/05-special-elements/01-svelte-boundary.md index 15f249a771..f5439b4b83 100644 --- a/documentation/docs/05-special-elements/01-svelte-boundary.md +++ b/documentation/docs/05-special-elements/01-svelte-boundary.md @@ -13,7 +13,7 @@ Boundaries allow you to guard against errors in part of your app from breaking t If an error occurs while rendering or updating the children of a `<svelte:boundary>`, or running any [`$effect`]($effect) functions contained therein, the contents will be removed. -Errors occurring outside the rendering process (for example, in event handlers) are _not_ caught by error boundaries. +Errors occurring outside the rendering process (for example, in event handlers or after a `setTimeout` or async work) are _not_ caught by error boundaries. ## Properties From 674f81b5ceaa1968263b74547b7b2887f74f02fd Mon Sep 17 00:00:00 2001 From: Scott Wu <sw@scottwu.ca> Date: Fri, 31 Jan 2025 02:48:35 +0800 Subject: [PATCH 0137/1429] [docs] clarify that `$effect` analyzes functions deeply (#15144) * Update 04-$effect.md * Update documentation/docs/02-runes/04-$effect.md --------- Co-authored-by: Rich Harris <hello@rich-harris.dev> --- documentation/docs/02-runes/04-$effect.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md index 1ea960de70..da24084d4d 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -66,7 +66,7 @@ You can return a function from `$effect`, which will run immediately before the ### Understanding dependencies -`$effect` automatically picks up any reactive values (`$state`, `$derived`, `$props`) that are _synchronously_ read inside its function body and registers them as dependencies. When those dependencies change, the `$effect` schedules a rerun. +`$effect` automatically picks up any reactive values (`$state`, `$derived`, `$props`) that are _synchronously_ read inside its function body (including indirectly, via function calls) and registers them as dependencies. When those dependencies change, the `$effect` schedules a rerun. Values that are read _asynchronously_ — after an `await` or inside a `setTimeout`, for example — will not be tracked. Here, the canvas will be repainted when `color` changes, but not when `size` changes ([demo](/playground/untitled#H4sIAAAAAAAAE31T246bMBD9lZF3pWSlBEirfaEQqdo_2PatVIpjBrDkGGQPJGnEv1e2IZfVal-wfHzmzJyZ4cIqqdCy9M-F0blDlnqArZjmB3f72XWRHVCRw_bc4me4aDWhJstSlllhZEfbQhekkMDKfwg5PFvihMvX5OXH_CJa1Zrb0-Kpqr5jkiwC48rieuDWQbqgZ6wqFLRcvkC-hYvnkWi1dWqa8ESQTxFRjfQWsOXiWzmr0sSLhEJu3p1YsoJkNUcdZUnN9dagrBu6FVRQHAM10sJRKgUG16bXcGxQ44AGdt7SDkTDdY02iqLHnJVU6hedlWuIp94JW6Tf8oBt_8GdTxlF0b4n0C35ZLBzXb3mmYn3ae6cOW74zj0YVzDNYXRHFt9mprNgHfZSl6mzml8CMoLvTV6wTZIUDEJv5us2iwMtiJRyAKG4tXnhl8O0yhbML0Wm-B7VNlSSSd31BG7z8oIZZ6dgIffAVY_5xdU9Qrz1Bnx8fCfwtZ7v8Qc9j3nB8PqgmMWlHIID6-bkVaPZwDySfWtKNGtquxQ23Qlsq2QJT0KIqb8dL0up6xQ2eIBkAg_c1FI_YqW0neLnFCqFpwmreedJYT7XX8FVOBfwWRhXstZrSXiwKQjUhOZeMIleb5JZfHWn2Yq5pWEpmR7Hv-N_wEqT8hEEAAA=)): From 2be3823e3aaaad753d65d21a160e342bd9b1c514 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Thu, 30 Jan 2025 13:49:01 -0500 Subject: [PATCH 0138/1429] chore: remove inert check from each block reconciliation (#15143) --- .../src/internal/client/dom/blocks/each.js | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 040e585215..3baa03a917 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -219,17 +219,7 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f } if (!hydrating) { - var effect = /** @type {Effect} */ (active_reaction); - reconcile( - array, - state, - anchor, - render_fn, - flags, - (effect.f & INERT) !== 0, - get_key, - get_collection - ); + reconcile(array, state, anchor, render_fn, flags, get_key, get_collection); } if (fallback_fn !== null) { @@ -273,12 +263,11 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f * @param {Element | Comment | Text} anchor * @param {(anchor: Node, item: MaybeSource<V>, index: number | Source<number>, collection: () => V[]) => void} render_fn * @param {number} flags - * @param {boolean} is_inert * @param {(value: V, index: number) => any} get_key * @param {() => V[]} get_collection * @returns {void} */ -function reconcile(array, state, anchor, render_fn, flags, is_inert, get_key, get_collection) { +function reconcile(array, state, anchor, render_fn, flags, get_key, get_collection) { var is_animated = (flags & EACH_IS_ANIMATED) !== 0; var should_update = (flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0; @@ -420,7 +409,7 @@ function reconcile(array, state, anchor, render_fn, flags, is_inert, get_key, ge while (current !== null && current.k !== key) { // If the each block isn't inert and an item has an effect that is already inert, // skip over adding it to our seen Set as the item is already being handled - if (is_inert || (current.e.f & INERT) === 0) { + if ((current.e.f & INERT) === 0) { (seen ??= new Set()).add(current); } stashed.push(current); @@ -444,7 +433,7 @@ function reconcile(array, state, anchor, render_fn, flags, is_inert, get_key, ge while (current !== null) { // If the each block isn't inert, then inert effects are currently outroing and will be removed once the transition is finished - if (is_inert || (current.e.f & INERT) === 0) { + if ((current.e.f & INERT) === 0) { to_destroy.push(current); } current = current.next; From 04addca428411088045b5a1774511593b5acc040 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 30 Jan 2025 13:50:00 -0500 Subject: [PATCH 0139/1429] Version Packages (#15156) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/famous-bulldogs-tan.md | 5 ----- .changeset/good-rocks-talk.md | 5 ----- .changeset/loud-cars-scream.md | 5 ----- .changeset/odd-rules-hear.md | 5 ----- .changeset/ten-cougars-look.md | 5 ----- .changeset/unlucky-gorillas-hunt.md | 5 ----- packages/svelte/CHANGELOG.md | 16 ++++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 9 files changed, 18 insertions(+), 32 deletions(-) delete mode 100644 .changeset/famous-bulldogs-tan.md delete mode 100644 .changeset/good-rocks-talk.md delete mode 100644 .changeset/loud-cars-scream.md delete mode 100644 .changeset/odd-rules-hear.md delete mode 100644 .changeset/ten-cougars-look.md delete mode 100644 .changeset/unlucky-gorillas-hunt.md diff --git a/.changeset/famous-bulldogs-tan.md b/.changeset/famous-bulldogs-tan.md deleted file mode 100644 index a5cc14b7f2..0000000000 --- a/.changeset/famous-bulldogs-tan.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: do not prune selectors like `:global(.foo):has(.scoped)` diff --git a/.changeset/good-rocks-talk.md b/.changeset/good-rocks-talk.md deleted file mode 100644 index 59af86c686..0000000000 --- a/.changeset/good-rocks-talk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: don't error on slot prop inside block inside other component diff --git a/.changeset/loud-cars-scream.md b/.changeset/loud-cars-scream.md deleted file mode 100644 index 2b61bc4453..0000000000 --- a/.changeset/loud-cars-scream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure reactions are correctly attached for unowned deriveds diff --git a/.changeset/odd-rules-hear.md b/.changeset/odd-rules-hear.md deleted file mode 100644 index 325b8ddf96..0000000000 --- a/.changeset/odd-rules-hear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: silence a11y attribute warnings when spread attributes present diff --git a/.changeset/ten-cougars-look.md b/.changeset/ten-cougars-look.md deleted file mode 100644 index fe20d057dd..0000000000 --- a/.changeset/ten-cougars-look.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: prevent false-positive ownership validations due to hot reload diff --git a/.changeset/unlucky-gorillas-hunt.md b/.changeset/unlucky-gorillas-hunt.md deleted file mode 100644 index 055fc120a6..0000000000 --- a/.changeset/unlucky-gorillas-hunt.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: widen ownership when calling setContext diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index bb48391e0e..0e8073cc5d 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,21 @@ # svelte +## 5.19.6 + +### Patch Changes + +- fix: do not prune selectors like `:global(.foo):has(.scoped)` ([#15140](https://github.com/sveltejs/svelte/pull/15140)) + +- fix: don't error on slot prop inside block inside other component ([#15148](https://github.com/sveltejs/svelte/pull/15148)) + +- fix: ensure reactions are correctly attached for unowned deriveds ([#15158](https://github.com/sveltejs/svelte/pull/15158)) + +- fix: silence a11y attribute warnings when spread attributes present ([#15150](https://github.com/sveltejs/svelte/pull/15150)) + +- fix: prevent false-positive ownership validations due to hot reload ([#15154](https://github.com/sveltejs/svelte/pull/15154)) + +- fix: widen ownership when calling setContext ([#15153](https://github.com/sveltejs/svelte/pull/15153)) + ## 5.19.5 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index a44ba43364..a286fa119d 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.19.5", + "version": "5.19.6", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index a509ef3fec..03ab37b583 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.19.5'; +export const VERSION = '5.19.6'; export const PUBLIC_VERSION = '5'; From e83ab1c0382199b50b0d2255668cd3f13cc603a2 Mon Sep 17 00:00:00 2001 From: tomoam <29677552+tomoam@users.noreply.github.com> Date: Fri, 31 Jan 2025 05:12:31 +0900 Subject: [PATCH 0141/1429] docs: fix typos and a link (#15135) --- documentation/docs/03-template-syntax/09-@const.md | 2 +- documentation/docs/03-template-syntax/11-bind.md | 2 +- documentation/docs/04-styling/01-scoped-styles.md | 5 +---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/documentation/docs/03-template-syntax/09-@const.md b/documentation/docs/03-template-syntax/09-@const.md index c42d3560fd..2a587b7a3d 100644 --- a/documentation/docs/03-template-syntax/09-@const.md +++ b/documentation/docs/03-template-syntax/09-@const.md @@ -11,4 +11,4 @@ The `{@const ...}` tag defines a local constant. {/each} ``` -`{@const}` is only allowed as an immediate child of a block — `{#if ...}`, `{#each ...}`, `{#snippet ...}` and so on — a `<Component />` or a `<svelte:boundary`. +`{@const}` is only allowed as an immediate child of a block — `{#if ...}`, `{#each ...}`, `{#snippet ...}` and so on — a `<Component />` or a `<svelte:boundary>`. diff --git a/documentation/docs/03-template-syntax/11-bind.md b/documentation/docs/03-template-syntax/11-bind.md index 90046c8c45..e56c2b4f77 100644 --- a/documentation/docs/03-template-syntax/11-bind.md +++ b/documentation/docs/03-template-syntax/11-bind.md @@ -235,7 +235,7 @@ You can give the `<select>` a default value by adding a `selected` attribute to ## `<video>` -`<video>` elements have all the same bindings as [#audio] elements, plus readonly [`videoWidth`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement/videoWidth) and [`videoHeight`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement/videoHeight) bindings. +`<video>` elements have all the same bindings as [`<audio>`](#audio) elements, plus readonly [`videoWidth`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement/videoWidth) and [`videoHeight`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement/videoHeight) bindings. ## `<img>` diff --git a/documentation/docs/04-styling/01-scoped-styles.md b/documentation/docs/04-styling/01-scoped-styles.md index f870d0a5b8..eae26d0cb1 100644 --- a/documentation/docs/04-styling/01-scoped-styles.md +++ b/documentation/docs/04-styling/01-scoped-styles.md @@ -33,10 +33,7 @@ If a component defines `@keyframes`, the name is scoped to the component using t /* these keyframes are only accessible inside this component */ @keyframes bounce { - /* ... *. + /* ... */ } </style> ``` - - - From 73fa9d33c8f862b88afc91601258738cf94adcd2 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 31 Jan 2025 06:43:44 -0500 Subject: [PATCH 0142/1429] chore: create intermediate directories as needed when compiling sandbox (#15163) --- playgrounds/sandbox/run.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/playgrounds/sandbox/run.js b/playgrounds/sandbox/run.js index 771dcc668e..4cb8314532 100644 --- a/playgrounds/sandbox/run.js +++ b/playgrounds/sandbox/run.js @@ -26,6 +26,15 @@ function mkdirp(dir) { } catch {} } +/** + * @param {string} file + * @param {string} contents + */ +function write(file, contents) { + mkdirp(path.dirname(file)); + fs.writeFileSync(file, contents); +} + const svelte_modules = glob('**/*.svelte', { cwd: `${cwd}/src` }); const js_modules = glob('**/*.js', { cwd: `${cwd}/src` }); @@ -46,7 +55,7 @@ for (const generate of /** @type {const} */ (['client', 'server'])) { modern: true }); - fs.writeFileSync( + write( `${cwd}/output/${file}.json`, JSON.stringify( ast, @@ -57,7 +66,7 @@ for (const generate of /** @type {const} */ (['client', 'server'])) { try { const migrated = migrate(source); - fs.writeFileSync(`${cwd}/output/${file}.migrated.svelte`, migrated.code); + write(`${cwd}/output/${file}.migrated.svelte`, migrated.code); } catch (e) { console.warn(`Error migrating ${file}`, e); } @@ -75,15 +84,12 @@ for (const generate of /** @type {const} */ (['client', 'server'])) { console.warn(warning.frame); } - fs.writeFileSync( - output_js, - compiled.js.code + '\n//# sourceMappingURL=' + path.basename(output_map) - ); + write(output_js, compiled.js.code + '\n//# sourceMappingURL=' + path.basename(output_map)); - fs.writeFileSync(output_map, compiled.js.map.toString()); + write(output_map, compiled.js.map.toString()); if (compiled.css) { - fs.writeFileSync(output_css, compiled.css.code); + write(output_css, compiled.css.code); } } @@ -100,6 +106,6 @@ for (const generate of /** @type {const} */ (['client', 'server'])) { const output_js = `${cwd}/output/${generate}/${file}`; mkdirp(path.dirname(output_js)); - fs.writeFileSync(output_js, compiled.js.code); + write(output_js, compiled.js.code); } } From 5b30fbfb0ba97bf031cb4b18cfac4bc45d54daf3 Mon Sep 17 00:00:00 2001 From: 7nik <kfiiranet@gmail.com> Date: Mon, 3 Feb 2025 11:11:40 +0200 Subject: [PATCH 0143/1429] fix: encounter svelte:element in blocks as sibling during pruning css (#15165) Fixes #14890 --- .changeset/sweet-mails-clean.md | 5 +++++ .../compiler/phases/2-analyze/css/css-prune.js | 17 +++++++++-------- .../_config.js | 8 ++++---- .../expected.css | 3 +++ .../input.svelte | 6 ++++++ 5 files changed, 27 insertions(+), 12 deletions(-) create mode 100644 .changeset/sweet-mails-clean.md diff --git a/.changeset/sweet-mails-clean.md b/.changeset/sweet-mails-clean.md new file mode 100644 index 0000000000..f82913abf7 --- /dev/null +++ b/.changeset/sweet-mails-clean.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: encounter svelte:element in blocks as sibling during pruning css diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index e719895798..fc8108e46e 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -933,11 +933,9 @@ function get_possible_element_siblings(node, adjacent_only, seen = new Set()) { /** * @param {Compiler.AST.EachBlock | Compiler.AST.IfBlock | Compiler.AST.AwaitBlock | Compiler.AST.KeyBlock | Compiler.AST.SlotElement} node * @param {boolean} adjacent_only - * @returns {Map<Compiler.AST.RegularElement, NodeExistsValue>} + * @returns {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement, NodeExistsValue>} */ function get_possible_last_child(node, adjacent_only) { - /** @typedef {Map<Compiler.AST.RegularElement, NodeExistsValue>} NodeMap */ - /** @type {Array<Compiler.AST.Fragment | undefined | null>} */ let fragments = []; @@ -960,7 +958,7 @@ function get_possible_last_child(node, adjacent_only) { break; } - /** @type {NodeMap} */ + /** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement, NodeExistsValue>} NodeMap */ const result = new Map(); let exhaustive = node.type !== 'SlotElement'; @@ -1001,9 +999,10 @@ function has_definite_elements(result) { } /** - * @template T - * @param {Map<T, NodeExistsValue>} from - * @param {Map<T, NodeExistsValue>} to + * @template T2 + * @template {T2} T1 + * @param {Map<T1, NodeExistsValue>} from + * @param {Map<T2, NodeExistsValue>} to * @returns {void} */ function add_to_map(from, to) { @@ -1028,7 +1027,7 @@ function higher_existence(exist1, exist2) { * @param {boolean} adjacent_only */ function loop_child(children, adjacent_only) { - /** @type {Map<Compiler.AST.RegularElement, NodeExistsValue>} */ + /** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement, NodeExistsValue>} */ const result = new Map(); let i = children.length; @@ -1041,6 +1040,8 @@ function loop_child(children, adjacent_only) { if (adjacent_only) { break; } + } else if (child.type === 'SvelteElement') { + result.set(child, NODE_PROBABLY_EXISTS); } else if (is_block(child)) { const child_result = get_possible_last_child(child, adjacent_only); add_to_map(child_result, result); diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/_config.js b/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/_config.js index 7c50a76bbb..a7d6c3a99d 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/_config.js +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/_config.js @@ -5,15 +5,15 @@ export default test({ { code: 'css_unused_selector', end: { - character: 496, + character: 627, column: 10, - line: 26 + line: 32 }, message: 'Unused CSS selector ".x + .bar"', start: { - character: 487, + character: 618, column: 1, - line: 26 + line: 32 } } ] diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/expected.css b/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/expected.css index 830d366702..d3fa8c97d2 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/expected.css +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/expected.css @@ -9,5 +9,8 @@ .x.svelte-xyz ~ .foo:where(.svelte-xyz) span:where(.svelte-xyz) { color: green; } .x.svelte-xyz ~ .bar:where(.svelte-xyz) { color: green; } + .z.svelte-xyz + .z:where(.svelte-xyz) { color: green; } + .z.svelte-xyz ~ .z:where(.svelte-xyz) { color: green; } + /* no match */ /* (unused) .x + .bar { color: green; }*/ diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/input.svelte index 1c51a2c516..655fd86153 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-svelteelement/input.svelte @@ -10,6 +10,9 @@ </p> <p class="bar">bar</p> </div> +{#each [1]} + <svelte:element class="z" this={tag}></svelte:element> +{/each} <style> .before + .foo { color: green; } @@ -22,6 +25,9 @@ .x ~ .foo span { color: green; } .x ~ .bar { color: green; } + .z + .z { color: green; } + .z ~ .z { color: green; } + /* no match */ .x + .bar { color: green; } </style> From e12fe8795ca2b86a9509c8924fb0fe07a8858d03 Mon Sep 17 00:00:00 2001 From: pgliang001 <65393356+pgliang001@users.noreply.github.com> Date: Mon, 3 Feb 2025 17:13:30 +0800 Subject: [PATCH 0144/1429] chore: simplify assignment expression (#15177) --- packages/svelte/src/internal/client/render.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/internal/client/render.js b/packages/svelte/src/internal/client/render.js index bc74047f64..3256fe8274 100644 --- a/packages/svelte/src/internal/client/render.js +++ b/packages/svelte/src/internal/client/render.js @@ -55,7 +55,7 @@ export function set_text(text, value) { if (str !== (text.__t ??= text.nodeValue)) { // @ts-expect-error text.__t = str; - text.nodeValue = str == null ? '' : str + ''; + text.nodeValue = str + ''; } } From e0b3dc020cb0ee693635d3ad9711a39689c32da6 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Mon, 3 Feb 2025 17:20:29 +0000 Subject: [PATCH 0145/1429] chore: remove unused code from signal logic (#15195) --- .changeset/blue-sheep-joke.md | 5 +++++ .../src/internal/client/reactivity/deriveds.js | 12 ------------ .../src/internal/client/reactivity/effects.js | 2 +- packages/svelte/src/internal/client/runtime.js | 16 +--------------- 4 files changed, 7 insertions(+), 28 deletions(-) create mode 100644 .changeset/blue-sheep-joke.md diff --git a/.changeset/blue-sheep-joke.md b/.changeset/blue-sheep-joke.md new file mode 100644 index 0000000000..1d9ff973c5 --- /dev/null +++ b/.changeset/blue-sheep-joke.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: remove unused code from signal logic diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 1f65ff38c3..60b55970e6 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -176,15 +176,3 @@ export function update_derived(derived) { derived.wv = increment_write_version(); } } - -/** - * @param {Derived} derived - * @returns {void} - */ -export function destroy_derived(derived) { - destroy_derived_effects(derived); - remove_reactions(derived, 0); - set_signal_status(derived, DESTROYED); - - derived.v = derived.deps = derived.ctx = derived.reactions = null; -} diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index d014ff793d..eab6c767f8 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -42,7 +42,7 @@ import * as e from '../errors.js'; import { DEV } from 'esm-env'; import { define_property } from '../../shared/utils.js'; import { get_next_sibling } from '../dom/operations.js'; -import { derived, destroy_derived } from './deriveds.js'; +import { derived } from './deriveds.js'; import { component_context, dev_current_component_function } from '../context.js'; /** diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index a572e27bf4..57cefccc01 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -27,12 +27,7 @@ import { } from './constants.js'; import { flush_tasks } from './dom/task.js'; import { internal_set } from './reactivity/sources.js'; -import { - destroy_derived, - destroy_derived_effects, - execute_derived, - update_derived -} from './reactivity/deriveds.js'; +import { destroy_derived_effects, update_derived } from './reactivity/deriveds.js'; import * as e from './errors.js'; import { FILENAME } from '../../constants.js'; import { tracing_mode_flag } from '../flags/index.js'; @@ -919,15 +914,6 @@ export function get(signal) { var flags = signal.f; var is_derived = (flags & DERIVED) !== 0; - // If the derived is destroyed, just execute it again without retaining - // its memoisation properties as the derived is stale - if (is_derived && (flags & DESTROYED) !== 0) { - var value = execute_derived(/** @type {Derived} */ (signal)); - // Ensure the derived remains destroyed - destroy_derived(/** @type {Derived} */ (signal)); - return value; - } - if (captured_signals !== null) { captured_signals.add(signal); } From e1014e30160dfbca414bfa907d77f30f8e4f0204 Mon Sep 17 00:00:00 2001 From: Scott Wu <sw@scottwu.ca> Date: Tue, 4 Feb 2025 01:21:28 +0800 Subject: [PATCH 0146/1429] Update 11-bind.md (#15192) --- documentation/docs/03-template-syntax/11-bind.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/documentation/docs/03-template-syntax/11-bind.md b/documentation/docs/03-template-syntax/11-bind.md index e56c2b4f77..119f87ed8e 100644 --- a/documentation/docs/03-template-syntax/11-bind.md +++ b/documentation/docs/03-template-syntax/11-bind.md @@ -219,11 +219,10 @@ You can give the `<select>` a default value by adding a `selected` attribute to - [`volume`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/volume) - [`muted`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/muted) -...and seven readonly ones: +...and six readonly ones: - [`duration`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/duration) - [`buffered`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/buffered) -- [`paused`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/paused) - [`seekable`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seekable) - [`seeking`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seeking_event) - [`ended`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ended) From 64f86ee9d470089f378749d64826f6ef0d24d800 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 12:30:41 -0500 Subject: [PATCH 0147/1429] Version Packages (#15190) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/blue-sheep-joke.md | 5 ----- .changeset/sweet-mails-clean.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/blue-sheep-joke.md delete mode 100644 .changeset/sweet-mails-clean.md diff --git a/.changeset/blue-sheep-joke.md b/.changeset/blue-sheep-joke.md deleted file mode 100644 index 1d9ff973c5..0000000000 --- a/.changeset/blue-sheep-joke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: remove unused code from signal logic diff --git a/.changeset/sweet-mails-clean.md b/.changeset/sweet-mails-clean.md deleted file mode 100644 index f82913abf7..0000000000 --- a/.changeset/sweet-mails-clean.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: encounter svelte:element in blocks as sibling during pruning css diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 0e8073cc5d..9ebd37aadf 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.19.7 + +### Patch Changes + +- chore: remove unused code from signal logic ([#15195](https://github.com/sveltejs/svelte/pull/15195)) + +- fix: encounter svelte:element in blocks as sibling during pruning css ([#15165](https://github.com/sveltejs/svelte/pull/15165)) + ## 5.19.6 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index a286fa119d..8ea495af99 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.19.6", + "version": "5.19.7", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 03ab37b583..d3c6e6b321 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.19.6'; +export const VERSION = '5.19.7'; export const PUBLIC_VERSION = '5'; From f878736f3825e1832fa7344306358e877e20bd7f Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Mon, 3 Feb 2025 13:23:46 -0500 Subject: [PATCH 0148/1429] try this (#15196) --- .github/workflows/pkg.pr.new.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml index 4292ec900a..99f8153517 100644 --- a/.github/workflows/pkg.pr.new.yml +++ b/.github/workflows/pkg.pr.new.yml @@ -9,6 +9,9 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: install corepack + run: npm i -g corepack@0.31.0 + - run: corepack enable - uses: actions/setup-node@v4 with: From 7867e7584bf120a309811f63cb87527ee83242b1 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Mon, 3 Feb 2025 13:36:00 -0500 Subject: [PATCH 0149/1429] bump vite (#15198) --- playgrounds/sandbox/package.json | 2 +- pnpm-lock.yaml | 75 +++++++++++++++++++++++++------- 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index 654a517c9f..09a84cadc9 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -16,7 +16,7 @@ "polka": "^1.0.0-next.25", "svelte": "workspace:*", "tiny-glob": "^0.2.9", - "vite": "^5.4.6", + "vite": "^5.4.14", "vite-plugin-inspect": "^0.8.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1979ce7b22..8288f229cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,7 +152,7 @@ importers: devDependencies: '@sveltejs/vite-plugin-svelte': specifier: ^4.0.0-next.6 - version: 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) polka: specifier: ^1.0.0-next.25 version: 1.0.0-next.25 @@ -163,11 +163,11 @@ importers: specifier: ^0.2.9 version: 0.2.9 vite: - specifier: ^5.4.6 - version: 5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + specifier: ^5.4.14 + version: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vite-plugin-inspect: specifier: ^0.8.4 - version: 0.8.4(rollup@4.22.4)(vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 0.8.4(rollup@4.22.4)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) packages: @@ -2144,6 +2144,37 @@ packages: '@nuxt/kit': optional: true + vite@5.4.14: + resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + vite@5.4.6: resolution: {integrity: sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==} engines: {node: ^18.0.0 || >=20.0.0} @@ -2757,25 +2788,25 @@ snapshots: typescript: 5.5.4 typescript-eslint: 8.2.0(eslint@9.9.1)(typescript@5.5.4) - '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.3.6 svelte: link:packages/svelte - vite: 5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.3.6 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.11 svelte: link:packages/svelte - vite: 5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) - vitefu: 0.2.5(vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vitefu: 0.2.5(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) transitivePeerDependencies: - supports-color @@ -4254,7 +4285,7 @@ snapshots: debug: 4.3.6 pathe: 1.1.2 tinyrainbow: 1.2.0 - vite: 5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - '@types/node' - less @@ -4266,7 +4297,7 @@ snapshots: - supports-color - terser - vite-plugin-inspect@0.8.4(rollup@4.22.4)(vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vite-plugin-inspect@0.8.4(rollup@4.22.4)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): dependencies: '@antfu/utils': 0.7.8 '@rollup/pluginutils': 5.1.0(rollup@4.22.4) @@ -4277,11 +4308,23 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.0 sirv: 2.0.4 - vite: 5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - rollup - supports-color + vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): + dependencies: + esbuild: 0.21.5 + postcss: 8.4.47 + rollup: 4.22.4 + optionalDependencies: + '@types/node': 20.12.7 + fsevents: 2.3.3 + lightningcss: 1.23.0 + sass: 1.70.0 + terser: 5.27.0 + vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: esbuild: 0.21.5 @@ -4294,9 +4337,9 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vitefu@0.2.5(vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vitefu@0.2.5(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): optionalDependencies: - vite: 5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vitest@2.0.5(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: From 502b585ed233adaa097e5eb72e45b9936219b60d Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 3 Feb 2025 19:37:49 +0100 Subject: [PATCH 0150/1429] docs: enhance migration docs about accessors (#15138) * docs: enhance migration docs about accessors related to #15134 * more --- .../docs/07-misc/07-v5-migration-guide.md | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/documentation/docs/07-misc/07-v5-migration-guide.md b/documentation/docs/07-misc/07-v5-migration-guide.md index ce95bf6ac7..94ade6e887 100644 --- a/documentation/docs/07-misc/07-v5-migration-guide.md +++ b/documentation/docs/07-misc/07-v5-migration-guide.md @@ -722,7 +722,39 @@ If a bindable property has a default value (e.g. `let { foo = $bindable('bar') } ### `accessors` option is ignored -Setting the `accessors` option to `true` makes properties of a component directly accessible on the component instance. In runes mode, properties are never accessible on the component instance. You can use component exports instead if you need to expose them. +Setting the `accessors` option to `true` makes properties of a component directly accessible on the component instance. + +```svelte +<svelte:options accessors={true} /> + +<script> + // available via componentInstance.name + export let name; +</script> +``` + +In runes mode, properties are never accessible on the component instance. You can use component exports instead if you need to expose them. + +```svelte +<script> + let { name } = $props(); + // available via componentInstance.getName() + export const getName = () => name; +</script> +``` + +Alternatively, if the place where they are instantiated is under your control, you can also make use of runes inside `.js/.ts` files by adjusting their ending to include `.svelte`, i.e. `.svelte.js` or `.svelte.ts`, and then use `$state`: + +```js ++++import { mount } from 'svelte';+++ +import App from './App.svelte' + +---const app = new App({ target: document.getElementById("app"), props: { foo: 'bar' } }); +app.foo = 'baz'--- ++++const props = $state({ foo: 'bar' }); +const app = mount(App, { target: document.getElementById("app"), props }); +props.foo = 'baz';+++ +``` ### `immutable` option is ignored From f67cf201af118af2091a91adb3082da10e72a170 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 14:51:08 -0500 Subject: [PATCH 0151/1429] chore(deps-dev): bump vite from 5.4.14 to 6.0.9 (#15199) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.14 to 6.0.9. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v6.0.9/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- playgrounds/sandbox/package.json | 2 +- pnpm-lock.yaml | 713 +++++++++++++++++++++++++++---- 2 files changed, 627 insertions(+), 88 deletions(-) diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index 09a84cadc9..d9c490ba86 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -16,7 +16,7 @@ "polka": "^1.0.0-next.25", "svelte": "workspace:*", "tiny-glob": "^0.2.9", - "vite": "^5.4.14", + "vite": "^6.0.9", "vite-plugin-inspect": "^0.8.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8288f229cb..31d1e4b816 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,7 +152,7 @@ importers: devDependencies: '@sveltejs/vite-plugin-svelte': specifier: ^4.0.0-next.6 - version: 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 4.0.0-next.6(svelte@packages+svelte)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) polka: specifier: ^1.0.0-next.25 version: 1.0.0-next.25 @@ -163,11 +163,11 @@ importers: specifier: ^0.2.9 version: 0.2.9 vite: - specifier: ^5.4.14 - version: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + specifier: ^6.0.9 + version: 6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vite-plugin-inspect: specifier: ^0.8.4 - version: 0.8.4(rollup@4.22.4)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 0.8.4(rollup@4.34.1)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) packages: @@ -270,148 +270,308 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.24.2': + resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.21.5': resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.24.2': + resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.21.5': resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.24.2': + resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.21.5': resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} cpu: [x64] os: [android] + '@esbuild/android-x64@0.24.2': + resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.21.5': resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.24.2': + resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.21.5': resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.24.2': + resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.21.5': resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.24.2': + resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.21.5': resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.24.2': + resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.21.5': resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.24.2': + resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.21.5': resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.24.2': + resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.21.5': resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.24.2': + resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.21.5': resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.24.2': + resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.21.5': resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.24.2': + resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.21.5': resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.24.2': + resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.21.5': resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.24.2': + resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.21.5': resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.24.2': + resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.21.5': resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.24.2': + resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.24.2': + resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.24.2': + resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.24.2': + resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.21.5': resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.24.2': + resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.24.2': + resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.21.5': resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.24.2': + resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.21.5': resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.24.2': + resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.21.5': resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.24.2': + resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@eslint-community/eslint-utils@4.4.0': resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.4.1': + resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.11.0': resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint/config-array@0.18.0': resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -545,81 +705,176 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.34.1': + resolution: {integrity: sha512-kwctwVlswSEsr4ljpmxKrRKp1eG1v2NAhlzFzDf1x1OdYaMjBYjDCbHkzWm57ZXzTwqn8stMXgROrnMw8dJK3w==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.22.4': resolution: {integrity: sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.34.1': + resolution: {integrity: sha512-4H5ZtZitBPlbPsTv6HBB8zh1g5d0T8TzCmpndQdqq20Ugle/nroOyDMf9p7f88Gsu8vBLU78/cuh8FYHZqdXxw==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.22.4': resolution: {integrity: sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.34.1': + resolution: {integrity: sha512-f2AJ7Qwx9z25hikXvg+asco8Sfuc5NCLg8rmqQBIOUoWys5sb/ZX9RkMZDPdnnDevXAMJA5AWLnRBmgdXGEUiA==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.22.4': resolution: {integrity: sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.34.1': + resolution: {integrity: sha512-+/2JBrRfISCsWE4aEFXxd+7k9nWGXA8+wh7ZUHn/u8UDXOU9LN+QYKKhd57sIn6WRcorOnlqPMYFIwie/OHXWw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.34.1': + resolution: {integrity: sha512-SUeB0pYjIXwT2vfAMQ7E4ERPq9VGRrPR7Z+S4AMssah5EHIilYqjWQoTn5dkDtuIJUSTs8H+C9dwoEcg3b0sCA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.34.1': + resolution: {integrity: sha512-L3T66wAZiB/ooiPbxz0s6JEX6Sr2+HfgPSK+LMuZkaGZFAFCQAHiP3dbyqovYdNaiUXcl9TlgnIbcsIicAnOZg==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.22.4': resolution: {integrity: sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.34.1': + resolution: {integrity: sha512-UBXdQ4+ATARuFgsFrQ+tAsKvBi/Hly99aSVdeCUiHV9dRTTpMU7OrM3WXGys1l40wKVNiOl0QYY6cZQJ2xhKlQ==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.22.4': resolution: {integrity: sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.34.1': + resolution: {integrity: sha512-m/yfZ25HGdcCSwmopEJm00GP7xAUyVcBPjttGLRAqZ60X/bB4Qn6gP7XTwCIU6bITeKmIhhwZ4AMh2XLro+4+w==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.22.4': resolution: {integrity: sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.34.1': + resolution: {integrity: sha512-Wy+cUmFuvziNL9qWRRzboNprqSQ/n38orbjRvd6byYWridp5TJ3CD+0+HUsbcWVSNz9bxkDUkyASGP0zS7GAvg==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.22.4': resolution: {integrity: sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.34.1': + resolution: {integrity: sha512-CQ3MAGgiFmQW5XJX5W3wnxOBxKwFlUAgSXFA2SwgVRjrIiVt5LHfcQLeNSHKq5OEZwv+VCBwlD1+YKCjDG8cpg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.34.1': + resolution: {integrity: sha512-rSzb1TsY4lSwH811cYC3OC2O2mzNMhM13vcnA7/0T6Mtreqr3/qs6WMDriMRs8yvHDI54qxHgOk8EV5YRAHFbw==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': resolution: {integrity: sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.34.1': + resolution: {integrity: sha512-fwr0n6NS0pG3QxxlqVYpfiY64Fd1Dqd8Cecje4ILAV01ROMp4aEdCj5ssHjRY3UwU7RJmeWd5fi89DBqMaTawg==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.22.4': resolution: {integrity: sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.34.1': + resolution: {integrity: sha512-4uJb9qz7+Z/yUp5RPxDGGGUcoh0PnKF33QyWgEZ3X/GocpWb6Mb+skDh59FEt5d8+Skxqs9mng6Swa6B2AmQZg==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.22.4': resolution: {integrity: sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.34.1': + resolution: {integrity: sha512-QlIo8ndocWBEnfmkYqj8vVtIUpIqJjfqKggjy7IdUncnt8BGixte1wDON7NJEvLg3Kzvqxtbo8tk+U1acYEBlw==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.22.4': resolution: {integrity: sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.34.1': + resolution: {integrity: sha512-hzpleiKtq14GWjz3ahWvJXgU1DQC9DteiwcsY4HgqUJUGxZThlL66MotdUEK9zEo0PK/2ADeZGM9LIondE302A==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.22.4': resolution: {integrity: sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.34.1': + resolution: {integrity: sha512-jqtKrO715hDlvUcEsPn55tZt2TEiBvBtCMkUuU0R6fO/WPT7lO9AONjPbd8II7/asSiNVQHCMn4OLGigSuxVQA==} + cpu: [x64] + os: [linux] + '@rollup/rollup-win32-arm64-msvc@4.22.4': resolution: {integrity: sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.34.1': + resolution: {integrity: sha512-RnHy7yFf2Wz8Jj1+h8klB93N0NHNHXFhNwAmiy9zJdpY7DE01VbEVtPdrK1kkILeIbHGRJjvfBDBhnxBr8kD4g==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.22.4': resolution: {integrity: sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.34.1': + resolution: {integrity: sha512-i7aT5HdiZIcd7quhzvwQ2oAuX7zPYrYfkrd1QFfs28Po/i0q6kas/oRrzGlDhAEyug+1UfUtkWdmoVlLJj5x9Q==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.22.4': resolution: {integrity: sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.34.1': + resolution: {integrity: sha512-k3MVFD9Oq+laHkw2N2v7ILgoa9017ZMF/inTtHzyTVZjYs9cSH18sdyAf6spBAJIGwJ5UaC7et2ZH1WCdlhkMw==} + cpu: [x64] + os: [win32] + '@stylistic/eslint-plugin-js@1.8.0': resolution: {integrity: sha512-jdvnzt+pZPg8TfclZlTZPiUbbima93ylvQ+wNgHLNmup3obY6heQvgewSu9i2CfS61BnRByv+F9fxQLPoNeHag==} engines: {node: ^16.0.0 || >=18.0.0} @@ -778,6 +1033,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@7.1.1: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} @@ -841,8 +1101,8 @@ packages: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} - binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} brace-expansion@1.1.11: @@ -855,6 +1115,10 @@ packages: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -885,8 +1149,8 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} - chokidar@3.5.3: - resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} ci-info@3.9.0: @@ -949,6 +1213,15 @@ packages: supports-color: optional: true + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} @@ -1011,8 +1284,8 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - enhanced-resolve@5.17.1: - resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} + enhanced-resolve@5.18.0: + resolution: {integrity: sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==} engines: {node: '>=10.13.0'} enquirer@2.4.1: @@ -1031,6 +1304,11 @@ packages: engines: {node: '>=12'} hasBin: true + esbuild@0.24.2: + resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} + engines: {node: '>=18'} + hasBin: true + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -1182,6 +1460,10 @@ packages: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -1237,8 +1519,8 @@ packages: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} - get-tsconfig@4.7.6: - resolution: {integrity: sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==} + get-tsconfig@4.10.0: + resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -1256,6 +1538,10 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} + globals@15.14.0: + resolution: {integrity: sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==} + engines: {node: '>=18'} + globals@15.9.0: resolution: {integrity: sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==} engines: {node: '>=18'} @@ -1318,8 +1604,8 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - immutable@4.3.4: - resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==} + immutable@4.3.7: + resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} @@ -1605,8 +1891,11 @@ packages: ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -1729,6 +2018,9 @@ packages: picocolors@1.1.0: resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -1783,8 +2075,8 @@ packages: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} - postcss@8.4.47: - resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} + postcss@8.5.1: + resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -1859,6 +2151,11 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.34.1: + resolution: {integrity: sha512-iYZ/+PcdLYSGfH3S+dGahlW/RWmsqDhLgj1BT9DH/xXJ0ggZN7xkdP9wipPNjjNLczI+fmMLmTB9pye+d2r4GQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + rrweb-cssom@0.7.1: resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} @@ -1893,6 +2190,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.0: + resolution: {integrity: sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==} + engines: {node: '>=10'} + hasBin: true + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -1996,11 +2298,11 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte-eslint-parser@0.41.0: - resolution: {integrity: sha512-L6f4hOL+AbgfBIB52Z310pg1d2QjRqm7wy3kI1W6hhdhX5bvu7+f0R6w4ykp5HoDdzq+vGhIJmsisaiJDGmVfA==} + svelte-eslint-parser@0.43.0: + resolution: {integrity: sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - svelte: ^3.37.0 || ^4.0.0 || ^5.0.0-next.191 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 peerDependenciesMeta: svelte: optional: true @@ -2144,8 +2446,8 @@ packages: '@nuxt/kit': optional: true - vite@5.4.14: - resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} + vite@5.4.6: + resolution: {integrity: sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -2175,22 +2477,27 @@ packages: terser: optional: true - vite@5.4.6: - resolution: {integrity: sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@6.0.9: + resolution: {integrity: sha512-MSgUxHcaXLtnBPktkbUSoQUANApKYuxZ6DrbVENlIorbhL2dZydTLaZ01tjUoE3szeFzlFk9ANOKk0xurh4MKA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' less: '*' lightningcss: ^1.21.0 sass: '*' sass-embedded: '*' stylus: '*' sugarss: '*' - terser: ^5.4.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + jiti: + optional: true less: optional: true lightningcss: @@ -2205,6 +2512,10 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true vitefu@0.2.5: resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} @@ -2505,79 +2816,161 @@ snapshots: '@esbuild/aix-ppc64@0.21.5': optional: true + '@esbuild/aix-ppc64@0.24.2': + optional: true + '@esbuild/android-arm64@0.21.5': optional: true + '@esbuild/android-arm64@0.24.2': + optional: true + '@esbuild/android-arm@0.21.5': optional: true + '@esbuild/android-arm@0.24.2': + optional: true + '@esbuild/android-x64@0.21.5': optional: true + '@esbuild/android-x64@0.24.2': + optional: true + '@esbuild/darwin-arm64@0.21.5': optional: true + '@esbuild/darwin-arm64@0.24.2': + optional: true + '@esbuild/darwin-x64@0.21.5': optional: true + '@esbuild/darwin-x64@0.24.2': + optional: true + '@esbuild/freebsd-arm64@0.21.5': optional: true + '@esbuild/freebsd-arm64@0.24.2': + optional: true + '@esbuild/freebsd-x64@0.21.5': optional: true + '@esbuild/freebsd-x64@0.24.2': + optional: true + '@esbuild/linux-arm64@0.21.5': optional: true + '@esbuild/linux-arm64@0.24.2': + optional: true + '@esbuild/linux-arm@0.21.5': optional: true + '@esbuild/linux-arm@0.24.2': + optional: true + '@esbuild/linux-ia32@0.21.5': optional: true + '@esbuild/linux-ia32@0.24.2': + optional: true + '@esbuild/linux-loong64@0.21.5': optional: true + '@esbuild/linux-loong64@0.24.2': + optional: true + '@esbuild/linux-mips64el@0.21.5': optional: true + '@esbuild/linux-mips64el@0.24.2': + optional: true + '@esbuild/linux-ppc64@0.21.5': optional: true + '@esbuild/linux-ppc64@0.24.2': + optional: true + '@esbuild/linux-riscv64@0.21.5': optional: true + '@esbuild/linux-riscv64@0.24.2': + optional: true + '@esbuild/linux-s390x@0.21.5': optional: true + '@esbuild/linux-s390x@0.24.2': + optional: true + '@esbuild/linux-x64@0.21.5': optional: true + '@esbuild/linux-x64@0.24.2': + optional: true + + '@esbuild/netbsd-arm64@0.24.2': + optional: true + '@esbuild/netbsd-x64@0.21.5': optional: true + '@esbuild/netbsd-x64@0.24.2': + optional: true + + '@esbuild/openbsd-arm64@0.24.2': + optional: true + '@esbuild/openbsd-x64@0.21.5': optional: true + '@esbuild/openbsd-x64@0.24.2': + optional: true + '@esbuild/sunos-x64@0.21.5': optional: true + '@esbuild/sunos-x64@0.24.2': + optional: true + '@esbuild/win32-arm64@0.21.5': optional: true + '@esbuild/win32-arm64@0.24.2': + optional: true + '@esbuild/win32-ia32@0.21.5': optional: true + '@esbuild/win32-ia32@0.24.2': + optional: true + '@esbuild/win32-x64@0.21.5': optional: true + '@esbuild/win32-x64@0.24.2': + optional: true + '@eslint-community/eslint-utils@4.4.0(eslint@9.9.1)': dependencies: eslint: 9.9.1 eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.4.1(eslint@9.9.1)': + dependencies: + eslint: 9.9.1 + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.11.0': {} + '@eslint-community/regexpp@4.12.1': {} + '@eslint/config-array@0.18.0': dependencies: '@eslint/object-schema': 2.1.4 @@ -2720,58 +3113,123 @@ snapshots: optionalDependencies: rollup: 4.22.4 + '@rollup/pluginutils@5.1.0(rollup@4.34.1)': + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 2.3.1 + optionalDependencies: + rollup: 4.34.1 + '@rollup/rollup-android-arm-eabi@4.22.4': optional: true + '@rollup/rollup-android-arm-eabi@4.34.1': + optional: true + '@rollup/rollup-android-arm64@4.22.4': optional: true + '@rollup/rollup-android-arm64@4.34.1': + optional: true + '@rollup/rollup-darwin-arm64@4.22.4': optional: true + '@rollup/rollup-darwin-arm64@4.34.1': + optional: true + '@rollup/rollup-darwin-x64@4.22.4': optional: true + '@rollup/rollup-darwin-x64@4.34.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.34.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.34.1': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.22.4': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.34.1': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.22.4': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.34.1': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.22.4': optional: true + '@rollup/rollup-linux-arm64-gnu@4.34.1': + optional: true + '@rollup/rollup-linux-arm64-musl@4.22.4': optional: true + '@rollup/rollup-linux-arm64-musl@4.34.1': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.34.1': + optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.34.1': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.22.4': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.34.1': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.22.4': optional: true + '@rollup/rollup-linux-s390x-gnu@4.34.1': + optional: true + '@rollup/rollup-linux-x64-gnu@4.22.4': optional: true + '@rollup/rollup-linux-x64-gnu@4.34.1': + optional: true + '@rollup/rollup-linux-x64-musl@4.22.4': optional: true + '@rollup/rollup-linux-x64-musl@4.34.1': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.22.4': optional: true + '@rollup/rollup-win32-arm64-msvc@4.34.1': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.22.4': optional: true + '@rollup/rollup-win32-ia32-msvc@4.34.1': + optional: true + '@rollup/rollup-win32-x64-msvc@4.22.4': optional: true + '@rollup/rollup-win32-x64-msvc@4.34.1': + optional: true + '@stylistic/eslint-plugin-js@1.8.0(eslint@9.9.1)': dependencies: '@types/eslint': 8.56.12 - acorn: 8.12.1 + acorn: 8.14.0 escape-string-regexp: 4.0.0 eslint: 9.9.1 eslint-visitor-keys: 3.4.3 @@ -2788,25 +3246,25 @@ snapshots: typescript: 5.5.4 typescript-eslint: 8.2.0(eslint@9.9.1)(typescript@5.5.4) - '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.3.6 svelte: link:packages/svelte - vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.3.6 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.11 svelte: link:packages/svelte - vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) - vitefu: 0.2.5(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + vite: 6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vitefu: 0.2.5(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) transitivePeerDependencies: - supports-color @@ -2976,12 +3434,18 @@ snapshots: dependencies: acorn: 8.12.1 + acorn-jsx@5.3.2(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + acorn-typescript@1.4.13(acorn@8.12.1): dependencies: acorn: 8.12.1 acorn@8.12.1: {} + acorn@8.14.0: {} + agent-base@7.1.1: dependencies: debug: 4.3.6 @@ -3035,7 +3499,7 @@ snapshots: dependencies: is-windows: 1.0.2 - binary-extensions@2.2.0: + binary-extensions@2.3.0: optional: true brace-expansion@1.1.11: @@ -3051,6 +3515,11 @@ snapshots: dependencies: fill-range: 7.0.1 + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + optional: true + buffer-from@1.1.2: {} bundle-name@4.1.0: @@ -3078,10 +3547,10 @@ snapshots: check-error@2.1.1: {} - chokidar@3.5.3: + chokidar@3.6.0: dependencies: anymatch: 3.1.3 - braces: 3.0.2 + braces: 3.0.3 glob-parent: 5.1.2 is-binary-path: 2.1.0 is-glob: 4.0.3 @@ -3140,6 +3609,10 @@ snapshots: dependencies: ms: 2.1.2 + debug@4.4.0: + dependencies: + ms: 2.1.3 + decimal.js@10.4.3: {} deep-eql@5.0.2: {} @@ -3189,7 +3662,7 @@ snapshots: emoji-regex@9.2.2: {} - enhanced-resolve@5.17.1: + enhanced-resolve@5.18.0: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 @@ -3229,12 +3702,40 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 + esbuild@0.24.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.24.2 + '@esbuild/android-arm': 0.24.2 + '@esbuild/android-arm64': 0.24.2 + '@esbuild/android-x64': 0.24.2 + '@esbuild/darwin-arm64': 0.24.2 + '@esbuild/darwin-x64': 0.24.2 + '@esbuild/freebsd-arm64': 0.24.2 + '@esbuild/freebsd-x64': 0.24.2 + '@esbuild/linux-arm': 0.24.2 + '@esbuild/linux-arm64': 0.24.2 + '@esbuild/linux-ia32': 0.24.2 + '@esbuild/linux-loong64': 0.24.2 + '@esbuild/linux-mips64el': 0.24.2 + '@esbuild/linux-ppc64': 0.24.2 + '@esbuild/linux-riscv64': 0.24.2 + '@esbuild/linux-s390x': 0.24.2 + '@esbuild/linux-x64': 0.24.2 + '@esbuild/netbsd-arm64': 0.24.2 + '@esbuild/netbsd-x64': 0.24.2 + '@esbuild/openbsd-arm64': 0.24.2 + '@esbuild/openbsd-x64': 0.24.2 + '@esbuild/sunos-x64': 0.24.2 + '@esbuild/win32-arm64': 0.24.2 + '@esbuild/win32-ia32': 0.24.2 + '@esbuild/win32-x64': 0.24.2 + escape-string-regexp@4.0.0: {} eslint-compat-utils@0.5.1(eslint@9.9.1): dependencies: eslint: 9.9.1 - semver: 7.6.3 + semver: 7.7.0 eslint-config-prettier@9.1.0(eslint@9.9.1): dependencies: @@ -3242,8 +3743,8 @@ snapshots: eslint-plugin-es-x@7.8.0(eslint@9.9.1): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1) - '@eslint-community/regexpp': 4.11.0 + '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) + '@eslint-community/regexpp': 4.12.1 eslint: 9.9.1 eslint-compat-utils: 0.5.1(eslint@9.9.1) @@ -3251,31 +3752,31 @@ snapshots: eslint-plugin-n@17.9.0(eslint@9.9.1): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1) - enhanced-resolve: 5.17.1 + '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) + enhanced-resolve: 5.18.0 eslint: 9.9.1 eslint-plugin-es-x: 7.8.0(eslint@9.9.1) - get-tsconfig: 4.7.6 - globals: 15.9.0 + get-tsconfig: 4.10.0 + globals: 15.14.0 ignore: 5.3.2 minimatch: 9.0.5 - semver: 7.6.3 + semver: 7.7.0 eslint-plugin-svelte@2.38.0(eslint@9.9.1)(svelte@packages+svelte): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) '@jridgewell/sourcemap-codec': 1.5.0 - debug: 4.3.6 + debug: 4.4.0 eslint: 9.9.1 eslint-compat-utils: 0.5.1(eslint@9.9.1) esutils: 2.0.3 known-css-properties: 0.30.0 - postcss: 8.4.47 - postcss-load-config: 3.1.4(postcss@8.4.47) - postcss-safe-parser: 6.0.0(postcss@8.4.47) + postcss: 8.5.1 + postcss-load-config: 3.1.4(postcss@8.5.1) + postcss-safe-parser: 6.0.0(postcss@8.5.1) postcss-selector-parser: 6.1.2 - semver: 7.6.3 - svelte-eslint-parser: 0.41.0(svelte@packages+svelte) + semver: 7.7.0 + svelte-eslint-parser: 0.43.0(svelte@packages+svelte) optionalDependencies: svelte: link:packages/svelte transitivePeerDependencies: @@ -3345,8 +3846,8 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} @@ -3423,6 +3924,11 @@ snapshots: dependencies: to-regex-range: 5.0.1 + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + optional: true + find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -3481,7 +3987,7 @@ snapshots: get-stream@8.0.1: {} - get-tsconfig@4.7.6: + get-tsconfig@4.10.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -3504,6 +4010,8 @@ snapshots: globals@14.0.0: {} + globals@15.14.0: {} + globals@15.9.0: {} globalyzer@0.1.0: {} @@ -3563,7 +4071,7 @@ snapshots: ignore@5.3.2: {} - immutable@4.3.4: + immutable@4.3.7: optional: true import-fresh@3.3.0: @@ -3575,7 +4083,7 @@ snapshots: is-binary-path@2.1.0: dependencies: - binary-extensions: 2.2.0 + binary-extensions: 2.3.0 optional: true is-core-module@2.13.1: @@ -3835,7 +4343,9 @@ snapshots: ms@2.1.2: {} - nanoid@3.3.7: {} + ms@2.1.3: {} + + nanoid@3.3.8: {} natural-compare@1.4.0: {} @@ -3935,6 +4445,8 @@ snapshots: picocolors@1.1.0: {} + picocolors@1.1.1: {} + picomatch@2.3.1: {} picomatch@4.0.2: {} @@ -3954,30 +4466,30 @@ snapshots: '@polka/url': 1.0.0-next.25 trouter: 4.0.0 - postcss-load-config@3.1.4(postcss@8.4.47): + postcss-load-config@3.1.4(postcss@8.5.1): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: - postcss: 8.4.47 + postcss: 8.5.1 - postcss-safe-parser@6.0.0(postcss@8.4.47): + postcss-safe-parser@6.0.0(postcss@8.5.1): dependencies: - postcss: 8.4.47 + postcss: 8.5.1 - postcss-scss@4.0.9(postcss@8.4.47): + postcss-scss@4.0.9(postcss@8.5.1): dependencies: - postcss: 8.4.47 + postcss: 8.5.1 postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss@8.4.47: + postcss@8.5.1: dependencies: - nanoid: 3.3.7 - picocolors: 1.1.0 + nanoid: 3.3.8 + picocolors: 1.1.1 source-map-js: 1.2.1 prelude-ls@1.2.1: {} @@ -4053,6 +4565,31 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.22.4 fsevents: 2.3.3 + rollup@4.34.1: + dependencies: + '@types/estree': 1.0.6 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.34.1 + '@rollup/rollup-android-arm64': 4.34.1 + '@rollup/rollup-darwin-arm64': 4.34.1 + '@rollup/rollup-darwin-x64': 4.34.1 + '@rollup/rollup-freebsd-arm64': 4.34.1 + '@rollup/rollup-freebsd-x64': 4.34.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.34.1 + '@rollup/rollup-linux-arm-musleabihf': 4.34.1 + '@rollup/rollup-linux-arm64-gnu': 4.34.1 + '@rollup/rollup-linux-arm64-musl': 4.34.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.34.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.34.1 + '@rollup/rollup-linux-riscv64-gnu': 4.34.1 + '@rollup/rollup-linux-s390x-gnu': 4.34.1 + '@rollup/rollup-linux-x64-gnu': 4.34.1 + '@rollup/rollup-linux-x64-musl': 4.34.1 + '@rollup/rollup-win32-arm64-msvc': 4.34.1 + '@rollup/rollup-win32-ia32-msvc': 4.34.1 + '@rollup/rollup-win32-x64-msvc': 4.34.1 + fsevents: 2.3.3 + rrweb-cssom@0.7.1: {} run-applescript@7.0.0: {} @@ -4071,8 +4608,8 @@ snapshots: sass@1.70.0: dependencies: - chokidar: 3.5.3 - immutable: 4.3.4 + chokidar: 3.6.0 + immutable: 4.3.7 source-map-js: 1.2.1 optional: true @@ -4082,6 +4619,8 @@ snapshots: semver@7.6.3: {} + semver@7.7.0: {} + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -4168,13 +4707,13 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-eslint-parser@0.41.0(svelte@packages+svelte): + svelte-eslint-parser@0.43.0(svelte@packages+svelte): dependencies: eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - postcss: 8.4.47 - postcss-scss: 4.0.9(postcss@8.4.47) + postcss: 8.5.1 + postcss-scss: 4.0.9(postcss@8.5.1) optionalDependencies: svelte: link:packages/svelte @@ -4285,7 +4824,7 @@ snapshots: debug: 4.3.6 pathe: 1.1.2 tinyrainbow: 1.2.0 - vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - '@types/node' - less @@ -4297,10 +4836,10 @@ snapshots: - supports-color - terser - vite-plugin-inspect@0.8.4(rollup@4.22.4)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vite-plugin-inspect@0.8.4(rollup@4.34.1)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): dependencies: '@antfu/utils': 0.7.8 - '@rollup/pluginutils': 5.1.0(rollup@4.22.4) + '@rollup/pluginutils': 5.1.0(rollup@4.34.1) debug: 4.3.6 error-stack-parser-es: 0.1.1 fs-extra: 11.2.0 @@ -4308,15 +4847,15 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.0 sirv: 2.0.4 - vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - rollup - supports-color - vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): + vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: esbuild: 0.21.5 - postcss: 8.4.47 + postcss: 8.5.1 rollup: 4.22.4 optionalDependencies: '@types/node': 20.12.7 @@ -4325,11 +4864,11 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): + vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: - esbuild: 0.21.5 - postcss: 8.4.47 - rollup: 4.22.4 + esbuild: 0.24.2 + postcss: 8.5.1 + rollup: 4.34.1 optionalDependencies: '@types/node': 20.12.7 fsevents: 2.3.3 @@ -4337,9 +4876,9 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vitefu@0.2.5(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vitefu@0.2.5(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): optionalDependencies: - vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vitest@2.0.5(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: From d39d8c0675fd1ab13fa752b6a57e471ae864f7eb Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Wed, 5 Feb 2025 12:28:18 +0000 Subject: [PATCH 0152/1429] fix: ensure tracking returns true, even if in unowned (#15214) * fix: ensure tracking returns true, even if in unowned * fix: ensure tracking returns true, even if in unowned * Update packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/main.svelte --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- .changeset/long-moles-join.md | 5 +++++ .../src/internal/client/reactivity/effects.js | 8 +------- .../samples/effect-tracking-unowned/_config.js | 16 ++++++++++++++++ .../samples/effect-tracking-unowned/main.svelte | 12 ++++++++++++ 4 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 .changeset/long-moles-join.md create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/main.svelte diff --git a/.changeset/long-moles-join.md b/.changeset/long-moles-join.md new file mode 100644 index 0000000000..92c3d7bf9d --- /dev/null +++ b/.changeset/long-moles-join.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure tracking returns true, even if in unowned diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index eab6c767f8..9d7b5e9de6 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -164,13 +164,7 @@ function create_effect(type, fn, sync, push = true) { * @returns {boolean} */ export function effect_tracking() { - if (active_reaction === null || untracking) { - return false; - } - - // If it's skipped, that's because we're inside an unowned - // that is not being tracked by another reaction - return !skip_reaction; + return active_reaction !== null && !untracking; } /** diff --git a/packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/_config.js new file mode 100644 index 0000000000..749b9997c2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/_config.js @@ -0,0 +1,16 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + const b1 = target.querySelector('button'); + + b1?.click(); + flushSync(); + + assert.htmlEqual( + target.innerHTML, + `<o>Store: new</o><p>Text: new message</p><button>Change Store</button>` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/main.svelte new file mode 100644 index 0000000000..3c16e3c036 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-tracking-unowned/main.svelte @@ -0,0 +1,12 @@ +<script> + import { writable, fromStore, toStore } from "svelte/store"; + + const store = writable("previous"); + let text = $derived(fromStore(store).current + " message"); + + text; // read derived in a non-tracking context +</script> + +<o>Store: {$store}</o> +<p>Text: {text}</p> +<button onclick={() => { store.set("new"); }}>Change Store</button> From ed1cf75b79f0bffbb5122f88a570b91deb744708 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 5 Feb 2025 11:42:24 -0500 Subject: [PATCH 0153/1429] chore: add download/hash scripts for sandbox (#15218) * chore: add download/hash scripts for sandbox * remove logging --- playgrounds/sandbox/index.html | 2 +- playgrounds/sandbox/package.json | 4 +- playgrounds/sandbox/scripts/download.js | 52 +++++++++++++++++++++++++ playgrounds/sandbox/scripts/hash.js | 45 +++++++++++++++++++++ playgrounds/sandbox/ssr-dev.js | 2 +- playgrounds/sandbox/ssr-prod.js | 2 +- 6 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 playgrounds/sandbox/scripts/download.js create mode 100644 playgrounds/sandbox/scripts/hash.js diff --git a/playgrounds/sandbox/index.html b/playgrounds/sandbox/index.html index 512b5426a9..845538abf0 100644 --- a/playgrounds/sandbox/index.html +++ b/playgrounds/sandbox/index.html @@ -12,7 +12,7 @@ <script type="module"> import { mount, hydrate, unmount } from 'svelte'; - import App from '/src/main.svelte'; + import App from '/src/App.svelte'; const root = document.getElementById('root'); const render = root.firstChild?.nextSibling ? hydrate : mount; diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index d9c490ba86..9a52fb4ad6 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -9,7 +9,9 @@ "ssr": "node --conditions=development ./ssr-dev.js", "build": "vite build --outDir dist/client && vite build --outDir dist/server --ssr ssr-prod.js", "prod": "npm run build && node dist/server/ssr-prod", - "preview": "vite preview" + "preview": "vite preview", + "download": "node scripts/download.js", + "hash": "node scripts/hash.js | pbcopy && echo \"copied URL to clipboard\"" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^4.0.0-next.6", diff --git a/playgrounds/sandbox/scripts/download.js b/playgrounds/sandbox/scripts/download.js new file mode 100644 index 0000000000..5678f69ee3 --- /dev/null +++ b/playgrounds/sandbox/scripts/download.js @@ -0,0 +1,52 @@ +import fs from 'node:fs'; + +const arg = process.argv[2]; + +/** @type {URL} */ +let url; + +try { + url = new URL(arg); +} catch (e) { + console.error(`${arg} is not a URL`); + process.exit(1); +} + +if (url.origin !== 'https://svelte.dev' || !url.pathname.startsWith('/playground/')) { + console.error(`${arg} is not a Svelte playground URL`); + process.exit(1); +} + +let files; + +if (url.hash.length > 1) { + const decoded = atob(url.hash.slice(1).replaceAll('-', '+').replaceAll('_', '/')); + // putting it directly into the blob gives a corrupted file + const u8 = new Uint8Array(decoded.length); + for (let i = 0; i < decoded.length; i++) { + u8[i] = decoded.charCodeAt(i); + } + const stream = new Blob([u8]).stream().pipeThrough(new DecompressionStream('gzip')); + const json = await new Response(stream).text(); + + files = JSON.parse(json).files; +} else { + const id = url.pathname.split('/')[2]; + const response = await fetch(`https://svelte.dev/playground/api/${id}.json`); + + files = (await response.json()).components.map((data) => { + const basename = `${data.name}.${data.type}`; + + return { + type: 'file', + name: basename, + basename, + contents: data.source, + text: true + }; + }); +} + +for (const file of files) { + fs.writeFileSync(`src/${file.name}`, file.contents); +} diff --git a/playgrounds/sandbox/scripts/hash.js b/playgrounds/sandbox/scripts/hash.js new file mode 100644 index 0000000000..e70dfc471a --- /dev/null +++ b/playgrounds/sandbox/scripts/hash.js @@ -0,0 +1,45 @@ +import fs from 'node:fs'; + +const files = []; + +for (const basename of fs.readdirSync('src')) { + if (fs.statSync(`src/${basename}`).isDirectory()) continue; + + files.push({ + type: 'file', + name: basename, + basename, + contents: fs.readFileSync(`src/${basename}`, 'utf-8'), + text: true // TODO might not be + }); +} + +const payload = JSON.stringify({ + name: 'sandbox', + files +}); + +async function compress(payload) { + const reader = new Blob([payload]) + .stream() + .pipeThrough(new CompressionStream('gzip')) + .getReader(); + + let buffer = ''; + for (;;) { + const { done, value } = await reader.read(); + + if (done) { + reader.releaseLock(); + return btoa(buffer).replaceAll('+', '-').replaceAll('/', '_'); + } else { + for (let i = 0; i < value.length; i++) { + // decoding as utf-8 will make btoa reject the string + buffer += String.fromCharCode(value[i]); + } + } + } +} + +const hash = await compress(payload); +console.log(`https://svelte.dev/playground/untitled#${hash}`); diff --git a/playgrounds/sandbox/ssr-dev.js b/playgrounds/sandbox/ssr-dev.js index 65390b70ca..01ce14e266 100644 --- a/playgrounds/sandbox/ssr-dev.js +++ b/playgrounds/sandbox/ssr-dev.js @@ -22,7 +22,7 @@ polka() .use(async (req, res) => { const template = fs.readFileSync(path.resolve(__dirname, 'index.html'), 'utf-8'); const transformed_template = await vite.transformIndexHtml(req.url, template); - const { default: App } = await vite.ssrLoadModule('/src/main.svelte'); + const { default: App } = await vite.ssrLoadModule('/src/App.svelte'); const { head, body } = render(App); const html = transformed_template diff --git a/playgrounds/sandbox/ssr-prod.js b/playgrounds/sandbox/ssr-prod.js index c268aac790..1ed9435249 100644 --- a/playgrounds/sandbox/ssr-prod.js +++ b/playgrounds/sandbox/ssr-prod.js @@ -2,7 +2,7 @@ import fs from 'node:fs'; import path from 'node:path'; import polka from 'polka'; import { render } from 'svelte/server'; -import App from './src/main.svelte'; +import App from './src/App.svelte'; const { head, body } = render(App); From 541f8eb29029a415665b6e9cd469c8770ae97ac6 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 5 Feb 2025 11:46:41 -0500 Subject: [PATCH 0154/1429] chore: remove pbcopy from hash script (#15221) --- playgrounds/sandbox/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index 9a52fb4ad6..5011d66727 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -11,7 +11,7 @@ "prod": "npm run build && node dist/server/ssr-prod", "preview": "vite preview", "download": "node scripts/download.js", - "hash": "node scripts/hash.js | pbcopy && echo \"copied URL to clipboard\"" + "hash": "node scripts/hash.js" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^4.0.0-next.6", From 1ada3026037dca8afd789b48653f87e8050d7d8d Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 5 Feb 2025 14:13:20 -0500 Subject: [PATCH 0155/1429] Revert "chore(deps-dev): bump vite from 5.4.14 to 6.0.9 (#15199)" (#15201) This reverts commit f67cf201af118af2091a91adb3082da10e72a170. --- playgrounds/sandbox/package.json | 2 +- pnpm-lock.yaml | 713 ++++--------------------------- 2 files changed, 88 insertions(+), 627 deletions(-) diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index 5011d66727..b5bd8ab2f9 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -18,7 +18,7 @@ "polka": "^1.0.0-next.25", "svelte": "workspace:*", "tiny-glob": "^0.2.9", - "vite": "^6.0.9", + "vite": "^5.4.14", "vite-plugin-inspect": "^0.8.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 31d1e4b816..8288f229cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,7 +152,7 @@ importers: devDependencies: '@sveltejs/vite-plugin-svelte': specifier: ^4.0.0-next.6 - version: 4.0.0-next.6(svelte@packages+svelte)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) polka: specifier: ^1.0.0-next.25 version: 1.0.0-next.25 @@ -163,11 +163,11 @@ importers: specifier: ^0.2.9 version: 0.2.9 vite: - specifier: ^6.0.9 - version: 6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + specifier: ^5.4.14 + version: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vite-plugin-inspect: specifier: ^0.8.4 - version: 0.8.4(rollup@4.34.1)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 0.8.4(rollup@4.22.4)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) packages: @@ -270,308 +270,148 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.24.2': - resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/android-arm64@0.21.5': resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.24.2': - resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm@0.21.5': resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} cpu: [arm] os: [android] - '@esbuild/android-arm@0.24.2': - resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-x64@0.21.5': resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} cpu: [x64] os: [android] - '@esbuild/android-x64@0.24.2': - resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/darwin-arm64@0.21.5': resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.24.2': - resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-x64@0.21.5': resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.24.2': - resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.24.2': - resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.24.2': - resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/linux-arm64@0.21.5': resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.24.2': - resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm@0.21.5': resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.24.2': - resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-ia32@0.21.5': resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.24.2': - resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-loong64@0.21.5': resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.24.2': - resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-mips64el@0.21.5': resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.24.2': - resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-ppc64@0.21.5': resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.24.2': - resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-riscv64@0.21.5': resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.24.2': - resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-s390x@0.21.5': resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.24.2': - resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-x64@0.21.5': resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.24.2': - resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.24.2': - resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-x64@0.21.5': resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.24.2': - resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.24.2': - resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-x64@0.21.5': resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.24.2': - resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/sunos-x64@0.21.5': resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.24.2': - resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/win32-arm64@0.21.5': resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.24.2': - resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-ia32@0.21.5': resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.24.2': - resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-x64@0.21.5': resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.24.2': - resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@eslint-community/eslint-utils@4.4.0': resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/eslint-utils@4.4.1': - resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.11.0': resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.18.0': resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -705,176 +545,81 @@ packages: cpu: [arm] os: [android] - '@rollup/rollup-android-arm-eabi@4.34.1': - resolution: {integrity: sha512-kwctwVlswSEsr4ljpmxKrRKp1eG1v2NAhlzFzDf1x1OdYaMjBYjDCbHkzWm57ZXzTwqn8stMXgROrnMw8dJK3w==} - cpu: [arm] - os: [android] - '@rollup/rollup-android-arm64@4.22.4': resolution: {integrity: sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==} cpu: [arm64] os: [android] - '@rollup/rollup-android-arm64@4.34.1': - resolution: {integrity: sha512-4H5ZtZitBPlbPsTv6HBB8zh1g5d0T8TzCmpndQdqq20Ugle/nroOyDMf9p7f88Gsu8vBLU78/cuh8FYHZqdXxw==} - cpu: [arm64] - os: [android] - '@rollup/rollup-darwin-arm64@4.22.4': resolution: {integrity: sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-arm64@4.34.1': - resolution: {integrity: sha512-f2AJ7Qwx9z25hikXvg+asco8Sfuc5NCLg8rmqQBIOUoWys5sb/ZX9RkMZDPdnnDevXAMJA5AWLnRBmgdXGEUiA==} - cpu: [arm64] - os: [darwin] - '@rollup/rollup-darwin-x64@4.22.4': resolution: {integrity: sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==} cpu: [x64] os: [darwin] - '@rollup/rollup-darwin-x64@4.34.1': - resolution: {integrity: sha512-+/2JBrRfISCsWE4aEFXxd+7k9nWGXA8+wh7ZUHn/u8UDXOU9LN+QYKKhd57sIn6WRcorOnlqPMYFIwie/OHXWw==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.34.1': - resolution: {integrity: sha512-SUeB0pYjIXwT2vfAMQ7E4ERPq9VGRrPR7Z+S4AMssah5EHIilYqjWQoTn5dkDtuIJUSTs8H+C9dwoEcg3b0sCA==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.34.1': - resolution: {integrity: sha512-L3T66wAZiB/ooiPbxz0s6JEX6Sr2+HfgPSK+LMuZkaGZFAFCQAHiP3dbyqovYdNaiUXcl9TlgnIbcsIicAnOZg==} - cpu: [x64] - os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.22.4': resolution: {integrity: sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-gnueabihf@4.34.1': - resolution: {integrity: sha512-UBXdQ4+ATARuFgsFrQ+tAsKvBi/Hly99aSVdeCUiHV9dRTTpMU7OrM3WXGys1l40wKVNiOl0QYY6cZQJ2xhKlQ==} - cpu: [arm] - os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.22.4': resolution: {integrity: sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.34.1': - resolution: {integrity: sha512-m/yfZ25HGdcCSwmopEJm00GP7xAUyVcBPjttGLRAqZ60X/bB4Qn6gP7XTwCIU6bITeKmIhhwZ4AMh2XLro+4+w==} - cpu: [arm] - os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.22.4': resolution: {integrity: sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.34.1': - resolution: {integrity: sha512-Wy+cUmFuvziNL9qWRRzboNprqSQ/n38orbjRvd6byYWridp5TJ3CD+0+HUsbcWVSNz9bxkDUkyASGP0zS7GAvg==} - cpu: [arm64] - os: [linux] - '@rollup/rollup-linux-arm64-musl@4.22.4': resolution: {integrity: sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.34.1': - resolution: {integrity: sha512-CQ3MAGgiFmQW5XJX5W3wnxOBxKwFlUAgSXFA2SwgVRjrIiVt5LHfcQLeNSHKq5OEZwv+VCBwlD1+YKCjDG8cpg==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-loongarch64-gnu@4.34.1': - resolution: {integrity: sha512-rSzb1TsY4lSwH811cYC3OC2O2mzNMhM13vcnA7/0T6Mtreqr3/qs6WMDriMRs8yvHDI54qxHgOk8EV5YRAHFbw==} - cpu: [loong64] - os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': resolution: {integrity: sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.34.1': - resolution: {integrity: sha512-fwr0n6NS0pG3QxxlqVYpfiY64Fd1Dqd8Cecje4ILAV01ROMp4aEdCj5ssHjRY3UwU7RJmeWd5fi89DBqMaTawg==} - cpu: [ppc64] - os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.22.4': resolution: {integrity: sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.34.1': - resolution: {integrity: sha512-4uJb9qz7+Z/yUp5RPxDGGGUcoh0PnKF33QyWgEZ3X/GocpWb6Mb+skDh59FEt5d8+Skxqs9mng6Swa6B2AmQZg==} - cpu: [riscv64] - os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.22.4': resolution: {integrity: sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.34.1': - resolution: {integrity: sha512-QlIo8ndocWBEnfmkYqj8vVtIUpIqJjfqKggjy7IdUncnt8BGixte1wDON7NJEvLg3Kzvqxtbo8tk+U1acYEBlw==} - cpu: [s390x] - os: [linux] - '@rollup/rollup-linux-x64-gnu@4.22.4': resolution: {integrity: sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.34.1': - resolution: {integrity: sha512-hzpleiKtq14GWjz3ahWvJXgU1DQC9DteiwcsY4HgqUJUGxZThlL66MotdUEK9zEo0PK/2ADeZGM9LIondE302A==} - cpu: [x64] - os: [linux] - '@rollup/rollup-linux-x64-musl@4.22.4': resolution: {integrity: sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.34.1': - resolution: {integrity: sha512-jqtKrO715hDlvUcEsPn55tZt2TEiBvBtCMkUuU0R6fO/WPT7lO9AONjPbd8II7/asSiNVQHCMn4OLGigSuxVQA==} - cpu: [x64] - os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.22.4': resolution: {integrity: sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.34.1': - resolution: {integrity: sha512-RnHy7yFf2Wz8Jj1+h8klB93N0NHNHXFhNwAmiy9zJdpY7DE01VbEVtPdrK1kkILeIbHGRJjvfBDBhnxBr8kD4g==} - cpu: [arm64] - os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.22.4': resolution: {integrity: sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.34.1': - resolution: {integrity: sha512-i7aT5HdiZIcd7quhzvwQ2oAuX7zPYrYfkrd1QFfs28Po/i0q6kas/oRrzGlDhAEyug+1UfUtkWdmoVlLJj5x9Q==} - cpu: [ia32] - os: [win32] - '@rollup/rollup-win32-x64-msvc@4.22.4': resolution: {integrity: sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.34.1': - resolution: {integrity: sha512-k3MVFD9Oq+laHkw2N2v7ILgoa9017ZMF/inTtHzyTVZjYs9cSH18sdyAf6spBAJIGwJ5UaC7et2ZH1WCdlhkMw==} - cpu: [x64] - os: [win32] - '@stylistic/eslint-plugin-js@1.8.0': resolution: {integrity: sha512-jdvnzt+pZPg8TfclZlTZPiUbbima93ylvQ+wNgHLNmup3obY6heQvgewSu9i2CfS61BnRByv+F9fxQLPoNeHag==} engines: {node: ^16.0.0 || >=18.0.0} @@ -1033,11 +778,6 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} - engines: {node: '>=0.4.0'} - hasBin: true - agent-base@7.1.1: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} @@ -1101,8 +841,8 @@ packages: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} engines: {node: '>=8'} brace-expansion@1.1.11: @@ -1115,10 +855,6 @@ packages: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -1149,8 +885,8 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} ci-info@3.9.0: @@ -1213,15 +949,6 @@ packages: supports-color: optional: true - debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} @@ -1284,8 +1011,8 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - enhanced-resolve@5.18.0: - resolution: {integrity: sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==} + enhanced-resolve@5.17.1: + resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} engines: {node: '>=10.13.0'} enquirer@2.4.1: @@ -1304,11 +1031,6 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.24.2: - resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} - engines: {node: '>=18'} - hasBin: true - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -1460,10 +1182,6 @@ packages: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -1519,8 +1237,8 @@ packages: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} - get-tsconfig@4.10.0: - resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} + get-tsconfig@4.7.6: + resolution: {integrity: sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -1538,10 +1256,6 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@15.14.0: - resolution: {integrity: sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==} - engines: {node: '>=18'} - globals@15.9.0: resolution: {integrity: sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==} engines: {node: '>=18'} @@ -1604,8 +1318,8 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - immutable@4.3.7: - resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} + immutable@4.3.4: + resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==} import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} @@ -1891,11 +1605,8 @@ packages: ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -2018,9 +1729,6 @@ packages: picocolors@1.1.0: resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -2075,8 +1783,8 @@ packages: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} - postcss@8.5.1: - resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} + postcss@8.4.47: + resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -2151,11 +1859,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rollup@4.34.1: - resolution: {integrity: sha512-iYZ/+PcdLYSGfH3S+dGahlW/RWmsqDhLgj1BT9DH/xXJ0ggZN7xkdP9wipPNjjNLczI+fmMLmTB9pye+d2r4GQ==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - rrweb-cssom@0.7.1: resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} @@ -2190,11 +1893,6 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.7.0: - resolution: {integrity: sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==} - engines: {node: '>=10'} - hasBin: true - serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -2298,11 +1996,11 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte-eslint-parser@0.43.0: - resolution: {integrity: sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==} + svelte-eslint-parser@0.41.0: + resolution: {integrity: sha512-L6f4hOL+AbgfBIB52Z310pg1d2QjRqm7wy3kI1W6hhdhX5bvu7+f0R6w4ykp5HoDdzq+vGhIJmsisaiJDGmVfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0-next.191 peerDependenciesMeta: svelte: optional: true @@ -2446,8 +2144,8 @@ packages: '@nuxt/kit': optional: true - vite@5.4.6: - resolution: {integrity: sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==} + vite@5.4.14: + resolution: {integrity: sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -2477,27 +2175,22 @@ packages: terser: optional: true - vite@6.0.9: - resolution: {integrity: sha512-MSgUxHcaXLtnBPktkbUSoQUANApKYuxZ6DrbVENlIorbhL2dZydTLaZ01tjUoE3szeFzlFk9ANOKk0xurh4MKA==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vite@5.4.6: + resolution: {integrity: sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==} + engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - jiti: '>=1.21.0' + '@types/node': ^18.0.0 || >=20.0.0 less: '*' lightningcss: ^1.21.0 sass: '*' sass-embedded: '*' stylus: '*' sugarss: '*' - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 + terser: ^5.4.0 peerDependenciesMeta: '@types/node': optional: true - jiti: - optional: true less: optional: true lightningcss: @@ -2512,10 +2205,6 @@ packages: optional: true terser: optional: true - tsx: - optional: true - yaml: - optional: true vitefu@0.2.5: resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} @@ -2816,161 +2505,79 @@ snapshots: '@esbuild/aix-ppc64@0.21.5': optional: true - '@esbuild/aix-ppc64@0.24.2': - optional: true - '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm64@0.24.2': - optional: true - '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-arm@0.24.2': - optional: true - '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/android-x64@0.24.2': - optional: true - '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.24.2': - optional: true - '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/darwin-x64@0.24.2': - optional: true - '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.24.2': - optional: true - '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.24.2': - optional: true - '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm64@0.24.2': - optional: true - '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-arm@0.24.2': - optional: true - '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-ia32@0.24.2': - optional: true - '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-loong64@0.24.2': - optional: true - '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-mips64el@0.24.2': - optional: true - '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-ppc64@0.24.2': - optional: true - '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.24.2': - optional: true - '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-s390x@0.24.2': - optional: true - '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/linux-x64@0.24.2': - optional: true - - '@esbuild/netbsd-arm64@0.24.2': - optional: true - '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.24.2': - optional: true - - '@esbuild/openbsd-arm64@0.24.2': - optional: true - '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.24.2': - optional: true - '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.24.2': - optional: true - '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-arm64@0.24.2': - optional: true - '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-ia32@0.24.2': - optional: true - '@esbuild/win32-x64@0.21.5': optional: true - '@esbuild/win32-x64@0.24.2': - optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@9.9.1)': dependencies: eslint: 9.9.1 eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.4.1(eslint@9.9.1)': - dependencies: - eslint: 9.9.1 - eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.11.0': {} - '@eslint-community/regexpp@4.12.1': {} - '@eslint/config-array@0.18.0': dependencies: '@eslint/object-schema': 2.1.4 @@ -3113,123 +2720,58 @@ snapshots: optionalDependencies: rollup: 4.22.4 - '@rollup/pluginutils@5.1.0(rollup@4.34.1)': - dependencies: - '@types/estree': 1.0.6 - estree-walker: 2.0.2 - picomatch: 2.3.1 - optionalDependencies: - rollup: 4.34.1 - '@rollup/rollup-android-arm-eabi@4.22.4': optional: true - '@rollup/rollup-android-arm-eabi@4.34.1': - optional: true - '@rollup/rollup-android-arm64@4.22.4': optional: true - '@rollup/rollup-android-arm64@4.34.1': - optional: true - '@rollup/rollup-darwin-arm64@4.22.4': optional: true - '@rollup/rollup-darwin-arm64@4.34.1': - optional: true - '@rollup/rollup-darwin-x64@4.22.4': optional: true - '@rollup/rollup-darwin-x64@4.34.1': - optional: true - - '@rollup/rollup-freebsd-arm64@4.34.1': - optional: true - - '@rollup/rollup-freebsd-x64@4.34.1': - optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.22.4': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.34.1': - optional: true - '@rollup/rollup-linux-arm-musleabihf@4.22.4': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.34.1': - optional: true - '@rollup/rollup-linux-arm64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-arm64-gnu@4.34.1': - optional: true - '@rollup/rollup-linux-arm64-musl@4.22.4': optional: true - '@rollup/rollup-linux-arm64-musl@4.34.1': - optional: true - - '@rollup/rollup-linux-loongarch64-gnu@4.34.1': - optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.34.1': - optional: true - '@rollup/rollup-linux-riscv64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.34.1': - optional: true - '@rollup/rollup-linux-s390x-gnu@4.22.4': optional: true - '@rollup/rollup-linux-s390x-gnu@4.34.1': - optional: true - '@rollup/rollup-linux-x64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-x64-gnu@4.34.1': - optional: true - '@rollup/rollup-linux-x64-musl@4.22.4': optional: true - '@rollup/rollup-linux-x64-musl@4.34.1': - optional: true - '@rollup/rollup-win32-arm64-msvc@4.22.4': optional: true - '@rollup/rollup-win32-arm64-msvc@4.34.1': - optional: true - '@rollup/rollup-win32-ia32-msvc@4.22.4': optional: true - '@rollup/rollup-win32-ia32-msvc@4.34.1': - optional: true - '@rollup/rollup-win32-x64-msvc@4.22.4': optional: true - '@rollup/rollup-win32-x64-msvc@4.34.1': - optional: true - '@stylistic/eslint-plugin-js@1.8.0(eslint@9.9.1)': dependencies: '@types/eslint': 8.56.12 - acorn: 8.14.0 + acorn: 8.12.1 escape-string-regexp: 4.0.0 eslint: 9.9.1 eslint-visitor-keys: 3.4.3 @@ -3246,25 +2788,25 @@ snapshots: typescript: 5.5.4 typescript-eslint: 8.2.0(eslint@9.9.1)(typescript@5.5.4) - '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.3.6 svelte: link:packages/svelte - vite: 6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.3.6 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.11 svelte: link:packages/svelte - vite: 6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) - vitefu: 0.2.5(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vitefu: 0.2.5(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) transitivePeerDependencies: - supports-color @@ -3434,18 +2976,12 @@ snapshots: dependencies: acorn: 8.12.1 - acorn-jsx@5.3.2(acorn@8.14.0): - dependencies: - acorn: 8.14.0 - acorn-typescript@1.4.13(acorn@8.12.1): dependencies: acorn: 8.12.1 acorn@8.12.1: {} - acorn@8.14.0: {} - agent-base@7.1.1: dependencies: debug: 4.3.6 @@ -3499,7 +3035,7 @@ snapshots: dependencies: is-windows: 1.0.2 - binary-extensions@2.3.0: + binary-extensions@2.2.0: optional: true brace-expansion@1.1.11: @@ -3515,11 +3051,6 @@ snapshots: dependencies: fill-range: 7.0.1 - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - optional: true - buffer-from@1.1.2: {} bundle-name@4.1.0: @@ -3547,10 +3078,10 @@ snapshots: check-error@2.1.1: {} - chokidar@3.6.0: + chokidar@3.5.3: dependencies: anymatch: 3.1.3 - braces: 3.0.3 + braces: 3.0.2 glob-parent: 5.1.2 is-binary-path: 2.1.0 is-glob: 4.0.3 @@ -3609,10 +3140,6 @@ snapshots: dependencies: ms: 2.1.2 - debug@4.4.0: - dependencies: - ms: 2.1.3 - decimal.js@10.4.3: {} deep-eql@5.0.2: {} @@ -3662,7 +3189,7 @@ snapshots: emoji-regex@9.2.2: {} - enhanced-resolve@5.18.0: + enhanced-resolve@5.17.1: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 @@ -3702,40 +3229,12 @@ snapshots: '@esbuild/win32-ia32': 0.21.5 '@esbuild/win32-x64': 0.21.5 - esbuild@0.24.2: - optionalDependencies: - '@esbuild/aix-ppc64': 0.24.2 - '@esbuild/android-arm': 0.24.2 - '@esbuild/android-arm64': 0.24.2 - '@esbuild/android-x64': 0.24.2 - '@esbuild/darwin-arm64': 0.24.2 - '@esbuild/darwin-x64': 0.24.2 - '@esbuild/freebsd-arm64': 0.24.2 - '@esbuild/freebsd-x64': 0.24.2 - '@esbuild/linux-arm': 0.24.2 - '@esbuild/linux-arm64': 0.24.2 - '@esbuild/linux-ia32': 0.24.2 - '@esbuild/linux-loong64': 0.24.2 - '@esbuild/linux-mips64el': 0.24.2 - '@esbuild/linux-ppc64': 0.24.2 - '@esbuild/linux-riscv64': 0.24.2 - '@esbuild/linux-s390x': 0.24.2 - '@esbuild/linux-x64': 0.24.2 - '@esbuild/netbsd-arm64': 0.24.2 - '@esbuild/netbsd-x64': 0.24.2 - '@esbuild/openbsd-arm64': 0.24.2 - '@esbuild/openbsd-x64': 0.24.2 - '@esbuild/sunos-x64': 0.24.2 - '@esbuild/win32-arm64': 0.24.2 - '@esbuild/win32-ia32': 0.24.2 - '@esbuild/win32-x64': 0.24.2 - escape-string-regexp@4.0.0: {} eslint-compat-utils@0.5.1(eslint@9.9.1): dependencies: eslint: 9.9.1 - semver: 7.7.0 + semver: 7.6.3 eslint-config-prettier@9.1.0(eslint@9.9.1): dependencies: @@ -3743,8 +3242,8 @@ snapshots: eslint-plugin-es-x@7.8.0(eslint@9.9.1): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) - '@eslint-community/regexpp': 4.12.1 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1) + '@eslint-community/regexpp': 4.11.0 eslint: 9.9.1 eslint-compat-utils: 0.5.1(eslint@9.9.1) @@ -3752,31 +3251,31 @@ snapshots: eslint-plugin-n@17.9.0(eslint@9.9.1): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) - enhanced-resolve: 5.18.0 + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1) + enhanced-resolve: 5.17.1 eslint: 9.9.1 eslint-plugin-es-x: 7.8.0(eslint@9.9.1) - get-tsconfig: 4.10.0 - globals: 15.14.0 + get-tsconfig: 4.7.6 + globals: 15.9.0 ignore: 5.3.2 minimatch: 9.0.5 - semver: 7.7.0 + semver: 7.6.3 eslint-plugin-svelte@2.38.0(eslint@9.9.1)(svelte@packages+svelte): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) + '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1) '@jridgewell/sourcemap-codec': 1.5.0 - debug: 4.4.0 + debug: 4.3.6 eslint: 9.9.1 eslint-compat-utils: 0.5.1(eslint@9.9.1) esutils: 2.0.3 known-css-properties: 0.30.0 - postcss: 8.5.1 - postcss-load-config: 3.1.4(postcss@8.5.1) - postcss-safe-parser: 6.0.0(postcss@8.5.1) + postcss: 8.4.47 + postcss-load-config: 3.1.4(postcss@8.4.47) + postcss-safe-parser: 6.0.0(postcss@8.4.47) postcss-selector-parser: 6.1.2 - semver: 7.7.0 - svelte-eslint-parser: 0.43.0(svelte@packages+svelte) + semver: 7.6.3 + svelte-eslint-parser: 0.41.0(svelte@packages+svelte) optionalDependencies: svelte: link:packages/svelte transitivePeerDependencies: @@ -3846,8 +3345,8 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) + acorn: 8.12.1 + acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} @@ -3924,11 +3423,6 @@ snapshots: dependencies: to-regex-range: 5.0.1 - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - optional: true - find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -3987,7 +3481,7 @@ snapshots: get-stream@8.0.1: {} - get-tsconfig@4.10.0: + get-tsconfig@4.7.6: dependencies: resolve-pkg-maps: 1.0.0 @@ -4010,8 +3504,6 @@ snapshots: globals@14.0.0: {} - globals@15.14.0: {} - globals@15.9.0: {} globalyzer@0.1.0: {} @@ -4071,7 +3563,7 @@ snapshots: ignore@5.3.2: {} - immutable@4.3.7: + immutable@4.3.4: optional: true import-fresh@3.3.0: @@ -4083,7 +3575,7 @@ snapshots: is-binary-path@2.1.0: dependencies: - binary-extensions: 2.3.0 + binary-extensions: 2.2.0 optional: true is-core-module@2.13.1: @@ -4343,9 +3835,7 @@ snapshots: ms@2.1.2: {} - ms@2.1.3: {} - - nanoid@3.3.8: {} + nanoid@3.3.7: {} natural-compare@1.4.0: {} @@ -4445,8 +3935,6 @@ snapshots: picocolors@1.1.0: {} - picocolors@1.1.1: {} - picomatch@2.3.1: {} picomatch@4.0.2: {} @@ -4466,30 +3954,30 @@ snapshots: '@polka/url': 1.0.0-next.25 trouter: 4.0.0 - postcss-load-config@3.1.4(postcss@8.5.1): + postcss-load-config@3.1.4(postcss@8.4.47): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: - postcss: 8.5.1 + postcss: 8.4.47 - postcss-safe-parser@6.0.0(postcss@8.5.1): + postcss-safe-parser@6.0.0(postcss@8.4.47): dependencies: - postcss: 8.5.1 + postcss: 8.4.47 - postcss-scss@4.0.9(postcss@8.5.1): + postcss-scss@4.0.9(postcss@8.4.47): dependencies: - postcss: 8.5.1 + postcss: 8.4.47 postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss@8.5.1: + postcss@8.4.47: dependencies: - nanoid: 3.3.8 - picocolors: 1.1.1 + nanoid: 3.3.7 + picocolors: 1.1.0 source-map-js: 1.2.1 prelude-ls@1.2.1: {} @@ -4565,31 +4053,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.22.4 fsevents: 2.3.3 - rollup@4.34.1: - dependencies: - '@types/estree': 1.0.6 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.34.1 - '@rollup/rollup-android-arm64': 4.34.1 - '@rollup/rollup-darwin-arm64': 4.34.1 - '@rollup/rollup-darwin-x64': 4.34.1 - '@rollup/rollup-freebsd-arm64': 4.34.1 - '@rollup/rollup-freebsd-x64': 4.34.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.34.1 - '@rollup/rollup-linux-arm-musleabihf': 4.34.1 - '@rollup/rollup-linux-arm64-gnu': 4.34.1 - '@rollup/rollup-linux-arm64-musl': 4.34.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.34.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.34.1 - '@rollup/rollup-linux-riscv64-gnu': 4.34.1 - '@rollup/rollup-linux-s390x-gnu': 4.34.1 - '@rollup/rollup-linux-x64-gnu': 4.34.1 - '@rollup/rollup-linux-x64-musl': 4.34.1 - '@rollup/rollup-win32-arm64-msvc': 4.34.1 - '@rollup/rollup-win32-ia32-msvc': 4.34.1 - '@rollup/rollup-win32-x64-msvc': 4.34.1 - fsevents: 2.3.3 - rrweb-cssom@0.7.1: {} run-applescript@7.0.0: {} @@ -4608,8 +4071,8 @@ snapshots: sass@1.70.0: dependencies: - chokidar: 3.6.0 - immutable: 4.3.7 + chokidar: 3.5.3 + immutable: 4.3.4 source-map-js: 1.2.1 optional: true @@ -4619,8 +4082,6 @@ snapshots: semver@7.6.3: {} - semver@7.7.0: {} - serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -4707,13 +4168,13 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-eslint-parser@0.43.0(svelte@packages+svelte): + svelte-eslint-parser@0.41.0(svelte@packages+svelte): dependencies: eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - postcss: 8.5.1 - postcss-scss: 4.0.9(postcss@8.5.1) + postcss: 8.4.47 + postcss-scss: 4.0.9(postcss@8.4.47) optionalDependencies: svelte: link:packages/svelte @@ -4824,7 +4285,7 @@ snapshots: debug: 4.3.6 pathe: 1.1.2 tinyrainbow: 1.2.0 - vite: 5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - '@types/node' - less @@ -4836,10 +4297,10 @@ snapshots: - supports-color - terser - vite-plugin-inspect@0.8.4(rollup@4.34.1)(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vite-plugin-inspect@0.8.4(rollup@4.22.4)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): dependencies: '@antfu/utils': 0.7.8 - '@rollup/pluginutils': 5.1.0(rollup@4.34.1) + '@rollup/pluginutils': 5.1.0(rollup@4.22.4) debug: 4.3.6 error-stack-parser-es: 0.1.1 fs-extra: 11.2.0 @@ -4847,15 +4308,15 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.0 sirv: 2.0.4 - vite: 6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - rollup - supports-color - vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): + vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: esbuild: 0.21.5 - postcss: 8.5.1 + postcss: 8.4.47 rollup: 4.22.4 optionalDependencies: '@types/node': 20.12.7 @@ -4864,11 +4325,11 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): + vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: - esbuild: 0.24.2 - postcss: 8.5.1 - rollup: 4.34.1 + esbuild: 0.21.5 + postcss: 8.4.47 + rollup: 4.22.4 optionalDependencies: '@types/node': 20.12.7 fsevents: 2.3.3 @@ -4876,9 +4337,9 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vitefu@0.2.5(vite@6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vitefu@0.2.5(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): optionalDependencies: - vite: 6.0.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vitest@2.0.5(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: From 872ee51378433f628dbde3866129b803f8269f2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2025 14:18:59 -0500 Subject: [PATCH 0156/1429] chore(deps-dev): bump vitest from 2.0.5 to 2.1.9 (#15220) Bumps [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) from 2.0.5 to 2.1.9. - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v2.1.9/packages/vitest) --- updated-dependencies: - dependency-name: vitest dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- packages/svelte/package.json | 2 +- pnpm-lock.yaml | 530 ++++++++++++++++++----------------- 3 files changed, 279 insertions(+), 255 deletions(-) diff --git a/package.json b/package.json index 57dc4cdebe..efbee35c08 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,6 @@ "typescript": "^5.5.4", "typescript-eslint": "^8.2.0", "v8-natives": "^1.2.5", - "vitest": "^2.0.5" + "vitest": "^2.1.9" } } diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 8ea495af99..a9d42379cc 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -143,7 +143,7 @@ "source-map": "^0.7.4", "tiny-glob": "^0.2.9", "typescript": "^5.5.4", - "vitest": "^2.0.5" + "vitest": "^2.1.9" }, "dependencies": { "@ampproject/remapping": "^2.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8288f229cb..f7700cd65f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,7 +22,7 @@ importers: version: 20.12.7 '@vitest/coverage-v8': specifier: ^2.0.5 - version: 2.0.5(vitest@2.0.5(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 2.0.5(vitest@2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) eslint: specifier: ^9.9.1 version: 9.9.1 @@ -54,8 +54,8 @@ importers: specifier: ^1.2.5 version: 1.2.5 vitest: - specifier: ^2.0.5 - version: 2.0.5(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + specifier: ^2.1.9 + version: 2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) packages/svelte: dependencies: @@ -145,8 +145,8 @@ importers: specifier: ^5.5.4 version: 5.5.4 vitest: - specifier: ^2.0.5 - version: 2.0.5(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + specifier: ^2.1.9 + version: 2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) playgrounds/sandbox: devDependencies: @@ -408,10 +408,20 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.4.1': + resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.11.0': resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint/config-array@0.18.0': resolution: {integrity: sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -745,23 +755,34 @@ packages: peerDependencies: vitest: 2.0.5 - '@vitest/expect@2.0.5': - resolution: {integrity: sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==} + '@vitest/expect@2.1.9': + resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} + + '@vitest/mocker@2.1.9': + resolution: {integrity: sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true - '@vitest/pretty-format@2.0.5': - resolution: {integrity: sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==} + '@vitest/pretty-format@2.1.9': + resolution: {integrity: sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==} - '@vitest/runner@2.0.5': - resolution: {integrity: sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==} + '@vitest/runner@2.1.9': + resolution: {integrity: sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==} - '@vitest/snapshot@2.0.5': - resolution: {integrity: sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==} + '@vitest/snapshot@2.1.9': + resolution: {integrity: sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==} - '@vitest/spy@2.0.5': - resolution: {integrity: sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==} + '@vitest/spy@2.1.9': + resolution: {integrity: sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==} - '@vitest/utils@2.0.5': - resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} + '@vitest/utils@2.1.9': + resolution: {integrity: sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} @@ -778,6 +799,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@7.1.1: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} @@ -841,8 +867,8 @@ packages: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} - binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} brace-expansion@1.1.11: @@ -855,6 +881,10 @@ packages: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} @@ -870,8 +900,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - chai@5.1.1: - resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} + chai@5.1.2: + resolution: {integrity: sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==} engines: {node: '>=12'} chalk@4.1.2: @@ -885,8 +915,8 @@ packages: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} - chokidar@3.5.3: - resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} ci-info@3.9.0: @@ -949,6 +979,15 @@ packages: supports-color: optional: true + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} @@ -1011,8 +1050,8 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - enhanced-resolve@5.17.1: - resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} + enhanced-resolve@5.18.1: + resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} engines: {node: '>=10.13.0'} enquirer@2.4.1: @@ -1026,6 +1065,9 @@ packages: error-stack-parser-es@0.1.1: resolution: {integrity: sha512-g/9rfnvnagiNf+DRMHEVGuGuIBlCIMDFoTA616HaP2l9PlCjGjVhD98PNbVSJvmK4TttqT5mV5tInMhoFgi+aA==} + es-module-lexer@1.6.0: + resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -1139,9 +1181,9 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} + expect-type@1.1.0: + resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==} + engines: {node: '>=12.0.0'} extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} @@ -1182,6 +1224,10 @@ packages: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -1230,15 +1276,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - - get-tsconfig@4.7.6: - resolution: {integrity: sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==} + get-tsconfig@4.10.0: + resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -1256,6 +1295,10 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} + globals@15.14.0: + resolution: {integrity: sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==} + engines: {node: '>=18'} + globals@15.9.0: resolution: {integrity: sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==} engines: {node: '>=18'} @@ -1302,10 +1345,6 @@ packages: human-id@1.0.2: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -1318,8 +1357,8 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - immutable@4.3.4: - resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==} + immutable@4.3.7: + resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} @@ -1378,10 +1417,6 @@ packages: is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-subdir@1.2.0: resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} engines: {node: '>=4'} @@ -1541,8 +1576,8 @@ packages: lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - loupe@3.1.1: - resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} + loupe@3.1.3: + resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -1553,6 +1588,9 @@ packages: magic-string@0.30.11: resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magicast@0.3.4: resolution: {integrity: sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==} @@ -1560,9 +1598,6 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1579,10 +1614,6 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -1605,11 +1636,19 @@ packages: ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -1626,17 +1665,9 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - npm-run-path@5.2.0: - resolution: {integrity: sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - nwsapi@2.2.12: resolution: {integrity: sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==} - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - open@10.1.0: resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} engines: {node: '>=18'} @@ -1701,10 +1732,6 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -1729,6 +1756,9 @@ packages: picocolors@1.1.0: resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -1787,6 +1817,10 @@ packages: resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.1: + resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} + engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -1893,6 +1927,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -1960,6 +1999,9 @@ packages: std-env@3.7.0: resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + std-env@3.8.0: + resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -1980,10 +2022,6 @@ packages: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -1996,11 +2034,11 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte-eslint-parser@0.41.0: - resolution: {integrity: sha512-L6f4hOL+AbgfBIB52Z310pg1d2QjRqm7wy3kI1W6hhdhX5bvu7+f0R6w4ykp5HoDdzq+vGhIJmsisaiJDGmVfA==} + svelte-eslint-parser@0.43.0: + resolution: {integrity: sha512-GpU52uPKKcVnh8tKN5P4UZpJ/fUDndmq7wfsvoVXsyP+aY0anol7Yqo01fyrlaWGMFfm4av5DyrjlaXdLRJvGA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - svelte: ^3.37.0 || ^4.0.0 || ^5.0.0-next.191 + svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 peerDependenciesMeta: svelte: optional: true @@ -2034,16 +2072,19 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinypool@1.0.1: - resolution: {integrity: sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==} + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinypool@1.0.2: + resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} engines: {node: ^18.0.0 || >=20.0.0} tinyrainbow@1.2.0: resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} engines: {node: '>=14.0.0'} - tinyspy@3.0.0: - resolution: {integrity: sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==} + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} engines: {node: '>=14.0.0'} tldts-core@6.1.64: @@ -2129,8 +2170,8 @@ packages: resolution: {integrity: sha512-CVNliz6KF2yet3HBIkbFJKZmjlt95C8dsNZDnwoS6X98+QJRpsSz9uxo3TziBqdyJQkWwfD3VG9lRzsQNvF24Q==} engines: {node: '>= 0.6.0'} - vite-node@2.0.5: - resolution: {integrity: sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==} + vite-node@2.1.9: + resolution: {integrity: sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true @@ -2175,37 +2216,6 @@ packages: terser: optional: true - vite@5.4.6: - resolution: {integrity: sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - vitefu@0.2.5: resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} peerDependencies: @@ -2214,15 +2224,15 @@ packages: vite: optional: true - vitest@2.0.5: - resolution: {integrity: sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==} + vitest@2.1.9: + resolution: {integrity: sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.0.5 - '@vitest/ui': 2.0.5 + '@vitest/browser': 2.1.9 + '@vitest/ui': 2.1.9 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -2576,12 +2586,19 @@ snapshots: eslint: 9.9.1 eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.4.1(eslint@9.9.1)': + dependencies: + eslint: 9.9.1 + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.11.0': {} + '@eslint-community/regexpp@4.12.1': {} + '@eslint/config-array@0.18.0': dependencies: '@eslint/object-schema': 2.1.4 - debug: 4.3.6 + debug: 4.4.0 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -2589,7 +2606,7 @@ snapshots: '@eslint/eslintrc@3.1.0': dependencies: ajv: 6.12.6 - debug: 4.3.6 + debug: 4.4.0 espree: 10.1.0 globals: 14.0.0 ignore: 5.3.2 @@ -2771,7 +2788,7 @@ snapshots: '@stylistic/eslint-plugin-js@1.8.0(eslint@9.9.1)': dependencies: '@types/eslint': 8.56.12 - acorn: 8.12.1 + acorn: 8.14.0 escape-string-regexp: 4.0.0 eslint: 9.9.1 eslint-visitor-keys: 3.4.3 @@ -2864,7 +2881,7 @@ snapshots: '@typescript-eslint/types': 8.2.0 '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) '@typescript-eslint/visitor-keys': 8.2.0 - debug: 4.3.6 + debug: 4.4.0 eslint: 9.9.1 optionalDependencies: typescript: 5.5.4 @@ -2880,7 +2897,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) '@typescript-eslint/utils': 8.2.0(eslint@9.9.1)(typescript@5.5.4) - debug: 4.3.6 + debug: 4.4.0 ts-api-utils: 1.3.0(typescript@5.5.4) optionalDependencies: typescript: 5.5.4 @@ -2894,7 +2911,7 @@ snapshots: dependencies: '@typescript-eslint/types': 8.2.0 '@typescript-eslint/visitor-keys': 8.2.0 - debug: 4.3.6 + debug: 4.4.0 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 @@ -2921,7 +2938,7 @@ snapshots: '@typescript-eslint/types': 8.2.0 eslint-visitor-keys: 3.4.3 - '@vitest/coverage-v8@2.0.5(vitest@2.0.5(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@vitest/coverage-v8@2.0.5(vitest@2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -2935,56 +2952,69 @@ snapshots: std-env: 3.7.0 test-exclude: 7.0.1 tinyrainbow: 1.2.0 - vitest: 2.0.5(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vitest: 2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - supports-color - '@vitest/expect@2.0.5': + '@vitest/expect@2.1.9': dependencies: - '@vitest/spy': 2.0.5 - '@vitest/utils': 2.0.5 - chai: 5.1.1 + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.1.2 tinyrainbow: 1.2.0 - '@vitest/pretty-format@2.0.5': + '@vitest/mocker@2.1.9(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + dependencies: + '@vitest/spy': 2.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + + '@vitest/pretty-format@2.1.9': dependencies: tinyrainbow: 1.2.0 - '@vitest/runner@2.0.5': + '@vitest/runner@2.1.9': dependencies: - '@vitest/utils': 2.0.5 + '@vitest/utils': 2.1.9 pathe: 1.1.2 - '@vitest/snapshot@2.0.5': + '@vitest/snapshot@2.1.9': dependencies: - '@vitest/pretty-format': 2.0.5 - magic-string: 0.30.11 + '@vitest/pretty-format': 2.1.9 + magic-string: 0.30.17 pathe: 1.1.2 - '@vitest/spy@2.0.5': + '@vitest/spy@2.1.9': dependencies: - tinyspy: 3.0.0 + tinyspy: 3.0.2 - '@vitest/utils@2.0.5': + '@vitest/utils@2.1.9': dependencies: - '@vitest/pretty-format': 2.0.5 - estree-walker: 3.0.3 - loupe: 3.1.1 + '@vitest/pretty-format': 2.1.9 + loupe: 3.1.3 tinyrainbow: 1.2.0 acorn-jsx@5.3.2(acorn@8.12.1): dependencies: acorn: 8.12.1 + acorn-jsx@5.3.2(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + acorn-typescript@1.4.13(acorn@8.12.1): dependencies: acorn: 8.12.1 acorn@8.12.1: {} + acorn@8.14.0: {} + agent-base@7.1.1: dependencies: - debug: 4.3.6 + debug: 4.4.0 transitivePeerDependencies: - supports-color @@ -3035,7 +3065,7 @@ snapshots: dependencies: is-windows: 1.0.2 - binary-extensions@2.2.0: + binary-extensions@2.3.0: optional: true brace-expansion@1.1.11: @@ -3051,6 +3081,11 @@ snapshots: dependencies: fill-range: 7.0.1 + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + optional: true + buffer-from@1.1.2: {} bundle-name@4.1.0: @@ -3061,12 +3096,12 @@ snapshots: callsites@3.1.0: {} - chai@5.1.1: + chai@5.1.2: dependencies: assertion-error: 2.0.1 check-error: 2.1.1 deep-eql: 5.0.2 - loupe: 3.1.1 + loupe: 3.1.3 pathval: 2.0.0 chalk@4.1.2: @@ -3078,10 +3113,10 @@ snapshots: check-error@2.1.1: {} - chokidar@3.5.3: + chokidar@3.6.0: dependencies: anymatch: 3.1.3 - braces: 3.0.2 + braces: 3.0.3 glob-parent: 5.1.2 is-binary-path: 2.1.0 is-glob: 4.0.3 @@ -3140,6 +3175,10 @@ snapshots: dependencies: ms: 2.1.2 + debug@4.4.0: + dependencies: + ms: 2.1.3 + decimal.js@10.4.3: {} deep-eql@5.0.2: {} @@ -3189,7 +3228,7 @@ snapshots: emoji-regex@9.2.2: {} - enhanced-resolve@5.17.1: + enhanced-resolve@5.18.1: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 @@ -3203,6 +3242,8 @@ snapshots: error-stack-parser-es@0.1.1: {} + es-module-lexer@1.6.0: {} + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -3234,7 +3275,7 @@ snapshots: eslint-compat-utils@0.5.1(eslint@9.9.1): dependencies: eslint: 9.9.1 - semver: 7.6.3 + semver: 7.7.1 eslint-config-prettier@9.1.0(eslint@9.9.1): dependencies: @@ -3242,8 +3283,8 @@ snapshots: eslint-plugin-es-x@7.8.0(eslint@9.9.1): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1) - '@eslint-community/regexpp': 4.11.0 + '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) + '@eslint-community/regexpp': 4.12.1 eslint: 9.9.1 eslint-compat-utils: 0.5.1(eslint@9.9.1) @@ -3251,31 +3292,31 @@ snapshots: eslint-plugin-n@17.9.0(eslint@9.9.1): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1) - enhanced-resolve: 5.17.1 + '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) + enhanced-resolve: 5.18.1 eslint: 9.9.1 eslint-plugin-es-x: 7.8.0(eslint@9.9.1) - get-tsconfig: 4.7.6 - globals: 15.9.0 + get-tsconfig: 4.10.0 + globals: 15.14.0 ignore: 5.3.2 minimatch: 9.0.5 - semver: 7.6.3 + semver: 7.7.1 eslint-plugin-svelte@2.38.0(eslint@9.9.1)(svelte@packages+svelte): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) '@jridgewell/sourcemap-codec': 1.5.0 - debug: 4.3.6 + debug: 4.4.0 eslint: 9.9.1 eslint-compat-utils: 0.5.1(eslint@9.9.1) esutils: 2.0.3 known-css-properties: 0.30.0 - postcss: 8.4.47 - postcss-load-config: 3.1.4(postcss@8.4.47) - postcss-safe-parser: 6.0.0(postcss@8.4.47) + postcss: 8.5.1 + postcss-load-config: 3.1.4(postcss@8.5.1) + postcss-safe-parser: 6.0.0(postcss@8.5.1) postcss-selector-parser: 6.1.2 - semver: 7.6.3 - svelte-eslint-parser: 0.41.0(svelte@packages+svelte) + semver: 7.7.1 + svelte-eslint-parser: 0.43.0(svelte@packages+svelte) optionalDependencies: svelte: link:packages/svelte transitivePeerDependencies: @@ -3345,8 +3386,8 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} @@ -3373,17 +3414,7 @@ snapshots: esutils@2.0.3: {} - execa@8.0.1: - dependencies: - cross-spawn: 7.0.3 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.2.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 + expect-type@1.1.0: {} extendable-error@0.1.7: {} @@ -3423,6 +3454,11 @@ snapshots: dependencies: to-regex-range: 5.0.1 + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + optional: true + find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -3477,11 +3513,7 @@ snapshots: function-bind@1.1.2: {} - get-func-name@2.0.2: {} - - get-stream@8.0.1: {} - - get-tsconfig@4.7.6: + get-tsconfig@4.10.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -3504,6 +3536,8 @@ snapshots: globals@14.0.0: {} + globals@15.14.0: {} + globals@15.9.0: {} globalyzer@0.1.0: {} @@ -3538,21 +3572,19 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.1 - debug: 4.3.6 + debug: 4.4.0 transitivePeerDependencies: - supports-color https-proxy-agent@7.0.5: dependencies: agent-base: 7.1.1 - debug: 4.3.6 + debug: 4.4.0 transitivePeerDependencies: - supports-color human-id@1.0.2: {} - human-signals@5.0.0: {} - iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -3563,7 +3595,7 @@ snapshots: ignore@5.3.2: {} - immutable@4.3.4: + immutable@4.3.7: optional: true import-fresh@3.3.0: @@ -3575,7 +3607,7 @@ snapshots: is-binary-path@2.1.0: dependencies: - binary-extensions: 2.2.0 + binary-extensions: 2.3.0 optional: true is-core-module@2.13.1: @@ -3612,8 +3644,6 @@ snapshots: dependencies: '@types/estree': 1.0.6 - is-stream@3.0.0: {} - is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 @@ -3637,7 +3667,7 @@ snapshots: istanbul-lib-source-maps@5.0.6: dependencies: '@jridgewell/trace-mapping': 0.3.25 - debug: 4.3.6 + debug: 4.4.0 istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color @@ -3777,9 +3807,7 @@ snapshots: lodash.startcase@4.4.0: {} - loupe@3.1.1: - dependencies: - get-func-name: 2.0.2 + loupe@3.1.3: {} lru-cache@10.4.3: {} @@ -3792,6 +3820,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + magicast@0.3.4: dependencies: '@babel/parser': 7.25.4 @@ -3802,8 +3834,6 @@ snapshots: dependencies: semver: 7.6.3 - merge-stream@2.0.0: {} - merge2@1.4.1: {} micromatch@4.0.5: @@ -3817,8 +3847,6 @@ snapshots: dependencies: mime-db: 1.52.0 - mimic-fn@4.0.0: {} - minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -3835,8 +3863,12 @@ snapshots: ms@2.1.2: {} + ms@2.1.3: {} + nanoid@3.3.7: {} + nanoid@3.3.8: {} + natural-compare@1.4.0: {} node-fetch@2.7.0: @@ -3846,16 +3878,8 @@ snapshots: normalize-path@3.0.0: optional: true - npm-run-path@5.2.0: - dependencies: - path-key: 4.0.0 - nwsapi@2.2.12: {} - onetime@6.0.0: - dependencies: - mimic-fn: 4.0.0 - open@10.1.0: dependencies: default-browser: 5.2.1 @@ -3916,8 +3940,6 @@ snapshots: path-key@3.1.1: {} - path-key@4.0.0: {} - path-parse@1.0.7: {} path-scurry@1.11.1: @@ -3935,6 +3957,8 @@ snapshots: picocolors@1.1.0: {} + picocolors@1.1.1: {} + picomatch@2.3.1: {} picomatch@4.0.2: {} @@ -3954,20 +3978,20 @@ snapshots: '@polka/url': 1.0.0-next.25 trouter: 4.0.0 - postcss-load-config@3.1.4(postcss@8.4.47): + postcss-load-config@3.1.4(postcss@8.5.1): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: - postcss: 8.4.47 + postcss: 8.5.1 - postcss-safe-parser@6.0.0(postcss@8.4.47): + postcss-safe-parser@6.0.0(postcss@8.5.1): dependencies: - postcss: 8.4.47 + postcss: 8.5.1 - postcss-scss@4.0.9(postcss@8.4.47): + postcss-scss@4.0.9(postcss@8.5.1): dependencies: - postcss: 8.4.47 + postcss: 8.5.1 postcss-selector-parser@6.1.2: dependencies: @@ -3980,6 +4004,12 @@ snapshots: picocolors: 1.1.0 source-map-js: 1.2.1 + postcss@8.5.1: + dependencies: + nanoid: 3.3.8 + picocolors: 1.1.1 + source-map-js: 1.2.1 + prelude-ls@1.2.1: {} prettier-plugin-svelte@3.1.2(prettier@3.2.4)(svelte@packages+svelte): @@ -4071,8 +4101,8 @@ snapshots: sass@1.70.0: dependencies: - chokidar: 3.5.3 - immutable: 4.3.4 + chokidar: 3.6.0 + immutable: 4.3.7 source-map-js: 1.2.1 optional: true @@ -4082,6 +4112,8 @@ snapshots: semver@7.6.3: {} + semver@7.7.1: {} + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -4136,6 +4168,8 @@ snapshots: std-env@3.7.0: {} + std-env@3.8.0: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -4158,8 +4192,6 @@ snapshots: strip-bom@3.0.0: {} - strip-final-newline@3.0.0: {} - strip-json-comments@3.1.1: {} supports-color@7.2.0: @@ -4168,13 +4200,13 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-eslint-parser@0.41.0(svelte@packages+svelte): + svelte-eslint-parser@0.43.0(svelte@packages+svelte): dependencies: eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - postcss: 8.4.47 - postcss-scss: 4.0.9(postcss@8.4.47) + postcss: 8.5.1 + postcss-scss: 4.0.9(postcss@8.5.1) optionalDependencies: svelte: link:packages/svelte @@ -4206,11 +4238,13 @@ snapshots: tinybench@2.9.0: {} - tinypool@1.0.1: {} + tinyexec@0.3.2: {} + + tinypool@1.0.2: {} tinyrainbow@1.2.0: {} - tinyspy@3.0.0: {} + tinyspy@3.0.2: {} tldts-core@6.1.64: {} @@ -4279,12 +4313,12 @@ snapshots: v8-natives@1.2.5: {} - vite-node@2.0.5(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): + vite-node@2.1.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: cac: 6.7.14 - debug: 4.3.6 + debug: 4.4.0 + es-module-lexer: 1.6.0 pathe: 1.1.2 - tinyrainbow: 1.2.0 vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - '@types/node' @@ -4325,42 +4359,31 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vite@5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): - dependencies: - esbuild: 0.21.5 - postcss: 8.4.47 - rollup: 4.22.4 - optionalDependencies: - '@types/node': 20.12.7 - fsevents: 2.3.3 - lightningcss: 1.23.0 - sass: 1.70.0 - terser: 5.27.0 - vitefu@0.2.5(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): optionalDependencies: vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) - vitest@2.0.5(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): - dependencies: - '@ampproject/remapping': 2.3.0 - '@vitest/expect': 2.0.5 - '@vitest/pretty-format': 2.0.5 - '@vitest/runner': 2.0.5 - '@vitest/snapshot': 2.0.5 - '@vitest/spy': 2.0.5 - '@vitest/utils': 2.0.5 - chai: 5.1.1 - debug: 4.3.6 - execa: 8.0.1 - magic-string: 0.30.11 + vitest@2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): + dependencies: + '@vitest/expect': 2.1.9 + '@vitest/mocker': 2.1.9(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@vitest/pretty-format': 2.1.9 + '@vitest/runner': 2.1.9 + '@vitest/snapshot': 2.1.9 + '@vitest/spy': 2.1.9 + '@vitest/utils': 2.1.9 + chai: 5.1.2 + debug: 4.4.0 + expect-type: 1.1.0 + magic-string: 0.30.17 pathe: 1.1.2 - std-env: 3.7.0 + std-env: 3.8.0 tinybench: 2.9.0 - tinypool: 1.0.1 + tinyexec: 0.3.2 + tinypool: 1.0.2 tinyrainbow: 1.2.0 - vite: 5.4.6(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) - vite-node: 2.0.5(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite-node: 2.1.9(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.12.7 @@ -4368,6 +4391,7 @@ snapshots: transitivePeerDependencies: - less - lightningcss + - msw - sass - sass-embedded - stylus From 6f392d679bc5c4790aad8bfc1135c7f88c6ae510 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 5 Feb 2025 20:22:36 +0100 Subject: [PATCH 0157/1429] fix: properly set `value` property of custom elements (#15206) Avoid going through the `element.value = element.__value = newValue` condition because `__value` is actually how Lit stores the current value on the element, and messing with that would break things: Lit would think the value hasn't changed (because `__value` is already set to the new value by us) and doesn't fire an update. fixes #15194 --- .changeset/great-planes-swim.md | 5 +++++ .../client/dom/elements/attributes.js | 7 ++++--- .../custom-element-attributes/_config.js | 13 ++++++++---- .../custom-element-attributes/main.svelte | 20 +++++++++++++++++++ 4 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 .changeset/great-planes-swim.md diff --git a/.changeset/great-planes-swim.md b/.changeset/great-planes-swim.md new file mode 100644 index 0000000000..ecf8fa17aa --- /dev/null +++ b/.changeset/great-planes-swim.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: properly set `value` property of custom elements diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index eab27e6c02..8c86fe7e02 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -363,9 +363,10 @@ export function set_attributes( element.style.cssText = value + ''; } else if (key === 'autofocus') { autofocus(/** @type {HTMLElement} */ (element), Boolean(value)); - } else if (key === '__value' || (key === 'value' && value != null)) { - // @ts-ignore - element.value = element[key] = element.__value = value; + } else if (!is_custom_element && (key === '__value' || (key === 'value' && value != null))) { + // @ts-ignore We're not running this for custom elements because __value is actually + // how Lit stores the current value on the element, and messing with that would break things. + element.value = element.__value = value; } else if (key === 'selected' && is_option_element) { set_selected(/** @type {HTMLOptionElement} */ (element), value); } else { diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js index 118a51157e..7f406d8f0d 100644 --- a/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/_config.js @@ -1,19 +1,24 @@ import { test } from '../../test'; export default test({ - mode: ['client', 'server'], + mode: ['client'], async test({ assert, target }) { const my_element = /** @type HTMLElement & { object: { test: true }; } */ ( target.querySelector('my-element') ); - const my_link = /** @type HTMLAnchorElement & { object: { test: true }; } */ ( - target.querySelector('a') - ); assert.equal(my_element.getAttribute('string'), 'test'); assert.equal(my_element.hasAttribute('object'), false); assert.deepEqual(my_element.object, { test: true }); + + const my_link = /** @type HTMLAnchorElement & { object: { test: true }; } */ ( + target.querySelector('a') + ); assert.equal(my_link.getAttribute('string'), 'test'); assert.equal(my_link.hasAttribute('object'), false); assert.deepEqual(my_link.object, { test: true }); + + const [value1, value2] = target.querySelectorAll('value-element'); + assert.equal(value1.shadowRoot?.innerHTML, '<span>test</span>'); + assert.equal(value2.shadowRoot?.innerHTML, '<span>test</span>'); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte index ff94a9484c..4c98245e5b 100644 --- a/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/custom-element-attributes/main.svelte @@ -1,2 +1,22 @@ +<script module> + customElements.define('value-element', class extends HTMLElement { + + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + } + + set value(v) { + if (this.__value !== v) { + this.__value = v; + this.shadowRoot.innerHTML = `<span>${v}</span>`; + } + } + }); +</script> + <my-element string="test" object={{ test: true }}></my-element> <a is="my-link" string="test" object={{ test: true }}></a> + +<value-element value="test"></value-element> +<value-element {...{value: "test"}}></value-element> From 3b7066f5c611a9913ab62a5bf927443fc110d5f2 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 5 Feb 2025 20:22:58 +0100 Subject: [PATCH 0158/1429] fix: ensure custom element updates don't run in hydration mode (#15217) * fix: ensure custom element updates don't run in hydration mode When a custom element is created before Svelte hydration kicks in, it will scaffold itself, using the properties given via attributes. Now when a custom element property is set during Svelte's hydration, the Svelte custom element component could run logic like updating an each block. Without turning off hydration mode during that time, the update would try to pick up existing element nodes (because it thinks they must be there because of hydration mode), and crash. No test because it would require a setup where we can ensure the element is scaffolded before hydration runs. Fixes #15213 * changeset --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/light-ligers-switch.md | 5 +++++ .../client/dom/elements/attributes.js | 22 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 .changeset/light-ligers-switch.md diff --git a/.changeset/light-ligers-switch.md b/.changeset/light-ligers-switch.md new file mode 100644 index 0000000000..51b3271047 --- /dev/null +++ b/.changeset/light-ligers-switch.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: ensure custom element updates don't run in hydration mode diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 8c86fe7e02..308f23d340 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -1,5 +1,5 @@ import { DEV } from 'esm-env'; -import { hydrating } from '../hydration.js'; +import { hydrating, set_hydrating } from '../hydration.js'; import { get_descriptors, get_prototype_of } from '../../../shared/utils.js'; import { create_event, delegate } from './events.js'; import { add_form_reset_listener, autofocus } from './misc.js'; @@ -213,6 +213,12 @@ export function set_custom_element_data(node, prop, value) { // or effect var previous_reaction = active_reaction; var previous_effect = active_effect; + // If we're hydrating but the custom element is from Svelte, and it already scaffolded, + // then it might run block logic in hydration mode, which we have to prevent. + let was_hydrating = hydrating; + if (hydrating) { + set_hydrating(false); + } set_active_reaction(null); set_active_effect(null); @@ -239,6 +245,9 @@ export function set_custom_element_data(node, prop, value) { } finally { set_active_reaction(previous_reaction); set_active_effect(previous_effect); + if (was_hydrating) { + set_hydrating(true); + } } } @@ -262,6 +271,13 @@ export function set_attributes( is_custom_element = false, skip_warning = false ) { + // If we're hydrating but the custom element is from Svelte, and it already scaffolded, + // then it might run block logic in hydration mode, which we have to prevent. + let is_hydrating_custom_element = hydrating && is_custom_element; + if (is_hydrating_custom_element) { + set_hydrating(false); + } + var current = prev || {}; var is_option_element = element.tagName === 'OPTION'; @@ -416,6 +432,10 @@ export function set_attributes( } } + if (is_hydrating_custom_element) { + set_hydrating(true); + } + return current; } From b0c4fa52468fa00af5fe77c4caaf0f2e11a5b65c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Feb 2025 15:37:41 -0500 Subject: [PATCH 0159/1429] Version Packages (#15216) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/great-planes-swim.md | 5 ----- .changeset/light-ligers-switch.md | 5 ----- .changeset/long-moles-join.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 .changeset/great-planes-swim.md delete mode 100644 .changeset/light-ligers-switch.md delete mode 100644 .changeset/long-moles-join.md diff --git a/.changeset/great-planes-swim.md b/.changeset/great-planes-swim.md deleted file mode 100644 index ecf8fa17aa..0000000000 --- a/.changeset/great-planes-swim.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: properly set `value` property of custom elements diff --git a/.changeset/light-ligers-switch.md b/.changeset/light-ligers-switch.md deleted file mode 100644 index 51b3271047..0000000000 --- a/.changeset/light-ligers-switch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"svelte": patch ---- - -fix: ensure custom element updates don't run in hydration mode diff --git a/.changeset/long-moles-join.md b/.changeset/long-moles-join.md deleted file mode 100644 index 92c3d7bf9d..0000000000 --- a/.changeset/long-moles-join.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure tracking returns true, even if in unowned diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 9ebd37aadf..837697e70a 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.19.8 + +### Patch Changes + +- fix: properly set `value` property of custom elements ([#15206](https://github.com/sveltejs/svelte/pull/15206)) + +- fix: ensure custom element updates don't run in hydration mode ([#15217](https://github.com/sveltejs/svelte/pull/15217)) + +- fix: ensure tracking returns true, even if in unowned ([#15214](https://github.com/sveltejs/svelte/pull/15214)) + ## 5.19.7 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index a9d42379cc..ddbae00601 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.19.7", + "version": "5.19.8", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index d3c6e6b321..70be2b216f 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.19.7'; +export const VERSION = '5.19.8'; export const PUBLIC_VERSION = '5'; From f2c83e5db708ea5d320248ee832ddcbef355a8b7 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Thu, 6 Feb 2025 21:25:04 +0000 Subject: [PATCH 0160/1429] =?UTF-8?q?fix:=20ensure=20unowned=20derived=20d?= =?UTF-8?q?ependencies=20are=20not=20duplicated=20when=20reac=E2=80=A6=20(?= =?UTF-8?q?#15232)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: ensure unowned derived dependencies are not duplicated when reactions are skipped * added test case * added test case * add comment --- .changeset/stale-keys-taste.md | 5 +++++ .../svelte/src/internal/client/runtime.js | 16 ++++++-------- packages/svelte/tests/signals/test.ts | 22 +++++++++++++++++++ 3 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 .changeset/stale-keys-taste.md diff --git a/.changeset/stale-keys-taste.md b/.changeset/stale-keys-taste.md new file mode 100644 index 0000000000..b1b02323bb --- /dev/null +++ b/.changeset/stale-keys-taste.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure unowned derived dependencies are not duplicated when reactions are skipped diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 57cefccc01..30f14b7356 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -188,7 +188,8 @@ export function check_dirtiness(reaction) { dependency = dependencies[i]; // We always re-add all reactions (even duplicates) if the derived was - // previously disconnected + // previously disconnected, however we don't if it was unowned as we + // de-duplicate dependencies in that case if (is_disconnected || !dependency?.reactions?.includes(reaction)) { (dependency.reactions ??= []).push(reaction); } @@ -404,15 +405,9 @@ export function update_reaction(reaction) { skipped_deps = 0; untracked_writes = null; active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; - // prettier-ignore skip_reaction = (flags & UNOWNED) !== 0 && - (!is_flushing_effect || - // If we were previously not in a reactive context and we're reading an unowned derived - // that was created inside another reaction, then we don't fully know the real owner and thus - // we need to skip adding any reactions for this unowned - ((previous_reaction === null || previous_untracking) && - /** @type {Derived} */ (reaction).parent !== null)); + (!is_flushing_effect || previous_reaction === null || previous_untracking); derived_sources = null; set_component_context(reaction.ctx); @@ -933,7 +928,10 @@ export function get(signal) { skipped_deps++; } else if (new_deps === null) { new_deps = [signal]; - } else { + } else if (!skip_reaction || !new_deps.includes(signal)) { + // Normally we can push duplicated dependencies to `new_deps`, but if we're inside + // an unowned derived because skip_reaction is true, then we need to ensure that + // we don't have duplicates new_deps.push(signal); } } diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index bd9bc50ae3..46209ede81 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -818,6 +818,28 @@ describe('signals', () => { }; }); + test('unowned deriveds dependencies are correctly de-duped', () => { + return () => { + let a = state(0); + let b = state(true); + let c = derived(() => $.get(a)); + let d = derived(() => ($.get(b) ? 1 : $.get(a) + $.get(c) + $.get(a))); + + $.get(d); + + assert.equal(d.deps?.length, 1); + + $.get(d); + + set(a, 1); + set(b, false); + + $.get(d); + + assert.equal(d.deps?.length, 3); + }; + }); + test('unowned deriveds correctly update', () => { return () => { const arr1 = proxy<{ a: number }[]>([]); From 4883fd2c83e92226eb17c9a686712351163c482f Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 6 Feb 2025 22:26:03 +0100 Subject: [PATCH 0161/1429] fix: hydrate `href` that is part of spread attributes (#15226) We had some duplicated and buggy code, removing it fixes #15120 --- .changeset/wicked-apes-sin.md | 5 +++++ .../svelte/src/internal/client/dom/elements/attributes.js | 6 +----- .../samples/repair-mismatched-a-href/_expected.html | 2 +- .../hydration/samples/repair-mismatched-a-href/main.svelte | 1 + 4 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 .changeset/wicked-apes-sin.md diff --git a/.changeset/wicked-apes-sin.md b/.changeset/wicked-apes-sin.md new file mode 100644 index 0000000000..f40294ddff --- /dev/null +++ b/.changeset/wicked-apes-sin.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: hydrate `href` that is part of spread attributes diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 308f23d340..4a0f0cea0e 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -419,11 +419,7 @@ export function set_attributes( // @ts-ignore element[name] = value; } else if (typeof value !== 'function') { - if (hydrating && (name === 'src' || name === 'href' || name === 'srcset')) { - if (!skip_warning) check_src_in_dev_hydration(element, name, value ?? ''); - } else { - set_attribute(element, name, value); - } + set_attribute(element, name, value); } } if (key === 'style' && '__styles' in element) { diff --git a/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/_expected.html b/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/_expected.html index 2f5b652fac..e1076af2ec 100644 --- a/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/_expected.html +++ b/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/_expected.html @@ -1 +1 @@ -<!--[--><a href="/foo">foo</a><!--]--> +<!--[--><a href="/foo">foo</a> <a href="/foo">foo</a><!--]--> diff --git a/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/main.svelte b/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/main.svelte index be01d05f8e..3f0c988016 100644 --- a/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/main.svelte +++ b/packages/svelte/tests/hydration/samples/repair-mismatched-a-href/main.svelte @@ -3,3 +3,4 @@ </script> <a href={browser ? '/foo' : '/bar'}>foo</a> +<a {...{href: browser ? '/foo' : '/bar'}}>foo</a> From c4d4349d0a89c9588e06c8ccc142ef2063582f55 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 6 Feb 2025 21:29:58 +0000 Subject: [PATCH 0162/1429] Version Packages (#15234) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/stale-keys-taste.md | 5 ----- .changeset/wicked-apes-sin.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/stale-keys-taste.md delete mode 100644 .changeset/wicked-apes-sin.md diff --git a/.changeset/stale-keys-taste.md b/.changeset/stale-keys-taste.md deleted file mode 100644 index b1b02323bb..0000000000 --- a/.changeset/stale-keys-taste.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure unowned derived dependencies are not duplicated when reactions are skipped diff --git a/.changeset/wicked-apes-sin.md b/.changeset/wicked-apes-sin.md deleted file mode 100644 index f40294ddff..0000000000 --- a/.changeset/wicked-apes-sin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: hydrate `href` that is part of spread attributes diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 837697e70a..cf2f90b204 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.19.9 + +### Patch Changes + +- fix: ensure unowned derived dependencies are not duplicated when reactions are skipped ([#15232](https://github.com/sveltejs/svelte/pull/15232)) + +- fix: hydrate `href` that is part of spread attributes ([#15226](https://github.com/sveltejs/svelte/pull/15226)) + ## 5.19.8 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index ddbae00601..ec5e8cd6fc 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.19.8", + "version": "5.19.9", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 70be2b216f..aa5c7db036 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.19.8'; +export const VERSION = '5.19.9'; export const PUBLIC_VERSION = '5'; From 7ab18bba50e70d1d8c04bbcfe081b0a26e229a0a Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Mon, 10 Feb 2025 15:14:23 -0800 Subject: [PATCH 0163/1429] chore: allow pnpm 10 (#15258) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index efbee35c08..2fe545b361 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "license": "MIT", "packageManager": "pnpm@9.4.0", "engines": { - "pnpm": "^9.0.0" + "pnpm": ">=9.0.0" }, "repository": { "type": "git", From 02788f8e620aa91e98f895ede544ab76b559b00f Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Tue, 11 Feb 2025 09:43:49 +0100 Subject: [PATCH 0164/1429] fix: allow mutation of private derived state (#15228) Also disallow reassignment of private derived state Fixes #15227 --- .changeset/rare-carpets-wave.md | 5 ++++ .../src/compiler/phases/2-analyze/index.js | 4 +-- .../src/compiler/phases/2-analyze/types.d.ts | 2 +- .../phases/2-analyze/visitors/ClassBody.js | 8 +++--- .../phases/2-analyze/visitors/shared/utils.js | 25 ++++++++----------- .../mutate-derived-private-field/errors.json | 1 + .../mutate-derived-private-field/input.svelte | 9 +++++++ .../reassign-derived-literal/errors.json | 14 +++++++++++ .../reassign-derived-literal/input.svelte | 9 +++++++ .../errors.json | 14 +++++++++++ .../input.svelte | 9 +++++++ .../reassign-derived-public-field/errors.json | 14 +++++++++++ .../input.svelte | 9 +++++++ 13 files changed, 102 insertions(+), 21 deletions(-) create mode 100644 .changeset/rare-carpets-wave.md create mode 100644 packages/svelte/tests/validator/samples/mutate-derived-private-field/errors.json create mode 100644 packages/svelte/tests/validator/samples/mutate-derived-private-field/input.svelte create mode 100644 packages/svelte/tests/validator/samples/reassign-derived-literal/errors.json create mode 100644 packages/svelte/tests/validator/samples/reassign-derived-literal/input.svelte create mode 100644 packages/svelte/tests/validator/samples/reassign-derived-private-field/errors.json create mode 100644 packages/svelte/tests/validator/samples/reassign-derived-private-field/input.svelte create mode 100644 packages/svelte/tests/validator/samples/reassign-derived-public-field/errors.json create mode 100644 packages/svelte/tests/validator/samples/reassign-derived-public-field/input.svelte diff --git a/.changeset/rare-carpets-wave.md b/.changeset/rare-carpets-wave.md new file mode 100644 index 0000000000..cfd90cad6a --- /dev/null +++ b/.changeset/rare-carpets-wave.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow mutation of private derived state diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index bce6672505..ad9db24e1e 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -605,7 +605,7 @@ export function analyze_component(root, source, options) { has_props_rune: false, component_slots: new Set(), expression: null, - private_derived_state: [], + derived_state: [], function_depth: scope.function_depth, instance_scope: instance.scope, reactive_statement: null, @@ -676,7 +676,7 @@ export function analyze_component(root, source, options) { reactive_statements: analysis.reactive_statements, component_slots: new Set(), expression: null, - private_derived_state: [], + derived_state: [], function_depth: scope.function_depth }; diff --git a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts index 1e71accb9f..14b14f9c84 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts +++ b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts @@ -19,7 +19,7 @@ export interface AnalysisState { component_slots: Set<string>; /** Information about the current expression/directive/block value */ expression: ExpressionMetadata | null; - private_derived_state: string[]; + derived_state: string[]; function_depth: number; // legacy stuff diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ClassBody.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ClassBody.js index d445af0ebf..ed397258f8 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ClassBody.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ClassBody.js @@ -8,20 +8,20 @@ import { get_rune } from '../../scope.js'; */ export function ClassBody(node, context) { /** @type {string[]} */ - const private_derived_state = []; + const derived_state = []; for (const definition of node.body) { if ( definition.type === 'PropertyDefinition' && - definition.key.type === 'PrivateIdentifier' && + (definition.key.type === 'PrivateIdentifier' || definition.key.type === 'Identifier') && definition.value?.type === 'CallExpression' ) { const rune = get_rune(definition.value, context.state.scope); if (rune === '$derived' || rune === '$derived.by') { - private_derived_state.push(definition.key.name); + derived_state.push(definition.key.name); } } } - context.next({ ...context.state, private_derived_state }); + context.next({ ...context.state, derived_state }); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js index e265637c40..5fe2a8f24e 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js @@ -35,20 +35,17 @@ export function validate_assignment(node, argument, state) { } } - let object = /** @type {Expression | Super} */ (argument); - - /** @type {Expression | PrivateIdentifier | null} */ - let property = null; - - while (object.type === 'MemberExpression') { - property = object.property; - object = object.object; - } - - if (object.type === 'ThisExpression' && property?.type === 'PrivateIdentifier') { - if (state.private_derived_state.includes(property.name)) { - e.constant_assignment(node, 'derived state'); - } + if ( + argument.type === 'MemberExpression' && + argument.object.type === 'ThisExpression' && + (((argument.property.type === 'PrivateIdentifier' || argument.property.type === 'Identifier') && + state.derived_state.includes(argument.property.name)) || + (argument.property.type === 'Literal' && + argument.property.value && + typeof argument.property.value === 'string' && + state.derived_state.includes(argument.property.value))) + ) { + e.constant_assignment(node, 'derived state'); } } diff --git a/packages/svelte/tests/validator/samples/mutate-derived-private-field/errors.json b/packages/svelte/tests/validator/samples/mutate-derived-private-field/errors.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/packages/svelte/tests/validator/samples/mutate-derived-private-field/errors.json @@ -0,0 +1 @@ +[] diff --git a/packages/svelte/tests/validator/samples/mutate-derived-private-field/input.svelte b/packages/svelte/tests/validator/samples/mutate-derived-private-field/input.svelte new file mode 100644 index 0000000000..0a3df4bc1b --- /dev/null +++ b/packages/svelte/tests/validator/samples/mutate-derived-private-field/input.svelte @@ -0,0 +1,9 @@ +<script> + class Test{ + #der = $derived({test: 0}); + + set test(v){ + this.#der.test = 45; + } + } +</script> \ No newline at end of file diff --git a/packages/svelte/tests/validator/samples/reassign-derived-literal/errors.json b/packages/svelte/tests/validator/samples/reassign-derived-literal/errors.json new file mode 100644 index 0000000000..8681d84ab2 --- /dev/null +++ b/packages/svelte/tests/validator/samples/reassign-derived-literal/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "constant_assignment", + "message": "Cannot assign to derived state", + "start": { + "column": 3, + "line": 6 + }, + "end": { + "column": 29, + "line": 6 + } + } +] diff --git a/packages/svelte/tests/validator/samples/reassign-derived-literal/input.svelte b/packages/svelte/tests/validator/samples/reassign-derived-literal/input.svelte new file mode 100644 index 0000000000..8f109c9e1f --- /dev/null +++ b/packages/svelte/tests/validator/samples/reassign-derived-literal/input.svelte @@ -0,0 +1,9 @@ +<script> + class Test{ + der = $derived({ test: 0 }); + + set test(v){ + this["der"] = { test: 45 }; + } + } +</script> \ No newline at end of file diff --git a/packages/svelte/tests/validator/samples/reassign-derived-private-field/errors.json b/packages/svelte/tests/validator/samples/reassign-derived-private-field/errors.json new file mode 100644 index 0000000000..c211aa4608 --- /dev/null +++ b/packages/svelte/tests/validator/samples/reassign-derived-private-field/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "constant_assignment", + "message": "Cannot assign to derived state", + "start": { + "column": 3, + "line": 6 + }, + "end": { + "column": 27, + "line": 6 + } + } +] diff --git a/packages/svelte/tests/validator/samples/reassign-derived-private-field/input.svelte b/packages/svelte/tests/validator/samples/reassign-derived-private-field/input.svelte new file mode 100644 index 0000000000..62e2317e03 --- /dev/null +++ b/packages/svelte/tests/validator/samples/reassign-derived-private-field/input.svelte @@ -0,0 +1,9 @@ +<script> + class Test{ + #der = $derived({ test: 0 }); + + set test(v){ + this.#der = { test: 45 }; + } + } +</script> \ No newline at end of file diff --git a/packages/svelte/tests/validator/samples/reassign-derived-public-field/errors.json b/packages/svelte/tests/validator/samples/reassign-derived-public-field/errors.json new file mode 100644 index 0000000000..98837589ac --- /dev/null +++ b/packages/svelte/tests/validator/samples/reassign-derived-public-field/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "constant_assignment", + "message": "Cannot assign to derived state", + "start": { + "column": 3, + "line": 6 + }, + "end": { + "column": 26, + "line": 6 + } + } +] diff --git a/packages/svelte/tests/validator/samples/reassign-derived-public-field/input.svelte b/packages/svelte/tests/validator/samples/reassign-derived-public-field/input.svelte new file mode 100644 index 0000000000..e2c4693e86 --- /dev/null +++ b/packages/svelte/tests/validator/samples/reassign-derived-public-field/input.svelte @@ -0,0 +1,9 @@ +<script> + class Test{ + der = $derived({ test: 0 }); + + set test(v){ + this.der = { test: 45 }; + } + } +</script> \ No newline at end of file From b602c59a2201f3be0d0e2ec357a59047fd9ee50d Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:02:08 +0000 Subject: [PATCH 0165/1429] fix: when re-connecting unowned deriveds, remove their unowned flag (#15255) * fix: when an unowned derived is tracked again, remove unowned flag * fix: when an unowned derived is tracked again, remove unowned flag * test case * test case * cleanup unused logic * simplify * simplify * simplify * simplify * add test * fix other issue * back to original plan * Apply suggestions from code review --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- .changeset/nasty-pigs-lay.md | 5 + .../internal/client/reactivity/deriveds.js | 11 +- .../src/internal/client/reactivity/props.js | 60 +++---- .../svelte/src/internal/client/runtime.js | 15 +- .../samples/derived-unowned-12/_config.js | 25 +++ .../samples/derived-unowned-12/main.svelte | 18 +++ packages/svelte/tests/signals/test.ts | 147 +++++++++++++++--- 7 files changed, 208 insertions(+), 73 deletions(-) create mode 100644 .changeset/nasty-pigs-lay.md create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-unowned-12/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/derived-unowned-12/main.svelte diff --git a/.changeset/nasty-pigs-lay.md b/.changeset/nasty-pigs-lay.md new file mode 100644 index 0000000000..64ef72103a --- /dev/null +++ b/.changeset/nasty-pigs-lay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: when re-connecting unowned deriveds, remove their unowned flag diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 60b55970e6..59a7ed0f16 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -1,18 +1,9 @@ /** @import { Derived, Effect } from '#client' */ import { DEV } from 'esm-env'; -import { - CLEAN, - DERIVED, - DESTROYED, - DIRTY, - EFFECT_HAS_DERIVED, - MAYBE_DIRTY, - UNOWNED -} from '../constants.js'; +import { CLEAN, DERIVED, DIRTY, EFFECT_HAS_DERIVED, MAYBE_DIRTY, UNOWNED } from '../constants.js'; import { active_reaction, active_effect, - remove_reactions, set_signal_status, skip_reaction, update_reaction, diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index d157642d5d..5a3b30281f 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -10,7 +10,15 @@ import { import { get_descriptor, is_function } from '../../shared/utils.js'; import { mutable_source, set, source, update } from './sources.js'; import { derived, derived_safe_equal } from './deriveds.js'; -import { active_effect, get, captured_signals, set_active_effect, untrack } from '../runtime.js'; +import { + active_effect, + get, + captured_signals, + set_active_effect, + untrack, + active_reaction, + set_active_reaction +} from '../runtime.js'; import { safe_equals } from './equality.js'; import * as e from '../errors.js'; import { @@ -241,26 +249,6 @@ export function spread_props(...props) { return new Proxy({ props }, spread_props_handler); } -/** - * @template T - * @param {() => T} fn - * @returns {T} - */ -function with_parent_branch(fn) { - var effect = active_effect; - var previous_effect = active_effect; - - while (effect !== null && (effect.f & (BRANCH_EFFECT | ROOT_EFFECT)) === 0) { - effect = effect.parent; - } - try { - set_active_effect(effect); - return fn(); - } finally { - set_active_effect(previous_effect); - } -} - /** * This function is responsible for synchronizing a possibly bound prop with the inner component state. * It is used whenever the compiler sees that the component writes to the prop, or when it has a default prop_value. @@ -335,8 +323,8 @@ export function prop(props, key, flags, fallback) { } else { // Svelte 4 did not trigger updates when a primitive value was updated to the same value. // Replicate that behavior through using a derived - var derived_getter = with_parent_branch(() => - (immutable ? derived : derived_safe_equal)(() => /** @type {V} */ (props[key])) + var derived_getter = (immutable ? derived : derived_safe_equal)( + () => /** @type {V} */ (props[key]) ); derived_getter.f |= LEGACY_DERIVED_PROP; getter = () => { @@ -380,21 +368,19 @@ export function prop(props, key, flags, fallback) { // The derived returns the current value. The underlying mutable // source is written to from various places to persist this value. var inner_current_value = mutable_source(prop_value); - var current_value = with_parent_branch(() => - derived(() => { - var parent_value = getter(); - var child_value = get(inner_current_value); - - if (from_child) { - from_child = false; - was_from_child = true; - return child_value; - } + var current_value = derived(() => { + var parent_value = getter(); + var child_value = get(inner_current_value); + + if (from_child) { + from_child = false; + was_from_child = true; + return child_value; + } - was_from_child = false; - return (inner_current_value.v = parent_value); - }) - ); + was_from_child = false; + return (inner_current_value.v = parent_value); + }); if (!immutable) current_value.equals = safe_equals; diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 30f14b7356..8b0f84268c 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -184,19 +184,28 @@ export function check_dirtiness(reaction) { // If we are working with a disconnected or an unowned signal that is now connected (due to an active effect) // then we need to re-connect the reaction to the dependency if (is_disconnected || is_unowned_connected) { + var derived = /** @type {Derived} */ (reaction); + var parent = derived.parent; + for (i = 0; i < length; i++) { dependency = dependencies[i]; // We always re-add all reactions (even duplicates) if the derived was // previously disconnected, however we don't if it was unowned as we // de-duplicate dependencies in that case - if (is_disconnected || !dependency?.reactions?.includes(reaction)) { - (dependency.reactions ??= []).push(reaction); + if (is_disconnected || !dependency?.reactions?.includes(derived)) { + (dependency.reactions ??= []).push(derived); } } if (is_disconnected) { - reaction.f ^= DISCONNECTED; + derived.f ^= DISCONNECTED; + } + // If the unowned derived is now fully connected to the graph again (it's unowned and reconnected, has a parent + // and the parent is not unowned), then we can mark it as connected again, removing the need for the unowned + // flag + if (is_unowned_connected && parent !== null && (parent.f & UNOWNED) === 0) { + derived.f ^= UNOWNED; } } diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-12/_config.js b/packages/svelte/tests/runtime-runes/samples/derived-unowned-12/_config.js new file mode 100644 index 0000000000..8cd4af0548 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-12/_config.js @@ -0,0 +1,25 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + let [btn1, btn2] = target.querySelectorAll('button'); + + btn1?.click(); + flushSync(); + + btn2?.click(); + flushSync(); + + btn1?.click(); + flushSync(); + + btn1?.click(); + flushSync(); + + assert.htmlEqual( + target.innerHTML, + `<button>linked.current</button>\n3\n<button>count</button>\n1` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/derived-unowned-12/main.svelte b/packages/svelte/tests/runtime-runes/samples/derived-unowned-12/main.svelte new file mode 100644 index 0000000000..48d4f5fd0b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/derived-unowned-12/main.svelte @@ -0,0 +1,18 @@ +<script> + import { untrack } from 'svelte'; + + let count = $state(0); + let state = $state({current: count}); + + let linked = $derived.by(() => { + count; + + untrack(() => state.current = count); + return untrack(() => state); + }); + + linked.current++; +</script> + +<button onclick={() => linked.current++}>linked.current</button> {linked.current} +<button onclick={() => count++}>count</button> {count} diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index 46209ede81..046f833e0e 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -224,20 +224,75 @@ describe('signals', () => { }; }); - test('effects correctly handle unowned derived values that do not change', () => { - const log: number[] = []; + test('https://perf.js.hyoo.ru/#!bench=9h2as6_u0mfnn #2', () => { + let res: number[] = []; - let count = state(0); - const read = () => { - const x = derived(() => ({ count: $.get(count) })); - return $.get(x); + const numbers = Array.from({ length: 2 }, (_, i) => i); + const fib = (n: number): number => (n < 2 ? 1 : fib(n - 1) + fib(n - 2)); + const hard = (n: number, l: string) => n + fib(16); + + const A = state(0); + const B = state(0); + + return () => { + const C = derived(() => ($.get(A) % 2) + ($.get(B) % 2)); + const D = derived(() => numbers.map((i) => i + ($.get(A) % 2) - ($.get(B) % 2))); + const E = derived(() => hard($.get(C) + $.get(A) + $.get(D)[0]!, 'E')); + const F = derived(() => hard($.get(D)[0]! && $.get(B), 'F')); + const G = derived(() => $.get(C) + ($.get(C) || $.get(E) % 2) + $.get(D)[0]! + $.get(F)); + + const destroy = effect_root(() => { + effect(() => { + res.push(hard($.get(G), 'H')); + }); + effect(() => { + res.push($.get(G)); + }); + effect(() => { + res.push(hard($.get(F), 'J')); + }); + }); + + flushSync(); + + let i = 2; + while (--i) { + res.length = 0; + set(B, 1); + set(A, 1 + i * 2); + flushSync(); + + set(A, 2 + i * 2); + set(B, 2); + flushSync(); + + assert.equal(res.length, 4); + assert.deepEqual(res, [3198, 1601, 3195, 1598]); + } + + destroy(); + assert(A.reactions === null); + assert(B.reactions === null); }; - const derivedCount = derived(() => read().count); - user_effect(() => { - log.push($.get(derivedCount)); - }); + }); + + test('effects correctly handle unowned derived values that do not change', () => { + const log: number[] = []; return () => { + let count = state(0); + const read = () => { + const x = derived(() => ({ count: $.get(count) })); + return $.get(x); + }; + const derivedCount = derived(() => read().count); + + const destroy = effect_root(() => { + user_effect(() => { + log.push($.get(derivedCount)); + }); + }); + flushSync(() => set(count, 1)); // Ensure we're not leaking consumers assert.deepEqual(count.reactions?.length, 1); @@ -248,6 +303,8 @@ describe('signals', () => { // Ensure we're not leaking consumers assert.deepEqual(count.reactions?.length, 1); assert.deepEqual(log, [0, 1, 2, 3]); + + destroy(); }; }); @@ -343,25 +400,69 @@ describe('signals', () => { }; }); - let some_state = state({}); - let some_deps = derived(() => { - return [$.get(some_state)]; - }); - test('two effects with an unowned derived that has some dependencies', () => { const log: Array<Array<any>> = []; - render_effect(() => { - log.push($.get(some_deps)); - }); + return () => { + let some_state = state({}); + let some_deps = derived(() => { + return [$.get(some_state)]; + }); + let destroy2: any; - render_effect(() => { - log.push($.get(some_deps)); - }); + const destroy = effect_root(() => { + render_effect(() => { + $.untrack(() => { + log.push($.get(some_deps)); + }); + }); - return () => { + destroy2 = effect_root(() => { + render_effect(() => { + log.push($.get(some_deps)); + }); + + render_effect(() => { + log.push($.get(some_deps)); + }); + }); + }); + + set(some_state, {}); + flushSync(); + + assert.deepEqual(log, [[{}], [{}], [{}], [{}], [{}]]); + + destroy2(); + + set(some_state, {}); flushSync(); - assert.deepEqual(log, [[{}], [{}]]); + + assert.deepEqual(log, [[{}], [{}], [{}], [{}], [{}]]); + + log.length = 0; + + const destroy3 = effect_root(() => { + render_effect(() => { + $.untrack(() => { + log.push($.get(some_deps)); + }); + log.push($.get(some_deps)); + }); + }); + + set(some_state, {}); + flushSync(); + + assert.deepEqual(log, [[{}], [{}], [{}], [{}]]); + + destroy3(); + + assert(some_state.reactions === null); + + destroy(); + + assert(some_state.reactions === null); }; }); From 280d8c74cc9d5cad9b200ce0f9d987633349cfae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:03:30 +0000 Subject: [PATCH 0166/1429] Version Packages (#15263) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/nasty-pigs-lay.md | 5 ----- .changeset/rare-carpets-wave.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/nasty-pigs-lay.md delete mode 100644 .changeset/rare-carpets-wave.md diff --git a/.changeset/nasty-pigs-lay.md b/.changeset/nasty-pigs-lay.md deleted file mode 100644 index 64ef72103a..0000000000 --- a/.changeset/nasty-pigs-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: when re-connecting unowned deriveds, remove their unowned flag diff --git a/.changeset/rare-carpets-wave.md b/.changeset/rare-carpets-wave.md deleted file mode 100644 index cfd90cad6a..0000000000 --- a/.changeset/rare-carpets-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: allow mutation of private derived state diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index cf2f90b204..e112bf6209 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.19.10 + +### Patch Changes + +- fix: when re-connecting unowned deriveds, remove their unowned flag ([#15255](https://github.com/sveltejs/svelte/pull/15255)) + +- fix: allow mutation of private derived state ([#15228](https://github.com/sveltejs/svelte/pull/15228)) + ## 5.19.9 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index ec5e8cd6fc..a4594b2a5c 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.19.9", + "version": "5.19.10", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index aa5c7db036..ada6f9019d 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.19.9'; +export const VERSION = '5.19.10'; export const PUBLIC_VERSION = '5'; From 73220b86676d59322fb6ff0e6cf13ea72a666e04 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Tue, 11 Feb 2025 15:11:40 -0500 Subject: [PATCH 0167/1429] chore: simplify process_effects (#15270) * chore: simplify process_effects * return effects --- .../svelte/src/internal/client/runtime.js | 68 ++++++++----------- 1 file changed, 29 insertions(+), 39 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 8b0f84268c..d5d01c9b6d 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -679,10 +679,7 @@ function flush_queued_root_effects(root_effects) { effect.f ^= CLEAN; } - /** @type {Effect[]} */ - var collected_effects = []; - - process_effects(effect, collected_effects); + var collected_effects = process_effects(effect); flush_queued_effects(collected_effects); } } finally { @@ -783,13 +780,14 @@ export function schedule_effect(signal) { * effects to be flushed. * * @param {Effect} effect - * @param {Effect[]} collected_effects - * @returns {void} + * @returns {Effect[]} */ -function process_effects(effect, collected_effects) { - var current_effect = effect.first; +function process_effects(effect) { + /** @type {Effect[]} */ var effects = []; + var current_effect = effect.first; + main_loop: while (current_effect !== null) { var flags = current_effect.f; var is_branch = (flags & BRANCH_EFFECT) !== 0; @@ -797,34 +795,32 @@ function process_effects(effect, collected_effects) { var sibling = current_effect.next; if (!is_skippable_branch && (flags & INERT) === 0) { - if ((flags & RENDER_EFFECT) !== 0) { - if (is_branch) { - current_effect.f ^= CLEAN; - } else { - // Ensure we set the effect to be the active reaction - // to ensure that unowned deriveds are correctly tracked - // because we're flushing the current effect - var previous_active_reaction = active_reaction; - try { - active_reaction = current_effect; - if (check_dirtiness(current_effect)) { - update_effect(current_effect); - } - } catch (error) { - handle_error(error, current_effect, null, current_effect.ctx); - } finally { - active_reaction = previous_active_reaction; + if ((flags & EFFECT) !== 0) { + effects.push(current_effect); + } else if (is_branch) { + current_effect.f ^= CLEAN; + } else { + // Ensure we set the effect to be the active reaction + // to ensure that unowned deriveds are correctly tracked + // because we're flushing the current effect + var previous_active_reaction = active_reaction; + try { + active_reaction = current_effect; + if (check_dirtiness(current_effect)) { + update_effect(current_effect); } + } catch (error) { + handle_error(error, current_effect, null, current_effect.ctx); + } finally { + active_reaction = previous_active_reaction; } + } - var child = current_effect.first; + var child = current_effect.first; - if (child !== null) { - current_effect = child; - continue; - } - } else if ((flags & EFFECT) !== 0) { - effects.push(current_effect); + if (child !== null) { + current_effect = child; + continue; } } @@ -847,13 +843,7 @@ function process_effects(effect, collected_effects) { current_effect = sibling; } - // We might be dealing with many effects here, far more than can be spread into - // an array push call (callstack overflow). So let's deal with each effect in a loop. - for (var i = 0; i < effects.length; i++) { - child = effects[i]; - collected_effects.push(child); - process_effects(child, collected_effects); - } + return effects; } /** From 85f83ec435ef0baa797f195f5b016395fc70be13 Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Tue, 11 Feb 2025 21:12:58 +0100 Subject: [PATCH 0168/1429] feat: $props.id(), a SSR-safe ID generation (#15185) * first impl of $$uid * fix * $props.id() * fix errors * rename $.create_uid() into $.props_id() * fix message * relax const requirement, validate assignments instead * oops * simplify * non-constants should be lowercased * ditto * start at 1 * add docs * changeset * add test * add docs * doc : add code example * fix type reported by bennymi --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/hip-singers-vanish.md | 5 ++ documentation/docs/02-runes/05-$props.md | 21 +++++++ .../98-reference/.generated/compile-errors.md | 8 ++- .../svelte/messages/compile-errors/script.md | 6 +- packages/svelte/src/ambient.d.ts | 9 +++ packages/svelte/src/compiler/errors.js | 16 ++++- .../src/compiler/phases/2-analyze/index.js | 1 + .../2-analyze/visitors/CallExpression.js | 28 ++++++++- .../phases/2-analyze/visitors/shared/utils.js | 4 ++ .../3-transform/client/transform-client.js | 5 ++ .../client/visitors/VariableDeclaration.js | 5 ++ .../client/visitors/shared/utils.js | 6 ++ .../3-transform/server/transform-server.js | 7 +++ .../server/visitors/VariableDeclaration.js | 9 +++ .../svelte/src/compiler/phases/types.d.ts | 2 + .../src/internal/client/dom/template.js | 20 ++++++ packages/svelte/src/internal/client/index.js | 3 +- packages/svelte/src/internal/server/index.js | 31 ++++++++-- .../svelte/src/internal/server/types.d.ts | 2 + packages/svelte/src/utils.js | 1 + .../samples/props-id/Child.svelte | 5 ++ .../runtime-runes/samples/props-id/_config.js | 61 +++++++++++++++++++ .../samples/props-id/main.svelte | 19 ++++++ packages/svelte/types/index.d.ts | 9 +++ 24 files changed, 272 insertions(+), 11 deletions(-) create mode 100644 .changeset/hip-singers-vanish.md create mode 100644 packages/svelte/tests/runtime-runes/samples/props-id/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/props-id/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/props-id/main.svelte diff --git a/.changeset/hip-singers-vanish.md b/.changeset/hip-singers-vanish.md new file mode 100644 index 0000000000..9dce4d98a8 --- /dev/null +++ b/.changeset/hip-singers-vanish.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: SSR-safe ID generation with `$props.id()` diff --git a/documentation/docs/02-runes/05-$props.md b/documentation/docs/02-runes/05-$props.md index 4b1775bf5a..f300fb239d 100644 --- a/documentation/docs/02-runes/05-$props.md +++ b/documentation/docs/02-runes/05-$props.md @@ -199,3 +199,24 @@ You can, of course, separate the type declaration from the annotation: > [!NOTE] Interfaces for native DOM elements are provided in the `svelte/elements` module (see [Typing wrapper components](typescript#Typing-wrapper-components)) Adding types is recommended, as it ensures that people using your component can easily discover which props they should provide. + + +## `$props.id()` + +This rune, added in version 5.20.0, generates an ID that is unique to the current component instance. When hydrating a server-rendered component, the value will be consistent between server and client. + +This is useful for linking elements via attributes like `for` and `aria-labelledby`. + +```svelte +<script> + const uid = $props.id(); +</script> + +<form> + <label for="{uid}-firstname">First Name: </label> + <input id="{uid}-firstname" type="text" /> + + <label for="{uid}-lastname">Last Name: </label> + <input id="{uid}-lastname" type="text" /> +</form> +``` \ No newline at end of file diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index 2fef3bd45d..a4ecbb31d5 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -573,7 +573,13 @@ Unrecognised compiler option %keypath% ### props_duplicate ``` -Cannot use `$props()` more than once +Cannot use `%rune%()` more than once +``` + +### props_id_invalid_placement + +``` +`$props.id()` can only be used at the top level of components as a variable declaration initializer ``` ### props_illegal_name diff --git a/packages/svelte/messages/compile-errors/script.md b/packages/svelte/messages/compile-errors/script.md index 0aa6fbed90..795c0b007d 100644 --- a/packages/svelte/messages/compile-errors/script.md +++ b/packages/svelte/messages/compile-errors/script.md @@ -120,7 +120,11 @@ This turned out to be buggy and unpredictable, particularly when working with de ## props_duplicate -> Cannot use `$props()` more than once +> Cannot use `%rune%()` more than once + +## props_id_invalid_placement + +> `$props.id()` can only be used at the top level of components as a variable declaration initializer ## props_illegal_name diff --git a/packages/svelte/src/ambient.d.ts b/packages/svelte/src/ambient.d.ts index fbcecba8e4..a1484718cc 100644 --- a/packages/svelte/src/ambient.d.ts +++ b/packages/svelte/src/ambient.d.ts @@ -339,6 +339,15 @@ declare namespace $effect { declare function $props(): any; declare namespace $props { + /** + * Generates an ID that is unique to the current component instance. When hydrating a server-rendered component, + * the value will be consistent between server and client. + * + * This is useful for linking elements via attributes like `for` and `aria-labelledby`. + * @since 5.20.0 + */ + export function id(): string; + // prevent intellisense from being unhelpful /** @deprecated */ export const apply: never; diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index 53a6ac6849..93eeee539c 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -279,12 +279,22 @@ export function module_illegal_default_export(node) { } /** - * Cannot use `$props()` more than once + * Cannot use `%rune%()` more than once + * @param {null | number | NodeLike} node + * @param {string} rune + * @returns {never} + */ +export function props_duplicate(node, rune) { + e(node, 'props_duplicate', `Cannot use \`${rune}()\` more than once\nhttps://svelte.dev/e/props_duplicate`); +} + +/** + * `$props.id()` can only be used at the top level of components as a variable declaration initializer * @param {null | number | NodeLike} node * @returns {never} */ -export function props_duplicate(node) { - e(node, 'props_duplicate', `Cannot use \`$props()\` more than once\nhttps://svelte.dev/e/props_duplicate`); +export function props_id_invalid_placement(node) { + e(node, 'props_id_invalid_placement', `\`$props.id()\` can only be used at the top level of components as a variable declaration initializer\nhttps://svelte.dev/e/props_id_invalid_placement`); } /** diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index ad9db24e1e..846abcf7df 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -416,6 +416,7 @@ export function analyze_component(root, source, options) { immutable: runes || options.immutable, exports: [], uses_props: false, + props_id: null, uses_rest_props: false, uses_slots: false, uses_component_bindings: false, diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index 0a6b3f3ee5..ce520cc980 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -55,7 +55,7 @@ export function CallExpression(node, context) { case '$props': if (context.state.has_props_rune) { - e.props_duplicate(node); + e.props_duplicate(node, rune); } context.state.has_props_rune = true; @@ -74,6 +74,32 @@ export function CallExpression(node, context) { break; + case '$props.id': { + const grand_parent = get_parent(context.path, -2); + + if (context.state.analysis.props_id) { + e.props_duplicate(node, rune); + } + + if ( + parent.type !== 'VariableDeclarator' || + parent.id.type !== 'Identifier' || + context.state.ast_type !== 'instance' || + context.state.scope !== context.state.analysis.instance.scope || + grand_parent.type !== 'VariableDeclaration' + ) { + e.props_id_invalid_placement(node); + } + + if (node.arguments.length > 0) { + e.rune_invalid_arguments(node, rune); + } + + context.state.analysis.props_id = parent.id; + + break; + } + case '$state': case '$state.raw': case '$derived': diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js index 5fe2a8f24e..2d90c85364 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js @@ -25,6 +25,10 @@ export function validate_assignment(node, argument, state) { e.constant_assignment(node, 'derived state'); } + if (binding?.node === state.analysis.props_id) { + e.constant_assignment(node, '$props.id()'); + } + if (binding?.kind === 'each') { e.each_item_invalid_assignment(node); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index f4a6c9a414..2e6307a4b7 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -562,6 +562,11 @@ export function client_component(analysis, options) { component_block.body.unshift(b.stmt(b.call('$.check_target', b.id('new.target')))); } + if (analysis.props_id) { + // need to be placed on first line of the component for hydration + component_block.body.unshift(b.const(analysis.props_id, b.call('$.props_id'))); + } + if (state.events.size > 0) { body.push( b.stmt(b.call('$.delegate', b.array(Array.from(state.events).map((name) => b.literal(name))))) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js index afb90bbec7..31e712cdcc 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js @@ -42,6 +42,11 @@ export function VariableDeclaration(node, context) { continue; } + if (rune === '$props.id') { + // skip + continue; + } + if (rune === '$props') { /** @type {string[]} */ const seen = ['$$slots', '$$events', '$$legacy']; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index 00634f229e..9214a13c94 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -129,6 +129,12 @@ export function build_template_chunk( if (value.right.value === null) { value = { ...value, right: b.literal('') }; } + } else if ( + state.analysis.props_id && + value.type === 'Identifier' && + value.name === state.analysis.props_id.name + ) { + // do nothing ($props.id() is never null/undefined) } else { value = b.logical('??', value, b.literal('')); } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index 982b75e12f..df3d831d3c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -244,6 +244,13 @@ export function server_component(analysis, options) { .../** @type {Statement[]} */ (template.body) ]); + if (analysis.props_id) { + // need to be placed on first line of the component for hydration + component_block.body.unshift( + b.const(analysis.props_id, b.call('$.props_id', b.id('$$payload'))) + ); + } + let should_inject_context = dev || analysis.needs_context; if (should_inject_context) { diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js index 31de811ac7..c4c31d7eb3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js @@ -24,6 +24,11 @@ export function VariableDeclaration(node, context) { continue; } + if (rune === '$props.id') { + // skip + continue; + } + if (rune === '$props') { let has_rest = false; // remove $bindable() from props declaration @@ -156,6 +161,10 @@ export function VariableDeclaration(node, context) { } } + if (declarations.length === 0) { + return b.empty; + } + return { ...node, declarations diff --git a/packages/svelte/src/compiler/phases/types.d.ts b/packages/svelte/src/compiler/phases/types.d.ts index fe32dbba3e..abe2b115de 100644 --- a/packages/svelte/src/compiler/phases/types.d.ts +++ b/packages/svelte/src/compiler/phases/types.d.ts @@ -44,6 +44,8 @@ export interface ComponentAnalysis extends Analysis { exports: Array<{ name: string; alias: string | null }>; /** Whether the component uses `$$props` */ uses_props: boolean; + /** The component ID variable name, if any */ + props_id: Identifier | null; /** Whether the component uses `$$restProps` */ uses_rest_props: boolean; /** Whether the component uses `$$slots` */ diff --git a/packages/svelte/src/internal/client/dom/template.js b/packages/svelte/src/internal/client/dom/template.js index bcbae393ec..3e4f45aba8 100644 --- a/packages/svelte/src/internal/client/dom/template.js +++ b/packages/svelte/src/internal/client/dom/template.js @@ -249,3 +249,23 @@ export function append(anchor, dom) { anchor.before(/** @type {Node} */ (dom)); } + +let uid = 1; + +/** + * Create (or hydrate) an unique UID for the component instance. + */ +export function props_id() { + if ( + hydrating && + hydrate_node && + hydrate_node.nodeType === 8 && + hydrate_node.textContent?.startsWith('#s') + ) { + const id = hydrate_node.textContent.substring(1); + hydrate_next(); + return id; + } + + return 'c' + uid++; +} diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 3f3b29b029..8eaa5d66e1 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -96,7 +96,8 @@ export { mathml_template, template, template_with_script, - text + text, + props_id } from './dom/template.js'; export { derived, derived_safe_equal } from './reactivity/deriveds.js'; export { diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 89b3c33df8..c4e5d318dc 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -28,14 +28,15 @@ const INVALID_ATTR_NAME_CHAR_REGEX = * @param {Payload} to_copy * @returns {Payload} */ -export function copy_payload({ out, css, head }) { +export function copy_payload({ out, css, head, uid }) { return { out, css: new Set(css), head: { title: head.title, out: head.out - } + }, + uid }; } @@ -48,6 +49,7 @@ export function copy_payload({ out, css, head }) { export function assign_payload(p1, p2) { p1.out = p2.out; p1.head = p2.head; + p1.uid = p2.uid; } /** @@ -83,17 +85,27 @@ export function element(payload, tag, attributes_fn = noop, children_fn = noop) */ export let on_destroy = []; +function props_id_generator() { + let uid = 1; + return () => 's' + uid++; +} + /** * Only available on the server and when compiling with the `server` option. * Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app. * @template {Record<string, any>} Props * @param {import('svelte').Component<Props> | ComponentType<SvelteComponent<Props>>} component - * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> }} [options] + * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>, uid?: () => string }} [options] * @returns {RenderOutput} */ export function render(component, options = {}) { /** @type {Payload} */ - const payload = { out: '', css: new Set(), head: { title: '', out: '' } }; + const payload = { + out: '', + css: new Set(), + head: { title: '', out: '' }, + uid: options.uid ?? props_id_generator() + }; const prev_on_destroy = on_destroy; on_destroy = []; @@ -526,6 +538,17 @@ export function once(get_value) { }; } +/** + * Create an unique ID + * @param {Payload} payload + * @returns {string} + */ +export function props_id(payload) { + const uid = payload.uid(); + payload.out += '<!--#' + uid + '-->'; + return uid; +} + export { attr, clsx }; export { html } from './blocks/html.js'; diff --git a/packages/svelte/src/internal/server/types.d.ts b/packages/svelte/src/internal/server/types.d.ts index e6c235147b..8a241deecd 100644 --- a/packages/svelte/src/internal/server/types.d.ts +++ b/packages/svelte/src/internal/server/types.d.ts @@ -18,6 +18,8 @@ export interface Payload { title: string; out: string; }; + /** Function that generates a unique ID */ + uid: () => string; } export interface RenderOutput { diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index e8e1bc224c..d4d106d56d 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -433,6 +433,7 @@ const RUNES = /** @type {const} */ ([ '$state.raw', '$state.snapshot', '$props', + '$props.id', '$bindable', '$derived', '$derived.by', diff --git a/packages/svelte/tests/runtime-runes/samples/props-id/Child.svelte b/packages/svelte/tests/runtime-runes/samples/props-id/Child.svelte new file mode 100644 index 0000000000..ad8bbd6f01 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-id/Child.svelte @@ -0,0 +1,5 @@ +<script> + let id = $props.id(); +</script> + +<p>{id}</p> diff --git a/packages/svelte/tests/runtime-runes/samples/props-id/_config.js b/packages/svelte/tests/runtime-runes/samples/props-id/_config.js new file mode 100644 index 0000000000..9d91b98e0f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-id/_config.js @@ -0,0 +1,61 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + test({ assert, target, variant }) { + if (variant === 'dom') { + assert.htmlEqual( + target.innerHTML, + ` + <button>toggle</button> + <h1>c1</h1> + <p>c2</p> + <p>c3</p> + <p>c4</p> + ` + ); + } else { + assert.htmlEqual( + target.innerHTML, + ` + <button>toggle</button> + <h1>s1</h1> + <p>s2</p> + <p>s3</p> + <p>s4</p> + ` + ); + } + + let button = target.querySelector('button'); + flushSync(() => button?.click()); + + if (variant === 'dom') { + assert.htmlEqual( + target.innerHTML, + ` + <button>toggle</button> + <h1>c1</h1> + <p>c2</p> + <p>c3</p> + <p>c4</p> + <p>c5</p> + ` + ); + } else { + // `c6` because this runs after the `dom` tests + // (slightly brittle but good enough for now) + assert.htmlEqual( + target.innerHTML, + ` + <button>toggle</button> + <h1>s1</h1> + <p>s2</p> + <p>s3</p> + <p>s4</p> + <p>c6</p> + ` + ); + } + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/props-id/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-id/main.svelte new file mode 100644 index 0000000000..646bb2ebde --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-id/main.svelte @@ -0,0 +1,19 @@ +<script> + import Child from './Child.svelte'; + + let id = $props.id(); + + let show = $state(false); +</script> + +<button onclick={() => show = !show}>toggle</button> + +<h1>{id}</h1> + +<Child /> +<Child /> +<Child /> + +{#if show} + <Child /> +{/if} diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index eb3e93e4b5..77d78477ee 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -2995,6 +2995,15 @@ declare namespace $effect { declare function $props(): any; declare namespace $props { + /** + * Generates an ID that is unique to the current component instance. When hydrating a server-rendered component, + * the value will be consistent between server and client. + * + * This is useful for linking elements via attributes like `for` and `aria-labelledby`. + * @since 5.20.0 + */ + export function id(): string; + // prevent intellisense from being unhelpful /** @deprecated */ export const apply: never; From afae274587b30bb505508e1302ae149fd9c8593c Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Tue, 11 Feb 2025 21:36:38 +0100 Subject: [PATCH 0169/1429] fix: value/checked not correctly set using spread (#15239) * set value/checked by JS * test * changeset * fix test form-default-value-spread --- .changeset/fuzzy-zoos-repeat.md | 5 +++ .../client/dom/elements/attributes.js | 13 +++++--- .../samples/attribute-spread-input/_config.js | 33 +++++++++++++++++++ .../attribute-spread-input/main.svelte | 22 +++++++++++++ 4 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 .changeset/fuzzy-zoos-repeat.md create mode 100644 packages/svelte/tests/runtime-runes/samples/attribute-spread-input/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/attribute-spread-input/main.svelte diff --git a/.changeset/fuzzy-zoos-repeat.md b/.changeset/fuzzy-zoos-repeat.md new file mode 100644 index 0000000000..3fb3f0502e --- /dev/null +++ b/.changeset/fuzzy-zoos-repeat.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: value/checked not correctly set using spread diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 4a0f0cea0e..987d1f2086 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -399,15 +399,18 @@ export function set_attributes( if (name === 'value' || name === 'checked') { // removing value/checked also removes defaultValue/defaultChecked — preserve let input = /** @type {HTMLInputElement} */ (element); - + const use_default = prev === undefined; if (name === 'value') { - let prev = input.defaultValue; + let previous = input.defaultValue; input.removeAttribute(name); - input.defaultValue = prev; + input.defaultValue = previous; + // @ts-ignore + input.value = input.__value = use_default ? previous : null; } else { - let prev = input.defaultChecked; + let previous = input.defaultChecked; input.removeAttribute(name); - input.defaultChecked = prev; + input.defaultChecked = previous; + input.checked = use_default ? previous : false; } } else { element.removeAttribute(key); diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-spread-input/_config.js b/packages/svelte/tests/runtime-runes/samples/attribute-spread-input/_config.js new file mode 100644 index 0000000000..ab94125503 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attribute-spread-input/_config.js @@ -0,0 +1,33 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ target, assert }) { + // Test for https://github.com/sveltejs/svelte/issues/15237 + const [setValues, clearValue] = target.querySelectorAll('button'); + const [text1, text2, check1, check2] = target.querySelectorAll('input'); + + assert.equal(text1.value, ''); + assert.equal(text2.value, ''); + assert.equal(check1.checked, false); + assert.equal(check2.checked, false); + + flushSync(() => { + setValues.click(); + }); + + assert.equal(text1.value, 'message'); + assert.equal(text2.value, 'message'); + assert.equal(check1.checked, true); + assert.equal(check2.checked, true); + + flushSync(() => { + clearValue.click(); + }); + + assert.equal(text1.value, ''); + assert.equal(text2.value, ''); + assert.equal(check1.checked, false); + assert.equal(check2.checked, false); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/attribute-spread-input/main.svelte b/packages/svelte/tests/runtime-runes/samples/attribute-spread-input/main.svelte new file mode 100644 index 0000000000..4bb4365ee2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attribute-spread-input/main.svelte @@ -0,0 +1,22 @@ +<script> + let value = $state(); + let checked = $state(false); + + function setValues() { + value = 'message'; + checked = true; + } + function clearValues() { + value = null; + checked = null; + } +</script> + +<button onclick={setValues}>setValues</button> +<button onclick={clearValues}>clearValues</button> + +<input type="text" {value} /> +<input type="text" {value} {...{}} /> + +<input type="checkbox" {checked} /> +<input type="checkbox" {checked} {...{}} /> From a3e49b611094559a2c4f3830dec2cc75680140ab Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 11 Feb 2025 23:07:17 +0100 Subject: [PATCH 0170/1429] fix: recurse into `$derived` for ownership validation (#15166) - `$derived` can contain `$state` declarations so we cannot ignore them, so this reverts #14533 - instead, we add equality checks to not do this expensive work unnecessarily - this also adds a render effect similar to the class ownership addition when it detects a getter on a POJO during ownership addition fixes #15164 --- .changeset/thick-carrots-arrive.md | 5 ++ .../3-transform/client/visitors/ClassBody.js | 29 ++++++------ .../client/visitors/shared/component.js | 41 +++++------------ .../src/internal/client/dev/ownership.js | 42 +++++++++++++++-- packages/svelte/src/internal/client/index.js | 1 + .../CounterBinding.svelte | 7 +++ .../CounterContext.svelte | 13 ++++++ .../_config.js | 34 ++++++++++++++ .../main.svelte | 46 +++++++++++++++++++ 9 files changed, 168 insertions(+), 50 deletions(-) create mode 100644 .changeset/thick-carrots-arrive.md create mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterBinding.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterContext.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/main.svelte diff --git a/.changeset/thick-carrots-arrive.md b/.changeset/thick-carrots-arrive.md new file mode 100644 index 0000000000..582cf5e6e1 --- /dev/null +++ b/.changeset/thick-carrots-arrive.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: recurse into `$derived` for ownership validation diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js index 7b3a9a4d0e..ed800e5226 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js @@ -190,22 +190,21 @@ export function ClassBody(node, context) { 'method', b.id('$.ADD_OWNER'), [b.id('owner')], - Array.from(public_state) - // Only run ownership addition on $state fields. - // Theoretically someone could create a `$state` while creating `$state.raw` or inside a `$derived.by`, - // but that feels so much of an edge case that it doesn't warrant a perf hit for the common case. - .filter(([_, { kind }]) => kind === 'state') - .map(([name]) => - b.stmt( - b.call( - '$.add_owner', - b.call('$.get', b.member(b.this, b.private_id(name))), - b.id('owner'), - b.literal(false), - is_ignored(node, 'ownership_invalid_binding') && b.true - ) + [ + b.stmt( + b.call( + '$.add_owner_to_class', + b.this, + b.id('owner'), + b.array( + Array.from(public_state).map(([name]) => + b.thunk(b.call('$.get', b.member(b.this, b.private_id(name)))) + ) + ), + is_ignored(node, 'ownership_invalid_binding') && b.true ) - ), + ) + ], true ) ); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js index 15e4f68e9e..2bae4486dc 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js @@ -180,37 +180,18 @@ export function build_component(node, component_name, context, anchor = context. const expression = /** @type {Expression} */ (context.visit(attribute.expression)); if (dev && attribute.name !== 'this') { - let should_add_owner = true; - - if (attribute.expression.type !== 'SequenceExpression') { - const left = object(attribute.expression); - - if (left?.type === 'Identifier') { - const binding = context.state.scope.get(left.name); - - // Only run ownership addition on $state fields. - // Theoretically someone could create a `$state` while creating `$state.raw` or inside a `$derived.by`, - // but that feels so much of an edge case that it doesn't warrant a perf hit for the common case. - if (binding?.kind === 'derived' || binding?.kind === 'raw_state') { - should_add_owner = false; - } - } - } - - if (should_add_owner) { - binding_initializers.push( - b.stmt( - b.call( - b.id('$.add_owner_effect'), - expression.type === 'SequenceExpression' - ? expression.expressions[0] - : b.thunk(expression), - b.id(component_name), - is_ignored(node, 'ownership_invalid_binding') && b.true - ) + binding_initializers.push( + b.stmt( + b.call( + b.id('$.add_owner_effect'), + expression.type === 'SequenceExpression' + ? expression.expressions[0] + : b.thunk(expression), + b.id(component_name), + is_ignored(node, 'ownership_invalid_binding') && b.true ) - ); - } + ) + ); } if (expression.type === 'SequenceExpression') { diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index 2a2527803a..62119b36db 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -6,7 +6,7 @@ import { render_effect, user_pre_effect } from '../reactivity/effects.js'; import { dev_current_component_function } from '../context.js'; import { get_prototype_of } from '../../shared/utils.js'; import * as w from '../warnings.js'; -import { FILENAME } from '../../../constants.js'; +import { FILENAME, UNINITIALIZED } from '../../../constants.js'; /** @type {Record<string, Array<{ start: Location, end: Location, component: Function }>>} */ const boundaries = {}; @@ -140,6 +140,25 @@ export function add_owner_effect(get_object, Component, skip_warning = false) { }); } +/** + * @param {any} _this + * @param {Function} owner + * @param {Array<() => any>} getters + * @param {boolean} skip_warning + */ +export function add_owner_to_class(_this, owner, getters, skip_warning) { + _this[ADD_OWNER].current ||= getters.map(() => UNINITIALIZED); + + for (let i = 0; i < getters.length; i += 1) { + const current = getters[i](); + // For performance reasons we only re-add the owner if the state has changed + if (current !== _this[ADD_OWNER][i]) { + _this[ADD_OWNER].current[i] = current; + add_owner(current, owner, false, skip_warning); + } + } +} + /** * @param {ProxyMetadata | null} from * @param {ProxyMetadata} to @@ -196,7 +215,19 @@ function add_owner_to_object(object, owner, seen) { if (proto === Object.prototype) { // recurse until we find a state proxy for (const key in object) { - add_owner_to_object(object[key], owner, seen); + if (Object.getOwnPropertyDescriptor(object, key)?.get) { + // Similar to the class case; the getter could update with a new state + let current = UNINITIALIZED; + render_effect(() => { + const next = object[key]; + if (current !== next) { + current = next; + add_owner_to_object(next, owner, seen); + } + }); + } else { + add_owner_to_object(object[key], owner, seen); + } } } else if (proto === Array.prototype) { // recurse until we find a state proxy @@ -221,9 +252,10 @@ function has_owner(metadata, component) { return ( metadata.owners.has(component) || // This helps avoid false positives when using HMR, where the component function is replaced - [...metadata.owners].some( - (owner) => /** @type {any} */ (owner)[FILENAME] === /** @type {any} */ (component)?.[FILENAME] - ) || + (FILENAME in component && + [...metadata.owners].some( + (owner) => /** @type {any} */ (owner)[FILENAME] === component[FILENAME] + )) || (metadata.parent !== null && has_owner(metadata.parent, component)) ); } diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 8eaa5d66e1..0d64b60496 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -10,6 +10,7 @@ export { mark_module_start, mark_module_end, add_owner_effect, + add_owner_to_class, skip_ownership_validation } from './dev/ownership.js'; export { check_target, legacy_api } from './dev/legacy.js'; diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterBinding.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterBinding.svelte new file mode 100644 index 0000000000..d6da559fb1 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterBinding.svelte @@ -0,0 +1,7 @@ +<script> + let { linked3 = $bindable(), linked4 = $bindable() } = $props(); +</script> + +<p>Binding</p> +<button onclick={() => linked3.count++}>Increment Linked 1 ({linked3.count})</button> +<button onclick={() => linked4.count++}>Increment Linked 2 ({linked4.count})</button> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterContext.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterContext.svelte new file mode 100644 index 0000000000..b935f0a472 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterContext.svelte @@ -0,0 +1,13 @@ +<script> + import { getContext } from 'svelte'; + const linked1 = getContext('linked1'); + const linked2 = getContext('linked2'); +</script> + +<p>Context</p> +<button onclick={() => linked1.linked.current.count++} + >Increment Linked 1 ({linked1.linked.current.count})</button +> +<button onclick={() => linked2.linked.current.count++} + >Increment Linked 2 ({linked2.linked.current.count})</button +> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/_config.js new file mode 100644 index 0000000000..d6d12d01cd --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/_config.js @@ -0,0 +1,34 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +// Tests that ownership is widened with $derived (on class or on its own) that contains $state +export default test({ + compileOptions: { + dev: true + }, + + test({ assert, target, warnings }) { + const [root, counter_context1, counter_context2, counter_binding1, counter_binding2] = + target.querySelectorAll('button'); + + counter_context1.click(); + counter_context2.click(); + counter_binding1.click(); + counter_binding2.click(); + flushSync(); + + assert.equal(warnings.length, 0); + + root.click(); + flushSync(); + counter_context1.click(); + counter_context2.click(); + counter_binding1.click(); + counter_binding2.click(); + flushSync(); + + assert.equal(warnings.length, 0); + }, + + warnings: [] +}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/main.svelte new file mode 100644 index 0000000000..aaade26e16 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/main.svelte @@ -0,0 +1,46 @@ +<script> + import CounterBinding from './CounterBinding.svelte'; + import CounterContext from './CounterContext.svelte'; + import { setContext } from 'svelte'; + + let counter = $state({ count: 0 }); + + class Linked { + #getter; + linked = $derived.by(() => { + const state = $state({ current: $state.snapshot(this.#getter()) }); + return state; + }); + + constructor(fn) { + this.#getter = fn; + } + } + + const linked1 = $derived.by(() => { + const state = $state({ current: $state.snapshot(counter) }); + return state; + }); + const linked2 = new Linked(() => counter); + + setContext('linked1', { + get linked() { + return linked1; + } + }); + setContext('linked2', linked2); + + const linked3 = $derived.by(() => { + const state = $state({ current: $state.snapshot(counter) }); + return state; + }); + const linked4 = new Linked(() => counter); +</script> + +<p>Parent</p> +<button onclick={() => counter.count++}> + Increment Original ({counter.count}) +</button> + +<CounterContext /> +<CounterBinding bind:linked3={linked3.current} bind:linked4={linked4.linked.current} /> From 5fe027286801043050b795169082ed5251e31dd1 Mon Sep 17 00:00:00 2001 From: Kid <44045911+kidonng@users.noreply.github.com> Date: Wed, 12 Feb 2025 17:02:40 +0800 Subject: [PATCH 0171/1429] docs: remove duplicate `onDestroy` description (#15274) --- documentation/docs/06-runtime/03-lifecycle-hooks.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/documentation/docs/06-runtime/03-lifecycle-hooks.md b/documentation/docs/06-runtime/03-lifecycle-hooks.md index a3dbe04b00..2b97ca796f 100644 --- a/documentation/docs/06-runtime/03-lifecycle-hooks.md +++ b/documentation/docs/06-runtime/03-lifecycle-hooks.md @@ -45,8 +45,6 @@ If a function is returned from `onMount`, it will be called when the component i ## `onDestroy` -> EXPORT_SNIPPET: svelte#onDestroy - Schedules a callback to run immediately before the component is unmounted. Out of `onMount`, `beforeUpdate`, `afterUpdate` and `onDestroy`, this is the only one that runs inside a server-side component. From 18481386f3667a13007b5dc40cb40a95382a696b Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Wed, 12 Feb 2025 14:05:25 +0100 Subject: [PATCH 0172/1429] fix: use `importNode` to clone templates for Firefox (#15272) * fix: use `importNode` to clone templates for Firefox * fix: move `is_firefox` check to line 28 * fix: revert using `is_firefox` too soon --- .changeset/slow-meals-wait.md | 5 ++++ .../phases/2-analyze/visitors/Attribute.js | 5 ---- .../client/visitors/RegularElement.js | 5 ---- .../client/dom/elements/attributes.js | 25 ------------------- .../src/internal/client/dom/operations.js | 4 +++ .../src/internal/client/dom/template.js | 4 +-- packages/svelte/src/internal/client/index.js | 1 - .../svelte/src/internal/client/runtime.js | 3 ++- .../_expected/client/index.svelte.js | 6 +---- 9 files changed, 14 insertions(+), 44 deletions(-) create mode 100644 .changeset/slow-meals-wait.md diff --git a/.changeset/slow-meals-wait.md b/.changeset/slow-meals-wait.md new file mode 100644 index 0000000000..e1408e3849 --- /dev/null +++ b/.changeset/slow-meals-wait.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: use `importNode` to clone templates for Firefox diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js index 41144fc74c..42e4498969 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -23,11 +23,6 @@ export function Attribute(node, context) { if (node.name === 'value' && parent.name === 'option') { mark_subtree_dynamic(context.path); } - - // special case <img loading="lazy" /> - if (node.name === 'loading' && parent.name === 'img') { - mark_subtree_dynamic(context.path); - } } if (is_event_attribute(node)) { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index c7e218d521..98036aa9b6 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -300,11 +300,6 @@ export function RegularElement(node, context) { build_class_directives(class_directives, node_id, context, is_attributes_reactive); build_style_directives(style_directives, node_id, context, is_attributes_reactive); - // Apply the src and loading attributes for <img> elements after the element is appended to the document - if (node.name === 'img' && (has_spread || lookup.has('loading'))) { - context.state.after_update.push(b.stmt(b.call('$.handle_lazy_img', node_id))); - } - if ( is_load_error_element(node.name) && (has_spread || has_use || lookup.has('onload') || lookup.has('onerror')) diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 987d1f2086..2dba2d797a 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -523,28 +523,3 @@ function srcset_url_equal(element, srcset) { ) ); } - -/** - * @param {HTMLImageElement} element - * @returns {void} - */ -export function handle_lazy_img(element) { - // If we're using an image that has a lazy loading attribute, we need to apply - // the loading and src after the img element has been appended to the document. - // Otherwise the lazy behaviour will not work due to our cloneNode heuristic for - // templates. - if (!hydrating && element.loading === 'lazy') { - var src = element.src; - // @ts-expect-error - element[LOADING_ATTR_SYMBOL] = null; - element.loading = 'eager'; - element.removeAttribute('src'); - requestAnimationFrame(() => { - // @ts-expect-error - if (element[LOADING_ATTR_SYMBOL] !== 'eager') { - element.loading = 'lazy'; - } - element.src = src; - }); - } -} diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index 627bf917ee..83565d17ae 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -11,6 +11,9 @@ export var $window; /** @type {Document} */ export var $document; +/** @type {boolean} */ +export var is_firefox; + /** @type {() => Node | null} */ var first_child_getter; /** @type {() => Node | null} */ @@ -27,6 +30,7 @@ export function init_operations() { $window = window; $document = document; + is_firefox = /Firefox/.test(navigator.userAgent); var element_prototype = Element.prototype; var node_prototype = Node.prototype; diff --git a/packages/svelte/src/internal/client/dom/template.js b/packages/svelte/src/internal/client/dom/template.js index 3e4f45aba8..6ff3b0fa19 100644 --- a/packages/svelte/src/internal/client/dom/template.js +++ b/packages/svelte/src/internal/client/dom/template.js @@ -1,6 +1,6 @@ /** @import { Effect, TemplateNode } from '#client' */ import { hydrate_next, hydrate_node, hydrating, set_hydrate_node } from './hydration.js'; -import { create_text, get_first_child } from './operations.js'; +import { create_text, get_first_child, is_firefox } from './operations.js'; import { create_fragment_from_html } from './reconciler.js'; import { active_effect } from '../runtime.js'; import { TEMPLATE_FRAGMENT, TEMPLATE_USE_IMPORT_NODE } from '../../../constants.js'; @@ -48,7 +48,7 @@ export function template(content, flags) { } var clone = /** @type {TemplateNode} */ ( - use_import_node ? document.importNode(node, true) : node.cloneNode(true) + use_import_node || is_firefox ? document.importNode(node, true) : node.cloneNode(true) ); if (is_fragment) { diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 0d64b60496..d78f6d452e 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -35,7 +35,6 @@ export { set_attributes, set_custom_element_data, set_xlink_attribute, - handle_lazy_img, set_value, set_checked, set_selected, diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index d5d01c9b6d..75d45d9db9 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -39,6 +39,7 @@ import { set_component_context, set_dev_current_component_function } from './context.js'; +import { is_firefox } from './dom/operations.js'; const FLUSH_MICROTASK = 0; const FLUSH_SYNC = 1; @@ -333,7 +334,7 @@ export function handle_error(error, effect, previous_effect, component_context) current_context = current_context.p; } - const indent = /Firefox/.test(navigator.userAgent) ? ' ' : '\t'; + const indent = is_firefox ? ' ' : '\t'; define_property(error, 'message', { value: error.message + `\n${component_stack.map((name) => `\n${indent}in ${name}`).join('')}\n` }); diff --git a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js index 9b203b97e8..46d376aca2 100644 --- a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js @@ -42,12 +42,8 @@ export default function Skip_static_subtree($$anchor, $$props) { $.reset(select); var img = $.sibling(select, 2); - var div_2 = $.sibling(img, 2); - var img_1 = $.child(div_2); - $.reset(div_2); + $.next(2); $.template_effect(() => $.set_text(text, $$props.title)); - $.handle_lazy_img(img); - $.handle_lazy_img(img_1); $.append($$anchor, fragment); } \ No newline at end of file From 5e52825d608cd99e1bd1b7b278202ee3b8d65740 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Wed, 12 Feb 2025 17:53:15 +0100 Subject: [PATCH 0173/1429] fix: take private and public into account for `constant_assignment` of derived state (#15276) Fixes #15273 --- .changeset/five-apes-develop.md | 5 +++++ .../src/compiler/phases/2-analyze/types.d.ts | 2 +- .../phases/2-analyze/visitors/ClassBody.js | 7 +++++-- .../phases/2-analyze/visitors/shared/utils.js | 14 ++++++++++---- .../errors.json | 1 + .../input.svelte | 13 +++++++++++++ 6 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 .changeset/five-apes-develop.md create mode 100644 packages/svelte/tests/validator/samples/reassign-derived-private-public-field/errors.json create mode 100644 packages/svelte/tests/validator/samples/reassign-derived-private-public-field/input.svelte diff --git a/.changeset/five-apes-develop.md b/.changeset/five-apes-develop.md new file mode 100644 index 0000000000..68e13c94a9 --- /dev/null +++ b/.changeset/five-apes-develop.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: take private and public into account for `constant_assignment` of derived state diff --git a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts index 14b14f9c84..70796a0d59 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts +++ b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts @@ -19,7 +19,7 @@ export interface AnalysisState { component_slots: Set<string>; /** Information about the current expression/directive/block value */ expression: ExpressionMetadata | null; - derived_state: string[]; + derived_state: { name: string; private: boolean }[]; function_depth: number; // legacy stuff diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ClassBody.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ClassBody.js index ed397258f8..0463e4da85 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ClassBody.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ClassBody.js @@ -7,7 +7,7 @@ import { get_rune } from '../../scope.js'; * @param {Context} context */ export function ClassBody(node, context) { - /** @type {string[]} */ + /** @type {{name: string, private: boolean}[]} */ const derived_state = []; for (const definition of node.body) { @@ -18,7 +18,10 @@ export function ClassBody(node, context) { ) { const rune = get_rune(definition.value, context.state.scope); if (rune === '$derived' || rune === '$derived.by') { - derived_state.push(definition.key.name); + derived_state.push({ + name: definition.key.name, + private: definition.key.type === 'PrivateIdentifier' + }); } } } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js index 2d90c85364..1507123e13 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js @@ -1,4 +1,4 @@ -/** @import { AssignmentExpression, Expression, Literal, Node, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */ +/** @import { AssignmentExpression, Expression, Identifier, Literal, Node, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */ /** @import { AST, Binding } from '#compiler' */ /** @import { AnalysisState, Context } from '../../types' */ /** @import { Scope } from '../../../scope' */ @@ -38,16 +38,22 @@ export function validate_assignment(node, argument, state) { e.snippet_parameter_assignment(node); } } - if ( argument.type === 'MemberExpression' && argument.object.type === 'ThisExpression' && (((argument.property.type === 'PrivateIdentifier' || argument.property.type === 'Identifier') && - state.derived_state.includes(argument.property.name)) || + state.derived_state.some( + (derived) => + derived.name === /** @type {PrivateIdentifier | Identifier} */ (argument.property).name && + derived.private === (argument.property.type === 'PrivateIdentifier') + )) || (argument.property.type === 'Literal' && argument.property.value && typeof argument.property.value === 'string' && - state.derived_state.includes(argument.property.value))) + state.derived_state.some( + (derived) => + derived.name === /** @type {Literal} */ (argument.property).value && !derived.private + ))) ) { e.constant_assignment(node, 'derived state'); } diff --git a/packages/svelte/tests/validator/samples/reassign-derived-private-public-field/errors.json b/packages/svelte/tests/validator/samples/reassign-derived-private-public-field/errors.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/packages/svelte/tests/validator/samples/reassign-derived-private-public-field/errors.json @@ -0,0 +1 @@ +[] diff --git a/packages/svelte/tests/validator/samples/reassign-derived-private-public-field/input.svelte b/packages/svelte/tests/validator/samples/reassign-derived-private-public-field/input.svelte new file mode 100644 index 0000000000..92f3564920 --- /dev/null +++ b/packages/svelte/tests/validator/samples/reassign-derived-private-public-field/input.svelte @@ -0,0 +1,13 @@ +<script> + class Test { + #deps = () => []; + + deps = $derived.by(() => { + return []; + }); + + constructor(f = () => []) { + this.#deps = f; + } + } + </script> \ No newline at end of file From f747c412f49a59be0873c6f875cf90faced737d3 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Wed, 12 Feb 2025 16:55:04 +0000 Subject: [PATCH 0174/1429] chore: tweak effect self invalidation logic (#15275) Also make sure events dispatched via transition/animation logic runs outside event context Related to #15262 --- .changeset/rare-hounds-wave.md | 5 +++++ .../internal/client/dom/elements/transitions.js | 5 ++++- packages/svelte/src/internal/client/runtime.js | 14 ++++++-------- 3 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 .changeset/rare-hounds-wave.md diff --git a/.changeset/rare-hounds-wave.md b/.changeset/rare-hounds-wave.md new file mode 100644 index 0000000000..11b368b038 --- /dev/null +++ b/.changeset/rare-hounds-wave.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: tweak effect self invalidation logic, run transition dispatches without reactive context diff --git a/packages/svelte/src/internal/client/dom/elements/transitions.js b/packages/svelte/src/internal/client/dom/elements/transitions.js index b3c16cdd08..fbc1da95df 100644 --- a/packages/svelte/src/internal/client/dom/elements/transitions.js +++ b/packages/svelte/src/internal/client/dom/elements/transitions.js @@ -14,6 +14,7 @@ import { current_each_item } from '../blocks/each.js'; import { TRANSITION_GLOBAL, TRANSITION_IN, TRANSITION_OUT } from '../../../../constants.js'; import { BLOCK_EFFECT, EFFECT_RAN, EFFECT_TRANSPARENT } from '../../constants.js'; import { queue_micro_task } from '../task.js'; +import { without_reactive_context } from './bindings/shared.js'; /** * @param {Element} element @@ -21,7 +22,9 @@ import { queue_micro_task } from '../task.js'; * @returns {void} */ function dispatch_event(element, type) { - element.dispatchEvent(new CustomEvent(type)); + without_reactive_context(() => { + element.dispatchEvent(new CustomEvent(type)); + }); } /** diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 75d45d9db9..8a9ca9065b 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -370,22 +370,18 @@ export function handle_error(error, effect, previous_effect, component_context) /** * @param {Value} signal * @param {Effect} effect - * @param {number} [depth] + * @param {boolean} [root] */ -function schedule_possible_effect_self_invalidation(signal, effect, depth = 0) { +function schedule_possible_effect_self_invalidation(signal, effect, root = true) { var reactions = signal.reactions; if (reactions === null) return; for (var i = 0; i < reactions.length; i++) { var reaction = reactions[i]; if ((reaction.f & DERIVED) !== 0) { - schedule_possible_effect_self_invalidation( - /** @type {Derived} */ (reaction), - effect, - depth + 1 - ); + schedule_possible_effect_self_invalidation(/** @type {Derived} */ (reaction), effect, false); } else if (effect === reaction) { - if (depth === 0) { + if (root) { set_signal_status(reaction, DIRTY); } else if ((reaction.f & CLEAN) !== 0) { set_signal_status(reaction, MAYBE_DIRTY); @@ -458,6 +454,8 @@ export function update_reaction(reaction) { if ( is_runes() && untracked_writes !== null && + !untracking && + deps !== null && (reaction.f & (DERIVED | MAYBE_DIRTY | DIRTY)) === 0 ) { for (i = 0; i < /** @type {Source[]} */ (untracked_writes).length; i++) { From 23ecc364da255444208d5b08a56c44f7c3f2dcc1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 17:56:15 +0100 Subject: [PATCH 0175/1429] Version Packages (#15271) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/five-apes-develop.md | 5 ----- .changeset/fuzzy-zoos-repeat.md | 5 ----- .changeset/hip-singers-vanish.md | 5 ----- .changeset/rare-hounds-wave.md | 5 ----- .changeset/slow-meals-wait.md | 5 ----- .changeset/thick-carrots-arrive.md | 5 ----- packages/svelte/CHANGELOG.md | 18 ++++++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 9 files changed, 20 insertions(+), 32 deletions(-) delete mode 100644 .changeset/five-apes-develop.md delete mode 100644 .changeset/fuzzy-zoos-repeat.md delete mode 100644 .changeset/hip-singers-vanish.md delete mode 100644 .changeset/rare-hounds-wave.md delete mode 100644 .changeset/slow-meals-wait.md delete mode 100644 .changeset/thick-carrots-arrive.md diff --git a/.changeset/five-apes-develop.md b/.changeset/five-apes-develop.md deleted file mode 100644 index 68e13c94a9..0000000000 --- a/.changeset/five-apes-develop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: take private and public into account for `constant_assignment` of derived state diff --git a/.changeset/fuzzy-zoos-repeat.md b/.changeset/fuzzy-zoos-repeat.md deleted file mode 100644 index 3fb3f0502e..0000000000 --- a/.changeset/fuzzy-zoos-repeat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: value/checked not correctly set using spread diff --git a/.changeset/hip-singers-vanish.md b/.changeset/hip-singers-vanish.md deleted file mode 100644 index 9dce4d98a8..0000000000 --- a/.changeset/hip-singers-vanish.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: SSR-safe ID generation with `$props.id()` diff --git a/.changeset/rare-hounds-wave.md b/.changeset/rare-hounds-wave.md deleted file mode 100644 index 11b368b038..0000000000 --- a/.changeset/rare-hounds-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: tweak effect self invalidation logic, run transition dispatches without reactive context diff --git a/.changeset/slow-meals-wait.md b/.changeset/slow-meals-wait.md deleted file mode 100644 index e1408e3849..0000000000 --- a/.changeset/slow-meals-wait.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: use `importNode` to clone templates for Firefox diff --git a/.changeset/thick-carrots-arrive.md b/.changeset/thick-carrots-arrive.md deleted file mode 100644 index 582cf5e6e1..0000000000 --- a/.changeset/thick-carrots-arrive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: recurse into `$derived` for ownership validation diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index e112bf6209..ff3b08fe7a 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,23 @@ # svelte +## 5.20.0 + +### Minor Changes + +- feat: SSR-safe ID generation with `$props.id()` ([#15185](https://github.com/sveltejs/svelte/pull/15185)) + +### Patch Changes + +- fix: take private and public into account for `constant_assignment` of derived state ([#15276](https://github.com/sveltejs/svelte/pull/15276)) + +- fix: value/checked not correctly set using spread ([#15239](https://github.com/sveltejs/svelte/pull/15239)) + +- chore: tweak effect self invalidation logic, run transition dispatches without reactive context ([#15275](https://github.com/sveltejs/svelte/pull/15275)) + +- fix: use `importNode` to clone templates for Firefox ([#15272](https://github.com/sveltejs/svelte/pull/15272)) + +- fix: recurse into `$derived` for ownership validation ([#15166](https://github.com/sveltejs/svelte/pull/15166)) + ## 5.19.10 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index a4594b2a5c..bea1efd7b4 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.19.10", + "version": "5.20.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index ada6f9019d..b246076156 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.19.10'; +export const VERSION = '5.20.0'; export const PUBLIC_VERSION = '5'; From 21dadfc997c5572730e607bd98d513437215dd06 Mon Sep 17 00:00:00 2001 From: Caique Torres <56696506+caiquetorres@users.noreply.github.com> Date: Wed, 12 Feb 2025 14:14:14 -0300 Subject: [PATCH 0176/1429] fix: ignore typescript abstract methods during code transformation (#15267) --- .changeset/short-fireants-talk.md | 5 +++++ .../src/compiler/phases/1-parse/remove_typescript_nodes.js | 6 ++++++ .../tests/runtime-runes/samples/typescript/main.svelte | 5 +++++ 3 files changed, 16 insertions(+) create mode 100644 .changeset/short-fireants-talk.md diff --git a/.changeset/short-fireants-talk.md b/.changeset/short-fireants-talk.md new file mode 100644 index 0000000000..c26bdc9c8b --- /dev/null +++ b/.changeset/short-fireants-talk.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ignore typescript abstract methods diff --git a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js index 18c805128d..09eb0bfa68 100644 --- a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js +++ b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js @@ -118,6 +118,12 @@ const visitors = { delete node.implements; return context.next(); }, + MethodDefinition(node, context) { + if (node.abstract) { + return b.empty; + } + return context.next(); + }, VariableDeclaration(node, context) { if (node.declare) { return b.empty; diff --git a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte index cd23b31096..e2942b21f3 100644 --- a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte @@ -22,6 +22,11 @@ class MyClass implements Hello {} + abstract class MyAbstractClass { + abstract x(): void; + y() {} + } + declare const declared_const: number; declare function declared_fn(): void; declare class declared_class { From b6bf735a23b36927d7f3b2414bc0555db5d5c567 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Thu, 13 Feb 2025 23:09:56 +0100 Subject: [PATCH 0177/1429] fix: correctly ssr component in `svelte:head` with `$props.id()` or `css='injected'` (#15291) --- .changeset/thirty-feet-tap.md | 5 +++++ packages/svelte/src/internal/server/index.js | 9 ++++++--- packages/svelte/src/internal/server/types.d.ts | 2 ++ .../samples/head-component-props-id/HeadNested.svelte | 5 +++++ .../samples/head-component-props-id/_config.js | 3 +++ .../samples/head-component-props-id/main.svelte | 8 ++++++++ 6 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 .changeset/thirty-feet-tap.md create mode 100644 packages/svelte/tests/server-side-rendering/samples/head-component-props-id/HeadNested.svelte create mode 100644 packages/svelte/tests/server-side-rendering/samples/head-component-props-id/_config.js create mode 100644 packages/svelte/tests/server-side-rendering/samples/head-component-props-id/main.svelte diff --git a/.changeset/thirty-feet-tap.md b/.changeset/thirty-feet-tap.md new file mode 100644 index 0000000000..5f7e589064 --- /dev/null +++ b/.changeset/thirty-feet-tap.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly ssr component in `svelte:head` with `$props.id()` or `css='injected'` diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index c4e5d318dc..728f2ebc2a 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -34,7 +34,9 @@ export function copy_payload({ out, css, head, uid }) { css: new Set(css), head: { title: head.title, - out: head.out + out: head.out, + css: new Set(head.css), + uid: head.uid }, uid }; @@ -99,12 +101,13 @@ function props_id_generator() { * @returns {RenderOutput} */ export function render(component, options = {}) { + const uid = options.uid ?? props_id_generator(); /** @type {Payload} */ const payload = { out: '', css: new Set(), - head: { title: '', out: '' }, - uid: options.uid ?? props_id_generator() + head: { title: '', out: '', css: new Set(), uid }, + uid }; const prev_on_destroy = on_destroy; diff --git a/packages/svelte/src/internal/server/types.d.ts b/packages/svelte/src/internal/server/types.d.ts index 8a241deecd..2fffdbbdf0 100644 --- a/packages/svelte/src/internal/server/types.d.ts +++ b/packages/svelte/src/internal/server/types.d.ts @@ -17,6 +17,8 @@ export interface Payload { head: { title: string; out: string; + uid: () => string; + css: Set<{ hash: string; code: string }>; }; /** Function that generates a unique ID */ uid: () => string; diff --git a/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/HeadNested.svelte b/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/HeadNested.svelte new file mode 100644 index 0000000000..0784208798 --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/HeadNested.svelte @@ -0,0 +1,5 @@ +<script> + let id = $props.id(); +</script> + +<meta name="id" content={id} /> \ No newline at end of file diff --git a/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/_config.js b/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/_config.js new file mode 100644 index 0000000000..f47bee71df --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({}); diff --git a/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/main.svelte b/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/main.svelte new file mode 100644 index 0000000000..e32b40e9ed --- /dev/null +++ b/packages/svelte/tests/server-side-rendering/samples/head-component-props-id/main.svelte @@ -0,0 +1,8 @@ +<script> + import HeadNested from './HeadNested.svelte'; +</script> + +<svelte:head> + <HeadNested /> +</svelte:head> + From 8b977253f3573978f3678ed0d618a6a75f32cb1a Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Fri, 14 Feb 2025 16:22:52 +0100 Subject: [PATCH 0178/1429] chore: fix `$props.id` tests (#15294) * chore: fix `$props.id` tests * chore: reset uid between tests --- packages/svelte/src/internal/client/dom/template.js | 4 ++++ packages/svelte/tests/runtime-legacy/shared.ts | 2 ++ .../svelte/tests/runtime-runes/samples/props-id/_config.js | 4 +--- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/template.js b/packages/svelte/src/internal/client/dom/template.js index 6ff3b0fa19..575bf55cf6 100644 --- a/packages/svelte/src/internal/client/dom/template.js +++ b/packages/svelte/src/internal/client/dom/template.js @@ -252,6 +252,10 @@ export function append(anchor, dom) { let uid = 1; +export function reset_props_id() { + uid = 1; +} + /** * Create (or hydrate) an unique UID for the component instance. */ diff --git a/packages/svelte/tests/runtime-legacy/shared.ts b/packages/svelte/tests/runtime-legacy/shared.ts index e6dc0f385b..76036852c2 100644 --- a/packages/svelte/tests/runtime-legacy/shared.ts +++ b/packages/svelte/tests/runtime-legacy/shared.ts @@ -11,6 +11,7 @@ import { setup_html_equal } from '../html_equal.js'; import { raf } from '../animation-helpers.js'; import type { CompileOptions } from '#compiler'; import { suite_with_variants, type BaseTest } from '../suite.js'; +import { reset_props_id } from '../../src/internal/client/dom/template.js'; type Assert = typeof import('vitest').assert & { htmlEqual(a: string, b: string, description?: string): void; @@ -345,6 +346,7 @@ async function run_test_variant( if (runes) { props = proxy({ ...(config.props || {}) }); + reset_props_id(); if (manual_hydrate) { hydrate_fn = () => { instance = hydrate(mod.default, { diff --git a/packages/svelte/tests/runtime-runes/samples/props-id/_config.js b/packages/svelte/tests/runtime-runes/samples/props-id/_config.js index 9d91b98e0f..416ef6cfbe 100644 --- a/packages/svelte/tests/runtime-runes/samples/props-id/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/props-id/_config.js @@ -43,8 +43,6 @@ export default test({ ` ); } else { - // `c6` because this runs after the `dom` tests - // (slightly brittle but good enough for now) assert.htmlEqual( target.innerHTML, ` @@ -53,7 +51,7 @@ export default test({ <p>s2</p> <p>s3</p> <p>s4</p> - <p>c6</p> + <p>c1</p> ` ); } From 7e3253745e3921d5718dce3ee5d5a9e72d471f58 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Sat, 15 Feb 2025 00:03:52 +0100 Subject: [PATCH 0179/1429] fix: ensure AST analysis on `svelte.js` modules succeeds (#15297) This was the result of a `@ts-expect-error` silencing other type errors, which lead to this creeping in. This changes it so that the object is fully set, so we'll get type errors when new properties need to be added fixes #15284 --- .changeset/good-spiders-own.md | 5 +++++ .../src/compiler/phases/2-analyze/index.js | 16 ++++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 .changeset/good-spiders-own.md diff --git a/.changeset/good-spiders-own.md b/.changeset/good-spiders-own.md new file mode 100644 index 0000000000..aacbda133e --- /dev/null +++ b/.changeset/good-spiders-own.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure AST analysis on `svelte.js` modules succeeds diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 846abcf7df..93c438c852 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -258,8 +258,20 @@ export function analyze_module(ast, options) { { scope, scopes, - // @ts-expect-error TODO - analysis + analysis: /** @type {ComponentAnalysis} */ (analysis), + derived_state: [], + // TODO the following are not needed for modules, but we have to pass them in order to avoid type error, + // and reducing the type would result in a lot of tedious type casts elsewhere - find a good solution one day + ast_type: /** @type {any} */ (null), + component_slots: new Set(), + expression: null, + function_depth: 0, + has_props_rune: false, + instance_scope: /** @type {any} */ (null), + options: /** @type {ValidatedCompileOptions} */ (options), + parent_element: null, + reactive_statement: null, + reactive_statements: new Map() }, visitors ); From 32b78c995422c59ade2359de86f87b0cb5239e53 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 00:05:21 +0100 Subject: [PATCH 0180/1429] Version Packages (#15279) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/good-spiders-own.md | 5 ----- .changeset/short-fireants-talk.md | 5 ----- .changeset/thirty-feet-tap.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 .changeset/good-spiders-own.md delete mode 100644 .changeset/short-fireants-talk.md delete mode 100644 .changeset/thirty-feet-tap.md diff --git a/.changeset/good-spiders-own.md b/.changeset/good-spiders-own.md deleted file mode 100644 index aacbda133e..0000000000 --- a/.changeset/good-spiders-own.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure AST analysis on `svelte.js` modules succeeds diff --git a/.changeset/short-fireants-talk.md b/.changeset/short-fireants-talk.md deleted file mode 100644 index c26bdc9c8b..0000000000 --- a/.changeset/short-fireants-talk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ignore typescript abstract methods diff --git a/.changeset/thirty-feet-tap.md b/.changeset/thirty-feet-tap.md deleted file mode 100644 index 5f7e589064..0000000000 --- a/.changeset/thirty-feet-tap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly ssr component in `svelte:head` with `$props.id()` or `css='injected'` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index ff3b08fe7a..948a914778 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.20.1 + +### Patch Changes + +- fix: ensure AST analysis on `svelte.js` modules succeeds ([#15297](https://github.com/sveltejs/svelte/pull/15297)) + +- fix: ignore typescript abstract methods ([#15267](https://github.com/sveltejs/svelte/pull/15267)) + +- fix: correctly ssr component in `svelte:head` with `$props.id()` or `css='injected'` ([#15291](https://github.com/sveltejs/svelte/pull/15291)) + ## 5.20.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index bea1efd7b4..3261243832 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.20.0", + "version": "5.20.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b246076156..9f1873efc7 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.20.0'; +export const VERSION = '5.20.1'; export const PUBLIC_VERSION = '5'; From c40392c1e8437ae564737d75cbbc4ef3ebf7b0f0 Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Sat, 15 Feb 2025 09:39:44 +0100 Subject: [PATCH 0181/1429] chore: remove options.uid in render() (#15302) * remove options.uid in render() * changeset * chore: update changeset --------- Co-authored-by: Paolo Ricciuti <ricciutipaolo@gmail.com> --- .changeset/kind-kiwis-train.md | 5 +++++ packages/svelte/src/internal/server/index.js | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/kind-kiwis-train.md diff --git a/.changeset/kind-kiwis-train.md b/.changeset/kind-kiwis-train.md new file mode 100644 index 0000000000..c40cf761b2 --- /dev/null +++ b/.changeset/kind-kiwis-train.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: remove unused `options.uid` in `render` diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 728f2ebc2a..e8ffeed2fe 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -97,11 +97,11 @@ function props_id_generator() { * Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app. * @template {Record<string, any>} Props * @param {import('svelte').Component<Props> | ComponentType<SvelteComponent<Props>>} component - * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>, uid?: () => string }} [options] + * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> }} [options] * @returns {RenderOutput} */ export function render(component, options = {}) { - const uid = options.uid ?? props_id_generator(); + const uid = props_id_generator(); /** @type {Payload} */ const payload = { out: '', From 75f81991c27e9602d4bb3eb44aec8775de0713af Mon Sep 17 00:00:00 2001 From: Eric Liu <ericyl.us@gmail.com> Date: Sun, 16 Feb 2025 23:29:22 -0800 Subject: [PATCH 0182/1429] docs(bindable): fix `file:` annotation for App.svelte (#15314) --- documentation/docs/02-runes/06-$bindable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/02-runes/06-$bindable.md b/documentation/docs/02-runes/06-$bindable.md index 14bc8ddbec..c12c2bf490 100644 --- a/documentation/docs/02-runes/06-$bindable.md +++ b/documentation/docs/02-runes/06-$bindable.md @@ -33,7 +33,7 @@ Now, a component that uses `<FancyInput>` can add the [`bind:`](bind) directive <!-- prettier-ignore --> ```svelte -/// App.svelte +/// file: App.svelte <script> import FancyInput from './FancyInput.svelte'; From 87d7cc7d892f41bd662cb85c54c14cb72ce6d017 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Mon, 17 Feb 2025 04:36:30 -0800 Subject: [PATCH 0183/1429] chore: add missing await in tests (#15301) --- packages/svelte/tests/preprocess/test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/tests/preprocess/test.ts b/packages/svelte/tests/preprocess/test.ts index 08f237e286..81030a6346 100644 --- a/packages/svelte/tests/preprocess/test.ts +++ b/packages/svelte/tests/preprocess/test.ts @@ -25,7 +25,7 @@ const { test, run } = suite<PreprocessTest>(async (config, cwd) => { fs.writeFileSync(`${cwd}/_actual.html.map`, JSON.stringify(result.map, null, 2)); } - expect(result.code).toMatchFileSnapshot(`${cwd}/output.svelte`); + await expect(result.code).toMatchFileSnapshot(`${cwd}/output.svelte`); expect(result.dependencies).toEqual(config.dependencies || []); From dde8603872920f4f9c5e2d7682ed6adbfce1bf4b Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Mon, 17 Feb 2025 15:33:07 +0100 Subject: [PATCH 0184/1429] fix: do not warn for `binding_property_non_reactive` if binding is a store in an each (#15318) Fixes #15315 --- .changeset/little-laws-sit.md | 5 +++++ .../src/compiler/phases/3-transform/client/types.d.ts | 1 + .../phases/3-transform/client/visitors/EachBlock.js | 3 ++- .../phases/3-transform/client/visitors/shared/utils.js | 6 +++++- .../samples/each-bind-store-no-warning/_config.js | 10 ++++++++++ .../samples/each-bind-store-no-warning/main.svelte | 10 ++++++++++ 6 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 .changeset/little-laws-sit.md create mode 100644 packages/svelte/tests/runtime-runes/samples/each-bind-store-no-warning/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/each-bind-store-no-warning/main.svelte diff --git a/.changeset/little-laws-sit.md b/.changeset/little-laws-sit.md new file mode 100644 index 0000000000..a395ea6213 --- /dev/null +++ b/.changeset/little-laws-sit.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: do not warn for `binding_property_non_reactive` if binding is a store in an each diff --git a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts index db4adf451c..63fe3223cf 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts +++ b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts @@ -45,6 +45,7 @@ export interface ComponentClientTransformState extends ClientTransformState { readonly hoisted: Array<Statement | ModuleDeclaration>; readonly events: Set<string>; readonly is_instance: boolean; + readonly store_to_invalidate?: string; /** Stuff that happens before the render effect(s) */ readonly init: Statement[]; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js index 24a696e7d2..629cacda01 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js @@ -143,7 +143,8 @@ export function EachBlock(node, context) { const child_state = { ...context.state, - transform: { ...context.state.transform } + transform: { ...context.state.transform }, + store_to_invalidate }; /** The state used when generating the key function, if necessary */ diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index 9214a13c94..ca5094f455 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -309,12 +309,16 @@ export function validate_binding(state, binding, expression) { const loc = locator(binding.start); + const obj = /** @type {Expression} */ (expression.object); + state.init.push( b.stmt( b.call( '$.validate_binding', b.literal(state.analysis.source.slice(binding.start, binding.end)), - b.thunk(/** @type {Expression} */ (expression.object)), + b.thunk( + state.store_to_invalidate ? b.sequence([b.call('$.mark_store_binding'), obj]) : obj + ), b.thunk( /** @type {Expression} */ ( expression.computed diff --git a/packages/svelte/tests/runtime-runes/samples/each-bind-store-no-warning/_config.js b/packages/svelte/tests/runtime-runes/samples/each-bind-store-no-warning/_config.js new file mode 100644 index 0000000000..dba2e85650 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/each-bind-store-no-warning/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + async test({ assert, warnings }) { + assert.deepEqual(warnings, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/each-bind-store-no-warning/main.svelte b/packages/svelte/tests/runtime-runes/samples/each-bind-store-no-warning/main.svelte new file mode 100644 index 0000000000..f927bf079a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/each-bind-store-no-warning/main.svelte @@ -0,0 +1,10 @@ +<svelte:options runes /> +<script> + import { writable } from 'svelte/store'; + + const array = writable([{ name: "" }]); +</script> + +{#each $array as item} + <div><input bind:value={item.name} /></div> +{/each} From 073f4d8911a63164d685152cef973631c3110dc8 Mon Sep 17 00:00:00 2001 From: tomoam <29677552+tomoam@users.noreply.github.com> Date: Mon, 17 Feb 2025 23:52:56 +0900 Subject: [PATCH 0185/1429] fix: prevent writable store value from becoming a proxy when reassigning using $-prefix (#15283) fixes #15281 --- .changeset/lovely-chairs-compete.md | 5 +++++ .../client/visitors/AssignmentExpression.js | 1 + .../samples/store-reassign-object/_config.js | 7 +++++++ .../samples/store-reassign-object/main.svelte | 11 +++++++++++ 4 files changed, 24 insertions(+) create mode 100644 .changeset/lovely-chairs-compete.md create mode 100644 packages/svelte/tests/runtime-runes/samples/store-reassign-object/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/store-reassign-object/main.svelte diff --git a/.changeset/lovely-chairs-compete.md b/.changeset/lovely-chairs-compete.md new file mode 100644 index 0000000000..d311a3dde6 --- /dev/null +++ b/.changeset/lovely-chairs-compete.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent writable store value from becoming a proxy when reassigning using $-prefix diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js index 0c70f7e00c..a8c615af93 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js @@ -118,6 +118,7 @@ function build_assignment(operator, left, right, context) { binding.kind !== 'prop' && binding.kind !== 'bindable_prop' && binding.kind !== 'raw_state' && + binding.kind !== 'store_sub' && context.state.analysis.runes && should_proxy(right, context.state.scope) && is_non_coercive_operator(operator) diff --git a/packages/svelte/tests/runtime-runes/samples/store-reassign-object/_config.js b/packages/svelte/tests/runtime-runes/samples/store-reassign-object/_config.js new file mode 100644 index 0000000000..f9a329889d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/store-reassign-object/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + async test({ target, assert }) { + assert.htmlEqual(target.innerHTML, `<p>bar</p>`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/store-reassign-object/main.svelte b/packages/svelte/tests/runtime-runes/samples/store-reassign-object/main.svelte new file mode 100644 index 0000000000..ecffbb2d83 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/store-reassign-object/main.svelte @@ -0,0 +1,11 @@ +<script> + import { writable } from "svelte/store"; + + const obj = writable({ name: 'foo' }); + + $obj = { name: 'bar' }; + + const clone = structuredClone($obj); +</script> + +<p>{clone.name}</p> From 575900de885c84aa6498e3ec121eabf7bba6ae4b Mon Sep 17 00:00:00 2001 From: Ranjan Purbey <ranjan.purbey@gmail.com> Date: Mon, 17 Feb 2025 21:42:35 +0530 Subject: [PATCH 0186/1429] fix: Add more checks to determine if element is non-static (#15259) Fixes #15241 --- .changeset/unlucky-laws-decide.md | 5 +++++ .../3-transform/client/visitors/shared/fragment.js | 11 +++++++++++ .../samples/element-dir-attribute-sibling/_config.js | 9 +++++++++ .../samples/element-dir-attribute-sibling/main.svelte | 1 + .../input-checked-attribute-sibling/_config.js | 9 +++++++++ .../input-checked-attribute-sibling/_expected.html | 1 + .../input-checked-attribute-sibling/main.svelte | 1 + 7 files changed, 37 insertions(+) create mode 100644 .changeset/unlucky-laws-decide.md create mode 100644 packages/svelte/tests/hydration/samples/element-dir-attribute-sibling/_config.js create mode 100644 packages/svelte/tests/hydration/samples/element-dir-attribute-sibling/main.svelte create mode 100644 packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/_config.js create mode 100644 packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/_expected.html create mode 100644 packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/main.svelte diff --git a/.changeset/unlucky-laws-decide.md b/.changeset/unlucky-laws-decide.md new file mode 100644 index 0000000000..c5fd7c65d9 --- /dev/null +++ b/.changeset/unlucky-laws-decide.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure input elements and elements with `dir` attribute are marked as non-static diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js index 5bc3041ca4..f076d7c11e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js @@ -144,6 +144,17 @@ function is_static_element(node, state) { return false; } + if (attribute.name === 'dir') { + return false; + } + + if ( + ['input', 'textarea'].includes(node.name) && + ['value', 'checked'].includes(attribute.name) + ) { + return false; + } + if (node.name === 'option' && attribute.name === 'value') { return false; } diff --git a/packages/svelte/tests/hydration/samples/element-dir-attribute-sibling/_config.js b/packages/svelte/tests/hydration/samples/element-dir-attribute-sibling/_config.js new file mode 100644 index 0000000000..e3c629aef9 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/element-dir-attribute-sibling/_config.js @@ -0,0 +1,9 @@ +import { test } from '../../test'; + +export default test({ + test(assert, target) { + const p = target.querySelector('p'); + + assert.equal(p?.dir, 'rtl'); + } +}); diff --git a/packages/svelte/tests/hydration/samples/element-dir-attribute-sibling/main.svelte b/packages/svelte/tests/hydration/samples/element-dir-attribute-sibling/main.svelte new file mode 100644 index 0000000000..802edc0fee --- /dev/null +++ b/packages/svelte/tests/hydration/samples/element-dir-attribute-sibling/main.svelte @@ -0,0 +1 @@ +<p dir="rtl">text</p>. diff --git a/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/_config.js b/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/_config.js new file mode 100644 index 0000000000..31ec66fc88 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/_config.js @@ -0,0 +1,9 @@ +import { test } from '../../test'; + +export default test({ + test(assert, target) { + const input = target.querySelector('input'); + + assert.equal(input?.checked, true); + } +}); diff --git a/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/_expected.html b/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/_expected.html new file mode 100644 index 0000000000..bcd53f8783 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/_expected.html @@ -0,0 +1 @@ +<!--[--><!---->.<input><!--]--> diff --git a/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/main.svelte b/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/main.svelte new file mode 100644 index 0000000000..db3eae870f --- /dev/null +++ b/packages/svelte/tests/hydration/samples/input-checked-attribute-sibling/main.svelte @@ -0,0 +1 @@ +.<input checked /> From 0d5d9ab76080ecc61c0f9eec6670e5ac29f4d078 Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Mon, 17 Feb 2025 22:12:59 +0100 Subject: [PATCH 0187/1429] docs: fix two element_invalid_self_closing_tag warning in example (#15324) * code is incorrect (warning element_invalid_self_closing_tag) * close canvas --- documentation/docs/03-template-syntax/11-bind.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/docs/03-template-syntax/11-bind.md b/documentation/docs/03-template-syntax/11-bind.md index 119f87ed8e..c23f3b5232 100644 --- a/documentation/docs/03-template-syntax/11-bind.md +++ b/documentation/docs/03-template-syntax/11-bind.md @@ -267,7 +267,7 @@ Elements with the `contenteditable` attribute support the following bindings: <!-- for some reason puts the comment and html on same line --> <!-- prettier-ignore --> ```svelte -<div contenteditable="true" bind:innerHTML={html} /> +<div contenteditable="true" bind:innerHTML={html}></div> ``` ## Dimensions @@ -307,7 +307,7 @@ To get a reference to a DOM node, use `bind:this`. The value will be `undefined` }); </script> -<canvas bind:this={canvas} /> +<canvas bind:this={canvas}></canvas> ``` Components also support `bind:this`, allowing you to interact with component instances programmatically. From 9091e7e930a80695ba951bfdb8170791322651fe Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 17 Feb 2025 22:16:09 +0100 Subject: [PATCH 0188/1429] fix: fire delegated events on target even it was disabled in the meantime (#15319) They DOM could've been updated already by the time the event bubbling logic is reached, so the target element itself could not be notified of the event because it is marked as disabled. But the target could not have been disabled because it emits the event in the first place, so we emit regardless. No test because not reproducible in jsdom environment. Fixes #15256 --- .changeset/wicked-apples-yawn.md | 5 +++++ .../svelte/src/internal/client/dom/elements/events.js | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .changeset/wicked-apples-yawn.md diff --git a/.changeset/wicked-apples-yawn.md b/.changeset/wicked-apples-yawn.md new file mode 100644 index 0000000000..3022c34fd0 --- /dev/null +++ b/.changeset/wicked-apples-yawn.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: fire delegated events on target even it was disabled in the meantime diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js index 363b8e1ed5..25ece5f569 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -237,7 +237,13 @@ export function handle_event_propagation(event) { // @ts-expect-error var delegated = current_target['__' + event_name]; - if (delegated !== undefined && !(/** @type {any} */ (current_target).disabled)) { + if ( + delegated !== undefined && + (!(/** @type {any} */ (current_target).disabled) || + // DOM could've been updated already by the time this is reached, so we check this as well + // -> the target could not have been disabled because it emits the event in the first place + event.target === current_target) + ) { if (is_array(delegated)) { var [fn, ...data] = delegated; fn.apply(current_target, [event, ...data]); From d8e78f7f578488d6f8a66b6c6ca15086768d3b34 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Tue, 18 Feb 2025 13:33:11 +0100 Subject: [PATCH 0189/1429] =?UTF-8?q?fix:=20`muted`=20reactive=20without?= =?UTF-8?q?=20`bind`=20and=20select/autofocus=20attributes=20=E2=80=A6=20(?= =?UTF-8?q?#15326)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: `muted` reactive without `bind` and select/autofocus attributes working with function calls Co-authored-by: Joel Howse <joel@helicalco.com> * chore: add comments --------- Co-authored-by: Joel Howse <joel@helicalco.com> --- .changeset/silver-ties-itch.md | 5 +++ .../client/visitors/RegularElement.js | 38 ++++++++++++++----- .../samples/autofocus-with-call/_config.js | 7 ++++ .../samples/autofocus-with-call/main.svelte | 6 +++ .../muted-without-bind-works/_config.js | 13 +++++++ .../muted-without-bind-works/main.svelte | 13 +++++++ .../samples/select-value-with-call/_config.js | 7 ++++ .../select-value-with-call/main.svelte | 6 +++ 8 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 .changeset/silver-ties-itch.md create mode 100644 packages/svelte/tests/runtime-runes/samples/autofocus-with-call/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/autofocus-with-call/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/select-value-with-call/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/select-value-with-call/main.svelte diff --git a/.changeset/silver-ties-itch.md b/.changeset/silver-ties-itch.md new file mode 100644 index 0000000000..67e33dadfb --- /dev/null +++ b/.changeset/silver-ties-itch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: `muted` reactive without `bind` and select/autofocus attributes working with function calls diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 98036aa9b6..018bdacc5e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -29,7 +29,8 @@ import { build_render_statement, build_template_chunk, build_update_assignment, - get_expression_id + get_expression_id, + memoize_expression } from './shared/utils.js'; import { visit_event_attribute } from './shared/events.js'; @@ -532,18 +533,30 @@ function build_element_attribute_update_assignment( const is_svg = context.state.metadata.namespace === 'svg' || element.name === 'svg'; const is_mathml = context.state.metadata.namespace === 'mathml'; + const is_autofocus = name === 'autofocus'; + let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => - metadata.has_call ? get_expression_id(state, value) : value + metadata.has_call + ? // if it's autofocus we will not add this to a template effect so we don't want to get the expression id + // but separately memoize the expression + is_autofocus + ? memoize_expression(state, value) + : get_expression_id(state, value) + : value ); - if (name === 'autofocus') { + if (is_autofocus) { state.init.push(b.stmt(b.call('$.autofocus', node_id, value))); return false; } // Special case for Firefox who needs it set as a property in order to work if (name === 'muted') { - state.init.push(b.stmt(b.assignment('=', b.member(node_id, b.id('muted')), value))); + if (!has_state) { + state.init.push(b.stmt(b.assignment('=', b.member(node_id, b.id('muted')), value))); + return false; + } + state.update.push(b.stmt(b.assignment('=', b.member(node_id, b.id('muted')), value))); return false; } @@ -660,8 +673,18 @@ function build_custom_element_attribute_update_assignment(node_id, attribute, co */ function build_element_special_value_attribute(element, node_id, attribute, context) { const state = context.state; + const is_select_with_value = + // attribute.metadata.dynamic would give false negatives because even if the value does not change, + // the inner options could still change, so we need to always treat it as reactive + element === 'select' && attribute.value !== true && !is_text_attribute(attribute); + const { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => - metadata.has_call ? get_expression_id(state, value) : value + metadata.has_call + ? // if is a select with value we will also invoke `init_select` which need a reference before the template effect so we memoize separately + is_select_with_value + ? memoize_expression(context.state, value) + : get_expression_id(state, value) + : value ); const inner_assignment = b.assignment( @@ -674,11 +697,6 @@ function build_element_special_value_attribute(element, node_id, attribute, cont ) ); - const is_select_with_value = - // attribute.metadata.dynamic would give false negatives because even if the value does not change, - // the inner options could still change, so we need to always treat it as reactive - element === 'select' && attribute.value !== true && !is_text_attribute(attribute); - const update = b.stmt( is_select_with_value ? b.sequence([ diff --git a/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/_config.js b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/_config.js new file mode 100644 index 0000000000..0597c2fda8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + async test({ assert, errors }) { + assert.deepEqual(errors, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/main.svelte b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/main.svelte new file mode 100644 index 0000000000..cb3804af34 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/autofocus-with-call/main.svelte @@ -0,0 +1,6 @@ +<svelte:options runes /> +<script> + function test(){} +</script> + +<input autofocus={test()} /> \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/_config.js b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/_config.js new file mode 100644 index 0000000000..cc4dfb37f0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/_config.js @@ -0,0 +1,13 @@ +import { flushSync } from 'svelte'; +import { ok, test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + const btn = target.querySelector('button'); + ok(btn); + flushSync(() => { + btn.click(); + }); + assert.deepEqual(logs, [true]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/main.svelte b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/main.svelte new file mode 100644 index 0000000000..646334c1ec --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/muted-without-bind-works/main.svelte @@ -0,0 +1,13 @@ +<script> + let muted = $state(false); + + function volume_change(node){ + node.addEventListener("volumechange", () => { + console.log(node.muted); + }); + } +</script> + +<audio use:volume_change {muted}></audio> +<button onclick={() => (muted = !muted)}></button> + diff --git a/packages/svelte/tests/runtime-runes/samples/select-value-with-call/_config.js b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/_config.js new file mode 100644 index 0000000000..0597c2fda8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + async test({ assert, errors }) { + assert.deepEqual(errors, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/select-value-with-call/main.svelte b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/main.svelte new file mode 100644 index 0000000000..b1d60ecf6d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/select-value-with-call/main.svelte @@ -0,0 +1,6 @@ +<svelte:options runes /> +<script> + function test(){} +</script> + +<select value={test()}></select> \ No newline at end of file From 98734433376e99909fb6f2f141d9e66d0531ff4e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 18 Feb 2025 17:19:18 +0100 Subject: [PATCH 0190/1429] Version Packages (#15304) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/kind-kiwis-train.md | 5 ----- .changeset/little-laws-sit.md | 5 ----- .changeset/lovely-chairs-compete.md | 5 ----- .changeset/silver-ties-itch.md | 5 ----- .changeset/unlucky-laws-decide.md | 5 ----- .changeset/wicked-apples-yawn.md | 5 ----- packages/svelte/CHANGELOG.md | 16 ++++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 9 files changed, 18 insertions(+), 32 deletions(-) delete mode 100644 .changeset/kind-kiwis-train.md delete mode 100644 .changeset/little-laws-sit.md delete mode 100644 .changeset/lovely-chairs-compete.md delete mode 100644 .changeset/silver-ties-itch.md delete mode 100644 .changeset/unlucky-laws-decide.md delete mode 100644 .changeset/wicked-apples-yawn.md diff --git a/.changeset/kind-kiwis-train.md b/.changeset/kind-kiwis-train.md deleted file mode 100644 index c40cf761b2..0000000000 --- a/.changeset/kind-kiwis-train.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: remove unused `options.uid` in `render` diff --git a/.changeset/little-laws-sit.md b/.changeset/little-laws-sit.md deleted file mode 100644 index a395ea6213..0000000000 --- a/.changeset/little-laws-sit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: do not warn for `binding_property_non_reactive` if binding is a store in an each diff --git a/.changeset/lovely-chairs-compete.md b/.changeset/lovely-chairs-compete.md deleted file mode 100644 index d311a3dde6..0000000000 --- a/.changeset/lovely-chairs-compete.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: prevent writable store value from becoming a proxy when reassigning using $-prefix diff --git a/.changeset/silver-ties-itch.md b/.changeset/silver-ties-itch.md deleted file mode 100644 index 67e33dadfb..0000000000 --- a/.changeset/silver-ties-itch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: `muted` reactive without `bind` and select/autofocus attributes working with function calls diff --git a/.changeset/unlucky-laws-decide.md b/.changeset/unlucky-laws-decide.md deleted file mode 100644 index c5fd7c65d9..0000000000 --- a/.changeset/unlucky-laws-decide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure input elements and elements with `dir` attribute are marked as non-static diff --git a/.changeset/wicked-apples-yawn.md b/.changeset/wicked-apples-yawn.md deleted file mode 100644 index 3022c34fd0..0000000000 --- a/.changeset/wicked-apples-yawn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: fire delegated events on target even it was disabled in the meantime diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 948a914778..ab190e1cc2 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,21 @@ # svelte +## 5.20.2 + +### Patch Changes + +- chore: remove unused `options.uid` in `render` ([#15302](https://github.com/sveltejs/svelte/pull/15302)) + +- fix: do not warn for `binding_property_non_reactive` if binding is a store in an each ([#15318](https://github.com/sveltejs/svelte/pull/15318)) + +- fix: prevent writable store value from becoming a proxy when reassigning using $-prefix ([#15283](https://github.com/sveltejs/svelte/pull/15283)) + +- fix: `muted` reactive without `bind` and select/autofocus attributes working with function calls ([#15326](https://github.com/sveltejs/svelte/pull/15326)) + +- fix: ensure input elements and elements with `dir` attribute are marked as non-static ([#15259](https://github.com/sveltejs/svelte/pull/15259)) + +- fix: fire delegated events on target even it was disabled in the meantime ([#15319](https://github.com/sveltejs/svelte/pull/15319)) + ## 5.20.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 3261243832..579660f9d7 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.20.1", + "version": "5.20.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 9f1873efc7..0803fae736 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.20.1'; +export const VERSION = '5.20.2'; export const PUBLIC_VERSION = '5'; From c582b5d3b559283619384dd78b77526ce1d6f722 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Thu, 20 Feb 2025 16:47:52 -0500 Subject: [PATCH 0191/1429] chore: simplify flush_sync (#15346) --- packages/svelte/src/internal/client/runtime.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 8a9ca9065b..8c98f948f3 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -858,11 +858,8 @@ export function flush_sync(fn) { try { infinite_loop_guard(); - /** @type {Effect[]} */ - const root_effects = []; - scheduler_mode = FLUSH_SYNC; - queued_root_effects = root_effects; + queued_root_effects = []; is_micro_task_queued = false; flush_queued_root_effects(previous_queued_root_effects); @@ -870,7 +867,7 @@ export function flush_sync(fn) { var result = fn?.(); flush_tasks(); - if (queued_root_effects.length > 0 || root_effects.length > 0) { + if (queued_root_effects.length > 0) { flush_sync(); } From ac215d9b23d091b14caa1f47951997dc6acfb841 Mon Sep 17 00:00:00 2001 From: realguse <me@realguse.com> Date: Fri, 21 Feb 2025 18:43:32 +0100 Subject: [PATCH 0192/1429] docs: rephrase copyright holder (#15333) --- LICENSE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.md b/LICENSE.md index abbace7bfe..f872adf738 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2016-2025 [these people](https://github.com/sveltejs/svelte/graphs/contributors) +Copyright (c) 2016-2025 [Svelte Contributors](https://github.com/sveltejs/svelte/graphs/contributors) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: From 60b22c0b5fc17bf0062d08ad16dc2ba60af12971 Mon Sep 17 00:00:00 2001 From: realguse <me@realguse.com> Date: Fri, 21 Feb 2025 18:44:23 +0100 Subject: [PATCH 0193/1429] docs: update casing (#15321) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d2f718da7..7ea7164752 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ </picture> </a> -[![license](https://img.shields.io/npm/l/svelte.svg)](LICENSE.md) [![Chat](https://img.shields.io/discord/457912077277855764?label=chat&logo=discord)](https://svelte.dev/chat) +[![License](https://img.shields.io/npm/l/svelte.svg)](LICENSE.md) [![Chat](https://img.shields.io/discord/457912077277855764?label=chat&logo=discord)](https://svelte.dev/chat) ## What is Svelte? From 1e1aea4063eda9363db38f00ce7feb8bd3954323 Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Fri, 21 Feb 2025 19:24:19 +0100 Subject: [PATCH 0194/1429] chore: remove unnecessary `?? ''` on some expressions (#15287) * no `?? ''` on some expressions * changeset * delete operator returns boolean * inverted conditions are a lil confusing * no need for else after return * simplify condition * may as well add special handling for undefined while we're here * use normal string literal when there are no values * omit assignment when there is no text content --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/serious-glasses-kiss.md | 5 ++ .../client/visitors/RegularElement.js | 17 +++--- .../client/visitors/shared/utils.js | 57 +++++++++++-------- 3 files changed, 46 insertions(+), 33 deletions(-) create mode 100644 .changeset/serious-glasses-kiss.md diff --git a/.changeset/serious-glasses-kiss.md b/.changeset/serious-glasses-kiss.md new file mode 100644 index 0000000000..29a6fafeae --- /dev/null +++ b/.changeset/serious-glasses-kiss.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: remove unnecessary `?? ''` on some expressions diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 018bdacc5e..d4502210bd 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -364,15 +364,14 @@ export function RegularElement(node, context) { trimmed.some((node) => node.type === 'ExpressionTag'); if (use_text_content) { - child_state.init.push( - b.stmt( - b.assignment( - '=', - b.member(context.state.node, 'textContent'), - build_template_chunk(trimmed, context.visit, child_state).value - ) - ) - ); + const { value } = build_template_chunk(trimmed, context.visit, child_state); + const empty_string = value.type === 'Literal' && value.value === ''; + + if (!empty_string) { + child_state.init.push( + b.stmt(b.assignment('=', b.member(context.state.node, 'textContent'), value)) + ); + } } else { /** @type {Expression} */ let arg = context.state.node; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index ca5094f455..c25ef3ab50 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -101,11 +101,15 @@ export function build_template_chunk( if (node.type === 'Text') { quasi.value.cooked += node.data; - } else if (node.type === 'ExpressionTag' && node.expression.type === 'Literal') { + } else if (node.expression.type === 'Literal') { if (node.expression.value != null) { quasi.value.cooked += node.expression.value + ''; } - } else { + } else if ( + node.expression.type !== 'Identifier' || + node.expression.name !== 'undefined' || + state.scope.get('undefined') + ) { let value = memoize( /** @type {Expression} */ (visit(node.expression, state)), node.metadata.expression @@ -117,31 +121,33 @@ export function build_template_chunk( // If we have a single expression, then pass that in directly to possibly avoid doing // extra work in the template_effect (instead we do the work in set_text). return { value, has_state }; - } else { - // add `?? ''` where necessary (TODO optimise more cases) - if ( - value.type === 'LogicalExpression' && - value.right.type === 'Literal' && - (value.operator === '??' || value.operator === '||') - ) { - // `foo ?? null` -=> `foo ?? ''` - // otherwise leave the expression untouched - if (value.right.value === null) { - value = { ...value, right: b.literal('') }; - } - } else if ( - state.analysis.props_id && - value.type === 'Identifier' && - value.name === state.analysis.props_id.name - ) { - // do nothing ($props.id() is never null/undefined) - } else { - value = b.logical('??', value, b.literal('')); + } + + if ( + value.type === 'LogicalExpression' && + value.right.type === 'Literal' && + (value.operator === '??' || value.operator === '||') + ) { + // `foo ?? null` -=> `foo ?? ''` + // otherwise leave the expression untouched + if (value.right.value === null) { + value = { ...value, right: b.literal('') }; } + } + + const is_defined = + value.type === 'BinaryExpression' || + (value.type === 'UnaryExpression' && value.operator !== 'void') || + (value.type === 'LogicalExpression' && value.right.type === 'Literal') || + (value.type === 'Identifier' && value.name === state.analysis.props_id?.name); - expressions.push(value); + if (!is_defined) { + // add `?? ''` where necessary (TODO optimise more cases) + value = b.logical('??', value, b.literal('')); } + expressions.push(value); + quasi = b.quasi('', i + 1 === values.length); quasis.push(quasi); } @@ -151,7 +157,10 @@ export function build_template_chunk( quasi.value.raw = sanitize_template_string(/** @type {string} */ (quasi.value.cooked)); } - const value = b.template(quasis, expressions); + const value = + expressions.length > 0 + ? b.template(quasis, expressions) + : b.literal(/** @type {string} */ (quasi.value.cooked)); return { value, has_state }; } From 3c4a8d425b8192dc11ea2af256d531c51c37ba5d Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 21 Feb 2025 14:40:46 -0500 Subject: [PATCH 0195/1429] chore: make Binding a class (#15359) * chore: make Binding a class * reorder * regenerate --- packages/svelte/src/compiler/phases/scope.js | 82 ++++++++++++++---- packages/svelte/src/compiler/types/index.d.ts | 85 ++++--------------- packages/svelte/types/index.d.ts | 2 +- 3 files changed, 83 insertions(+), 86 deletions(-) diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 51e9eb088d..41e1c78c05 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -1,6 +1,6 @@ /** @import { ClassDeclaration, Expression, FunctionDeclaration, Identifier, ImportDeclaration, MemberExpression, Node, Pattern, VariableDeclarator } from 'estree' */ /** @import { Context, Visitor } from 'zimmerframe' */ -/** @import { AST, Binding, DeclarationKind } from '#compiler' */ +/** @import { AST, BindingKind, DeclarationKind } from '#compiler' */ import is_reference from 'is-reference'; import { walk } from 'zimmerframe'; import { create_expression_metadata } from './nodes.js'; @@ -16,6 +16,69 @@ import { is_reserved, is_rune } from '../../utils.js'; import { determine_slot } from '../utils/slot.js'; import { validate_identifier_name } from './2-analyze/visitors/shared/utils.js'; +export class Binding { + /** @type {Scope} */ + scope; + + /** @type {Identifier} */ + node; + + /** @type {BindingKind} */ + kind; + + /** @type {DeclarationKind} */ + declaration_kind; + + /** + * What the value was initialized with. + * For destructured props such as `let { foo = 'bar' } = $props()` this is `'bar'` and not `$props()` + * @type {null | Expression | FunctionDeclaration | ClassDeclaration | ImportDeclaration | AST.EachBlock | AST.SnippetBlock} + */ + initial; + + /** @type {Array<{ node: Identifier; path: AST.SvelteNode[] }>} */ + references = []; + + /** + * For `legacy_reactive`: its reactive dependencies + * @type {Binding[]} + */ + legacy_dependencies = []; + + /** + * Legacy props: the `class` in `{ export klass as class}`. $props(): The `class` in { class: klass } = $props() + * @type {string | null} + */ + prop_alias = null; + + /** + * Additional metadata, varies per binding type + * @type {null | { inside_rest?: boolean }} + */ + metadata = null; + + is_called = false; + mutated = false; + reassigned = false; + updated = false; + + /** + * + * @param {Scope} scope + * @param {Identifier} node + * @param {BindingKind} kind + * @param {DeclarationKind} declaration_kind + * @param {Binding['initial']} initial + */ + constructor(scope, node, kind, declaration_kind, initial) { + this.scope = scope; + this.node = node; + this.initial = initial; + this.kind = kind; + this.declaration_kind = declaration_kind; + } +} + export class Scope { /** @type {ScopeRoot} */ root; @@ -100,22 +163,7 @@ export class Scope { e.declaration_duplicate(node, node.name); } - /** @type {Binding} */ - const binding = { - node, - references: [], - legacy_dependencies: [], - initial, - reassigned: false, - mutated: false, - updated: false, - scope: this, - kind, - declaration_kind, - is_called: false, - prop_alias: null, - metadata: null - }; + const binding = new Binding(this, node, kind, declaration_kind, initial); validate_identifier_name(binding, this.function_depth); diff --git a/packages/svelte/src/compiler/types/index.d.ts b/packages/svelte/src/compiler/types/index.d.ts index b80b717e42..eec41bad9d 100644 --- a/packages/svelte/src/compiler/types/index.d.ts +++ b/packages/svelte/src/compiler/types/index.d.ts @@ -1,12 +1,5 @@ -import type { - ClassDeclaration, - Expression, - FunctionDeclaration, - Identifier, - ImportDeclaration -} from 'estree'; import type { SourceMap } from 'magic-string'; -import type { Scope } from '../phases/scope.js'; +import type { Binding } from '../phases/scope.js'; import type { AST, Namespace } from './template.js'; import type { ICompileDiagnostic } from '../utils/compile_diagnostic.js'; @@ -241,6 +234,20 @@ export type ValidatedCompileOptions = ValidatedModuleCompileOptions & hmr: CompileOptions['hmr']; }; +export type BindingKind = + | 'normal' // A variable that is not in any way special + | 'prop' // A normal prop (possibly reassigned or mutated) + | 'bindable_prop' // A prop one can `bind:` to (possibly reassigned or mutated) + | 'rest_prop' // A rest prop + | 'raw_state' // A state variable + | 'state' // A deeply reactive state variable + | 'derived' // A derived variable + | 'each' // An each block parameter + | 'snippet' // A snippet parameter + | 'store_sub' // A $store value + | 'legacy_reactive' // A `$:` declaration + | 'template'; // A binding declared in the template, e.g. in an `await` block or `const` tag + export type DeclarationKind = | 'var' | 'let' @@ -251,66 +258,6 @@ export type DeclarationKind = | 'rest_param' | 'synthetic'; -export interface Binding { - node: Identifier; - /** - * - `normal`: A variable that is not in any way special - * - `prop`: A normal prop (possibly reassigned or mutated) - * - `bindable_prop`: A prop one can `bind:` to (possibly reassigned or mutated) - * - `rest_prop`: A rest prop - * - `state`: A state variable - * - `derived`: A derived variable - * - `each`: An each block parameter - * - `snippet`: A snippet parameter - * - `store_sub`: A $store value - * - `legacy_reactive`: A `$:` declaration - * - `template`: A binding declared in the template, e.g. in an `await` block or `const` tag - */ - kind: - | 'normal' - | 'prop' - | 'bindable_prop' - | 'rest_prop' - | 'state' - | 'raw_state' - | 'derived' - | 'each' - | 'snippet' - | 'store_sub' - | 'legacy_reactive' - | 'template' - | 'snippet'; - declaration_kind: DeclarationKind; - /** - * What the value was initialized with. - * For destructured props such as `let { foo = 'bar' } = $props()` this is `'bar'` and not `$props()` - */ - initial: - | null - | Expression - | FunctionDeclaration - | ClassDeclaration - | ImportDeclaration - | AST.EachBlock - | AST.SnippetBlock; - is_called: boolean; - references: { node: Identifier; path: AST.SvelteNode[] }[]; - mutated: boolean; - reassigned: boolean; - /** `true` if mutated _or_ reassigned */ - updated: boolean; - scope: Scope; - /** For `legacy_reactive`: its reactive dependencies */ - legacy_dependencies: Binding[]; - /** Legacy props: the `class` in `{ export klass as class}`. $props(): The `class` in { class: klass } = $props() */ - prop_alias: string | null; - /** Additional metadata, varies per binding type */ - metadata: { - /** `true` if is (inside) a rest parameter */ - inside_rest?: boolean; - } | null; -} - export interface ExpressionMetadata { /** All the bindings that are referenced inside this expression */ dependencies: Set<Binding>; @@ -322,5 +269,7 @@ export interface ExpressionMetadata { export * from './template.js'; +export { Binding, Scope } from '../phases/scope.js'; + // TODO this chain is a bit weird export { ReactiveStatement } from '../phases/types.js'; diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 77d78477ee..b9ab8a522c 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -622,8 +622,8 @@ declare module 'svelte/animate' { } declare module 'svelte/compiler' { - import type { Expression, Identifier, ArrayExpression, ArrowFunctionExpression, VariableDeclaration, VariableDeclarator, MemberExpression, Node, ObjectExpression, Pattern, Program, ChainExpression, SimpleCallExpression, SequenceExpression } from 'estree'; import type { SourceMap } from 'magic-string'; + import type { ArrayExpression, ArrowFunctionExpression, VariableDeclaration, VariableDeclarator, Expression, Identifier, MemberExpression, Node, ObjectExpression, Pattern, Program, ChainExpression, SimpleCallExpression, SequenceExpression } from 'estree'; import type { Location } from 'locate-character'; /** * `compile` converts your `.svelte` source code into a JavaScript module that exports a component From 5a946e7905314f13ed51fb3d37872160c81ab996 Mon Sep 17 00:00:00 2001 From: Alexis <michel.alexis@outlook.com> Date: Mon, 24 Feb 2025 13:29:35 +0100 Subject: [PATCH 0196/1429] fix: Allow @const inside #key (#15377) --- .changeset/healthy-guests-itch.md | 5 +++++ .../src/compiler/phases/2-analyze/visitors/ConstTag.js | 1 + .../validator/samples/const-tag-inside-key-block/errors.json | 1 + .../samples/const-tag-inside-key-block/input.svelte | 3 +++ 4 files changed, 10 insertions(+) create mode 100644 .changeset/healthy-guests-itch.md create mode 100644 packages/svelte/tests/validator/samples/const-tag-inside-key-block/errors.json create mode 100644 packages/svelte/tests/validator/samples/const-tag-inside-key-block/input.svelte diff --git a/.changeset/healthy-guests-itch.md b/.changeset/healthy-guests-itch.md new file mode 100644 index 0000000000..24b33f761d --- /dev/null +++ b/.changeset/healthy-guests-itch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +Allow `@const` inside `#key` diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ConstTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ConstTag.js index 3f5e0473c5..f723f8447c 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ConstTag.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ConstTag.js @@ -25,6 +25,7 @@ export function ConstTag(node, context) { grand_parent?.type !== 'AwaitBlock' && grand_parent?.type !== 'SnippetBlock' && grand_parent?.type !== 'SvelteBoundary' && + grand_parent?.type !== 'KeyBlock' && ((grand_parent?.type !== 'RegularElement' && grand_parent?.type !== 'SvelteElement') || !grand_parent.attributes.some((a) => a.type === 'Attribute' && a.name === 'slot'))) ) { diff --git a/packages/svelte/tests/validator/samples/const-tag-inside-key-block/errors.json b/packages/svelte/tests/validator/samples/const-tag-inside-key-block/errors.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/packages/svelte/tests/validator/samples/const-tag-inside-key-block/errors.json @@ -0,0 +1 @@ +[] diff --git a/packages/svelte/tests/validator/samples/const-tag-inside-key-block/input.svelte b/packages/svelte/tests/validator/samples/const-tag-inside-key-block/input.svelte new file mode 100644 index 0000000000..008072bc47 --- /dev/null +++ b/packages/svelte/tests/validator/samples/const-tag-inside-key-block/input.svelte @@ -0,0 +1,3 @@ +{#key 'key'} + {@const foo = 'bar'} +{/key} From d4360af7517b6c7e980cafbd2c36dfcd55085da9 Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Mon, 24 Feb 2025 13:46:23 +0100 Subject: [PATCH 0197/1429] chore: rewrite set_class() to handle directives (#15352) * set_class with class: directives * update expected result (remove leading space) * fix * optimize literals * add test * add test for mutations on hydration * clean observer * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js unused Co-authored-by: Rich Harris <hello@rich-harris.dev> * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js unused for now Co-authored-by: Rich Harris <hello@rich-harris.dev> * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js unused for now Co-authored-by: Rich Harris <hello@rich-harris.dev> * Update packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js nit Co-authored-by: Rich Harris <hello@rich-harris.dev> * Update packages/svelte/src/internal/client/dom/elements/attributes.js nit Co-authored-by: Rich Harris <hello@rich-harris.dev> * Update packages/svelte/src/internal/shared/attributes.js rename clazz to value :D Co-authored-by: Rich Harris <hello@rich-harris.dev> * remove unused + fix JSDoc * drive-by fix * minor style tweaks * tweak test * this is faster * tweak * tweak * this is faster * typo * tweak * changeset --------- Co-authored-by: Rich Harris <hello@rich-harris.dev> Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/stale-plums-drop.md | 5 + .../src/compiler/phases/2-analyze/index.js | 84 ++++------ .../client/visitors/RegularElement.js | 95 ++++++++---- .../client/visitors/SvelteElement.js | 41 +++-- .../client/visitors/shared/element.js | 144 ++++++++++++----- .../server/visitors/shared/element.js | 123 +++++++++------ .../client/dom/elements/attributes.js | 19 ++- .../src/internal/client/dom/elements/class.js | 139 ++++------------- .../src/internal/client/dom/operations.js | 2 +- packages/svelte/src/internal/client/index.js | 6 +- packages/svelte/src/internal/server/index.js | 22 +-- .../svelte/src/internal/shared/attributes.js | 42 +++++ .../undefined-with-scope/expected.html | 2 +- .../_config.js | 10 +- .../_config.js | 8 +- .../class-directive-mutations/_config.js | 43 ++++++ .../class-directive-mutations/main.svelte | 47 ++++++ .../samples/class-directive/_config.js | 145 ++++++++++++++++++ .../samples/class-directive/main.svelte | 40 +++++ 19 files changed, 688 insertions(+), 329 deletions(-) create mode 100644 .changeset/stale-plums-drop.md create mode 100644 packages/svelte/tests/runtime-runes/samples/class-directive-mutations/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/class-directive-mutations/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/class-directive/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/class-directive/main.svelte diff --git a/.changeset/stale-plums-drop.md b/.changeset/stale-plums-drop.md new file mode 100644 index 0000000000..d39268eb72 --- /dev/null +++ b/.changeset/stale-plums-drop.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly override class attributes with class directives diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 93c438c852..ca9297279e 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -767,66 +767,40 @@ export function analyze_component(root, source, options) { if (!should_ignore_unused) { warn_unused(analysis.css.ast); } + } - outer: for (const node of analysis.elements) { - if (node.metadata.scoped) { - // Dynamic elements in dom mode always use spread for attributes and therefore shouldn't have a class attribute added to them - // TODO this happens during the analysis phase, which shouldn't know anything about client vs server - if (node.type === 'SvelteElement' && options.generate === 'client') continue; - - /** @type {AST.Attribute | undefined} */ - let class_attribute = undefined; - - for (const attribute of node.attributes) { - if (attribute.type === 'SpreadAttribute') { - // The spread method appends the hash to the end of the class attribute on its own - continue outer; - } + for (const node of analysis.elements) { + if (node.metadata.scoped && is_custom_element_node(node)) { + mark_subtree_dynamic(node.metadata.path); + } - if (attribute.type !== 'Attribute') continue; - if (attribute.name.toLowerCase() !== 'class') continue; - // The dynamic class method appends the hash to the end of the class attribute on its own - if (attribute.metadata.needs_clsx) continue outer; + let has_class = false; + let has_spread = false; + let has_class_directive = false; - class_attribute = attribute; - } + for (const attribute of node.attributes) { + // The spread method appends the hash to the end of the class attribute on its own + if (attribute.type === 'SpreadAttribute') { + has_spread = true; + break; + } + has_class_directive ||= attribute.type === 'ClassDirective'; + has_class ||= attribute.type === 'Attribute' && attribute.name.toLowerCase() === 'class'; + } - if (class_attribute && class_attribute.value !== true) { - if (is_text_attribute(class_attribute)) { - class_attribute.value[0].data += ` ${analysis.css.hash}`; - } else { - /** @type {AST.Text} */ - const css_text = { - type: 'Text', - data: ` ${analysis.css.hash}`, - raw: ` ${analysis.css.hash}`, - start: -1, - end: -1 - }; - - if (Array.isArray(class_attribute.value)) { - class_attribute.value.push(css_text); - } else { - class_attribute.value = [class_attribute.value, css_text]; - } - } - } else { - node.attributes.push( - create_attribute('class', -1, -1, [ - { - type: 'Text', - data: analysis.css.hash, - raw: analysis.css.hash, - start: -1, - end: -1 - } - ]) - ); - if (is_custom_element_node(node) && node.attributes.length === 1) { - mark_subtree_dynamic(node.metadata.path); + // We need an empty class to generate the set_class() or class="" correctly + if (!has_spread && !has_class && (node.metadata.scoped || has_class_directive)) { + node.attributes.push( + create_attribute('class', -1, -1, [ + { + type: 'Text', + data: '', + raw: '', + start: -1, + end: -1 } - } - } + ]) + ); } } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index d4502210bd..434b49caa1 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -1,4 +1,4 @@ -/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, Statement } from 'estree' */ +/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { SourceLocation } from '#shared' */ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */ @@ -20,9 +20,9 @@ import { build_getter } from '../utils.js'; import { get_attribute_name, build_attribute_value, - build_class_directives, build_style_directives, - build_set_attributes + build_set_attributes, + build_set_class } from './shared/element.js'; import { process_children } from './shared/fragment.js'; import { @@ -223,13 +223,13 @@ export function RegularElement(node, context) { build_set_attributes( attributes, + class_directives, context, node, node_id, attributes_id, (node.metadata.svg || node.metadata.mathml || is_custom_element_node(node)) && b.true, - is_custom_element_node(node) && b.true, - context.state + is_custom_element_node(node) && b.true ); // If value binding exists, that one takes care of calling $.init_select @@ -270,13 +270,22 @@ export function RegularElement(node, context) { continue; } + const name = get_attribute_name(node, attribute); if ( !is_custom_element && !cannot_be_set_statically(attribute.name) && - (attribute.value === true || is_text_attribute(attribute)) + (attribute.value === true || is_text_attribute(attribute)) && + (name !== 'class' || class_directives.length === 0) ) { - const name = get_attribute_name(node, attribute); - const value = is_text_attribute(attribute) ? attribute.value[0].data : true; + let value = is_text_attribute(attribute) ? attribute.value[0].data : true; + + if (name === 'class' && node.metadata.scoped && context.state.analysis.css.hash) { + if (value === true || value === '') { + value = context.state.analysis.css.hash; + } else { + value += ' ' + context.state.analysis.css.hash; + } + } if (name !== 'class' || value) { context.state.template.push( @@ -290,15 +299,22 @@ export function RegularElement(node, context) { continue; } - const is = is_custom_element - ? build_custom_element_attribute_update_assignment(node_id, attribute, context) - : build_element_attribute_update_assignment(node, node_id, attribute, attributes, context); + const is = + is_custom_element && name !== 'class' + ? build_custom_element_attribute_update_assignment(node_id, attribute, context) + : build_element_attribute_update_assignment( + node, + node_id, + attribute, + attributes, + class_directives, + context + ); if (is) is_attributes_reactive = true; } } - // class/style directives must be applied last since they could override class/style attributes - build_class_directives(class_directives, node_id, context, is_attributes_reactive); + // style directives must be applied last since they could override class/style attributes build_style_directives(style_directives, node_id, context, is_attributes_reactive); if ( @@ -491,6 +507,27 @@ function setup_select_synchronization(value_binding, context) { ); } +/** + * @param {AST.ClassDirective[]} class_directives + * @param {ComponentContext} context + * @return {ObjectExpression} + */ +export function build_class_directives_object(class_directives, context) { + let properties = []; + + for (const d of class_directives) { + let expression = /** @type Expression */ (context.visit(d.expression)); + + if (d.metadata.expression.has_call) { + expression = get_expression_id(context.state, expression); + } + + properties.push(b.init(d.name, expression)); + } + + return b.object(properties); +} + /** * Serializes an assignment to an element property by adding relevant statements to either only * the init or the the init and update arrays, depending on whether or not the value is dynamic. @@ -517,6 +554,7 @@ function setup_select_synchronization(value_binding, context) { * @param {Identifier} node_id * @param {AST.Attribute} attribute * @param {Array<AST.Attribute | AST.SpreadAttribute>} attributes + * @param {AST.ClassDirective[]} class_directives * @param {ComponentContext} context * @returns {boolean} */ @@ -525,6 +563,7 @@ function build_element_attribute_update_assignment( node_id, attribute, attributes, + class_directives, context ) { const state = context.state; @@ -563,19 +602,15 @@ function build_element_attribute_update_assignment( let update; if (name === 'class') { - if (attribute.metadata.needs_clsx) { - value = b.call('$.clsx', value); - } - - update = b.stmt( - b.call( - is_svg ? '$.set_svg_class' : is_mathml ? '$.set_mathml_class' : '$.set_class', - node_id, - value, - attribute.metadata.needs_clsx && context.state.analysis.css.hash - ? b.literal(context.state.analysis.css.hash) - : undefined - ) + return build_set_class( + element, + node_id, + attribute, + value, + has_state, + class_directives, + context, + !is_svg && !is_mathml ); } else if (name === 'value') { update = b.stmt(b.call('$.set_value', node_id, value)); @@ -639,14 +674,6 @@ function build_custom_element_attribute_update_assignment(node_id, attribute, co const name = attribute.name; // don't lowercase, as we set the element's property, which might be case sensitive let { value, has_state } = build_attribute_value(attribute.value, context); - // We assume that noone's going to redefine the semantics of the class attribute on custom elements, i.e. it's still used for CSS classes - if (name === 'class' && attribute.metadata.needs_clsx) { - if (context.state.analysis.css.hash) { - value = b.array([value, b.literal(context.state.analysis.css.hash)]); - } - value = b.call('$.clsx', value); - } - const update = b.stmt(b.call('$.set_custom_element_data', node_id, b.literal(name), value)); if (has_state) { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js index e275283655..7149f8d0e4 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js @@ -7,11 +7,11 @@ import * as b from '../../../../utils/builders.js'; import { determine_namespace_for_children } from '../../utils.js'; import { build_attribute_value, - build_class_directives, build_set_attributes, + build_set_class, build_style_directives } from './shared/element.js'; -import { build_render_statement } from './shared/utils.js'; +import { build_render_statement, get_expression_id } from './shared/utils.js'; /** * @param {AST.SvelteElement} node @@ -80,31 +80,46 @@ export function SvelteElement(node, context) { // Then do attributes let is_attributes_reactive = false; - if (attributes.length === 0) { - if (context.state.analysis.css.hash) { - inner_context.state.init.push( - b.stmt(b.call('$.set_class', element_id, b.literal(context.state.analysis.css.hash))) - ); - } - } else { + if ( + attributes.length === 1 && + attributes[0].type === 'Attribute' && + attributes[0].name.toLowerCase() === 'class' + ) { + // special case when there only a class attribute + let { value, has_state } = build_attribute_value( + attributes[0].value, + context, + (value, metadata) => (metadata.has_call ? get_expression_id(context.state, value) : value) + ); + + is_attributes_reactive = build_set_class( + node, + element_id, + attributes[0], + value, + has_state, + class_directives, + inner_context, + false + ); + } else if (attributes.length) { const attributes_id = b.id(context.state.scope.generate('attributes')); // Always use spread because we don't know whether the element is a custom element or not, // therefore we need to do the "how to set an attribute" logic at runtime. is_attributes_reactive = build_set_attributes( attributes, + class_directives, inner_context, node, element_id, attributes_id, b.binary('===', b.member(element_id, 'namespaceURI'), b.id('$.NAMESPACE_SVG')), - b.call(b.member(b.member(element_id, 'nodeName'), 'includes'), b.literal('-')), - context.state + b.call(b.member(b.member(element_id, 'nodeName'), 'includes'), b.literal('-')) ); } - // class/style directives must be applied last since they could override class/style attributes - build_class_directives(class_directives, element_id, inner_context, is_attributes_reactive); + // style directives must be applied last since they could override class/style attributes build_style_directives(style_directives, element_id, inner_context, is_attributes_reactive); const get_tag = b.thunk(/** @type {Expression} */ (context.visit(node.tag))); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index abffad0ff7..fc5fd2cc4c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -1,32 +1,34 @@ /** @import { Expression, Identifier, ObjectExpression } from 'estree' */ /** @import { AST, ExpressionMetadata } from '#compiler' */ /** @import { ComponentClientTransformState, ComponentContext } from '../../types' */ +import { escape_html } from '../../../../../../escaping.js'; import { normalize_attribute } from '../../../../../../utils.js'; import { is_ignored } from '../../../../../state.js'; import { is_event_attribute } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; import { build_getter } from '../../utils.js'; +import { build_class_directives_object } from '../RegularElement.js'; import { build_template_chunk, get_expression_id } from './utils.js'; /** * @param {Array<AST.Attribute | AST.SpreadAttribute>} attributes + * @param {AST.ClassDirective[]} class_directives * @param {ComponentContext} context * @param {AST.RegularElement | AST.SvelteElement} element * @param {Identifier} element_id * @param {Identifier} attributes_id * @param {false | Expression} preserve_attribute_case * @param {false | Expression} is_custom_element - * @param {ComponentClientTransformState} state */ export function build_set_attributes( attributes, + class_directives, context, element, element_id, attributes_id, preserve_attribute_case, - is_custom_element, - state + is_custom_element ) { let is_dynamic = false; @@ -68,6 +70,19 @@ export function build_set_attributes( } } + if (class_directives.length) { + values.push( + b.prop( + 'init', + b.array([b.id('$.CLASS')]), + build_class_directives_object(class_directives, context) + ) + ); + + is_dynamic ||= + class_directives.find((directive) => directive.metadata.expression.has_state) !== null; + } + const call = b.call( '$.set_attributes', element_id, @@ -134,39 +149,6 @@ export function build_style_directives( } } -/** - * Serializes each class directive into something like `$.class_toogle(element, class_name, value)` - * and adds it either to init or update, depending on whether or not the value or the attributes are dynamic. - * @param {AST.ClassDirective[]} class_directives - * @param {Identifier} element_id - * @param {ComponentContext} context - * @param {boolean} is_attributes_reactive - */ -export function build_class_directives( - class_directives, - element_id, - context, - is_attributes_reactive -) { - const state = context.state; - for (const directive of class_directives) { - const { has_state, has_call } = directive.metadata.expression; - let value = /** @type {Expression} */ (context.visit(directive.expression)); - - if (has_call) { - value = get_expression_id(state, value); - } - - const update = b.stmt(b.call('$.toggle_class', element_id, b.literal(directive.name), value)); - - if (is_attributes_reactive || has_state) { - state.update.push(update); - } else { - state.init.push(update); - } - } -} - /** * @param {AST.Attribute['value']} value * @param {ComponentContext} context @@ -207,3 +189,93 @@ export function get_attribute_name(element, attribute) { return attribute.name; } + +/** + * @param {AST.RegularElement | AST.SvelteElement} element + * @param {Identifier} node_id + * @param {AST.Attribute | null} attribute + * @param {Expression} value + * @param {boolean} has_state + * @param {AST.ClassDirective[]} class_directives + * @param {ComponentContext} context + * @param {boolean} is_html + * @returns {boolean} + */ +export function build_set_class( + element, + node_id, + attribute, + value, + has_state, + class_directives, + context, + is_html +) { + if (attribute && attribute.metadata.needs_clsx) { + value = b.call('$.clsx', value); + } + + /** @type {Identifier | undefined} */ + let previous_id; + + /** @type {ObjectExpression | Identifier | undefined} */ + let prev; + + /** @type {ObjectExpression | undefined} */ + let next; + + if (class_directives.length) { + next = build_class_directives_object(class_directives, context); + has_state ||= class_directives.some((d) => d.metadata.expression.has_state); + + if (has_state) { + previous_id = b.id(context.state.scope.generate('classes')); + context.state.init.push(b.declaration('let', [b.declarator(previous_id)])); + prev = previous_id; + } else { + prev = b.object([]); + } + } + + /** @type {Expression | undefined} */ + let css_hash; + + if (element.metadata.scoped && context.state.analysis.css.hash) { + if (value.type === 'Literal' && (value.value === '' || value.value === null)) { + value = b.literal(context.state.analysis.css.hash); + } else if (value.type === 'Literal' && typeof value.value === 'string') { + value = b.literal(escape_html(value.value, true) + ' ' + context.state.analysis.css.hash); + } else { + css_hash = b.literal(context.state.analysis.css.hash); + } + } + + if (!css_hash && next) { + css_hash = b.null; + } + + /** @type {Expression} */ + let set_class = b.call( + '$.set_class', + node_id, + is_html ? b.literal(1) : b.literal(0), + value, + css_hash, + prev, + next + ); + + if (previous_id) { + set_class = b.assignment('=', previous_id, set_class); + } + + const update = b.stmt(set_class); + + if (has_state) { + context.state.update.push(update); + return true; + } + + context.state.init.push(update); + return false; +} diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js index d0d800d3cb..57101af4b8 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js @@ -1,4 +1,4 @@ -/** @import { Expression, Literal } from 'estree' */ +/** @import { Expression, Literal, ObjectExpression } from 'estree' */ /** @import { AST, Namespace } from '#compiler' */ /** @import { ComponentContext, ComponentServerTransformState } from '../../types.js' */ import { @@ -24,6 +24,7 @@ import { is_content_editable_binding, is_load_error_element } from '../../../../../../utils.js'; +import { escape_html } from '../../../../../../escaping.js'; const WHITESPACE_INSENSITIVE_ATTRIBUTES = ['class', 'style']; @@ -86,23 +87,15 @@ export function build_element_attributes(node, context) { } else if (attribute.name !== 'defaultValue' && attribute.name !== 'defaultChecked') { if (attribute.name === 'class') { class_index = attributes.length; - if (attribute.metadata.needs_clsx) { - const clsx_value = b.call( - '$.clsx', - /** @type {AST.ExpressionTag} */ (attribute.value).expression - ); attributes.push({ ...attribute, value: { .../** @type {AST.ExpressionTag} */ (attribute.value), - expression: context.state.analysis.css.hash - ? b.binary( - '+', - b.binary('+', clsx_value, b.literal(' ')), - b.literal(context.state.analysis.css.hash) - ) - : clsx_value + expression: b.call( + '$.clsx', + /** @type {AST.ExpressionTag} */ (attribute.value).expression + ) } }); } else { @@ -219,8 +212,9 @@ export function build_element_attributes(node, context) { } } - if (class_directives.length > 0 && !has_spread) { - const class_attribute = build_class_directives( + if ((node.metadata.scoped || class_directives.length) && !has_spread) { + const class_attribute = build_to_class( + node.metadata.scoped ? context.state.analysis.css.hash : null, class_directives, /** @type {AST.Attribute | null} */ (attributes[class_index] ?? null) ); @@ -274,9 +268,14 @@ export function build_element_attributes(node, context) { WHITESPACE_INSENSITIVE_ATTRIBUTES.includes(name) ); - context.state.template.push( - b.call('$.attr', b.literal(name), value, is_boolean_attribute(name) && b.true) - ); + // pre-escape and inline literal attributes : + if (value.type === 'Literal' && typeof value.value === 'string') { + context.state.template.push(b.literal(` ${name}="${escape_html(value.value, true)}"`)); + } else { + context.state.template.push( + b.call('$.attr', b.literal(name), value, is_boolean_attribute(name) && b.true) + ); + } } } @@ -322,7 +321,7 @@ function build_element_spread_attributes( let styles; let flags = 0; - if (class_directives.length > 0 || context.state.analysis.css.hash) { + if (class_directives.length) { const properties = class_directives.map((directive) => b.init( directive.name, @@ -331,11 +330,6 @@ function build_element_spread_attributes( : /** @type {Expression} */ (context.visit(directive.expression)) ) ); - - if (context.state.analysis.css.hash) { - properties.unshift(b.init(context.state.analysis.css.hash, b.literal(true))); - } - classes = b.object(properties); } @@ -374,55 +368,82 @@ function build_element_spread_attributes( }) ); - const args = [object, classes, styles, flags ? b.literal(flags) : undefined]; + const css_hash = context.state.analysis.css.hash + ? b.literal(context.state.analysis.css.hash) + : b.null; + + const args = [object, css_hash, classes, styles, flags ? b.literal(flags) : undefined]; context.state.template.push(b.call('$.spread_attributes', ...args)); } /** * + * @param {string | null} hash * @param {AST.ClassDirective[]} class_directives * @param {AST.Attribute | null} class_attribute * @returns */ -function build_class_directives(class_directives, class_attribute) { - const expressions = class_directives.map((directive) => - b.conditional(directive.expression, b.literal(directive.name), b.literal('')) - ); - +function build_to_class(hash, class_directives, class_attribute) { if (class_attribute === null) { class_attribute = create_attribute('class', -1, -1, []); } - const chunks = get_attribute_chunks(class_attribute.value); - const last = chunks.at(-1); - - if (last?.type === 'Text') { - last.data += ' '; - last.raw += ' '; - } else if (last) { - chunks.push({ - type: 'Text', - start: -1, - end: -1, - data: ' ', - raw: ' ' - }); + /** @type {ObjectExpression | undefined} */ + let classes; + + if (class_directives.length) { + classes = b.object( + class_directives.map((directive) => + b.prop('init', b.literal(directive.name), directive.expression) + ) + ); + } + + /** @type {Expression} */ + let class_name; + + if (class_attribute.value === true) { + class_name = b.literal(''); + } else if (Array.isArray(class_attribute.value)) { + if (class_attribute.value.length === 0) { + class_name = b.null; + } else { + class_name = class_attribute.value + .map((val) => (val.type === 'Text' ? b.literal(val.data) : val.expression)) + .reduce((left, right) => b.binary('+', left, right)); + } + } else { + class_name = class_attribute.value.expression; } - chunks.push({ + /** @type {Expression} */ + let expression; + + if ( + hash && + !classes && + class_name.type === 'Literal' && + (class_name.value === null || class_name.value === '' || typeof class_name.value === 'string') + ) { + if (class_name.value === null || class_name.value === '') { + expression = b.literal(hash); + } else { + expression = b.literal(escape_html(class_name.value, true) + ' ' + hash); + } + } else { + expression = b.call('$.to_class', class_name, b.literal(hash), classes); + } + + class_attribute.value = { type: 'ExpressionTag', start: -1, end: -1, - expression: b.call( - b.member(b.call(b.member(b.array(expressions), 'filter'), b.id('Boolean')), b.id('join')), - b.literal(' ') - ), + expression: expression, metadata: { expression: create_expression_metadata() } - }); + }; - class_attribute.value = chunks; return class_attribute; } diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 2dba2d797a..151024e85c 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -14,6 +14,10 @@ import { set_active_reaction } from '../../runtime.js'; import { clsx } from '../../../shared/attributes.js'; +import { set_class } from './class.js'; + +export const CLASS = Symbol('class'); +export const STYLE = Symbol('style'); /** * The value/checked attribute in the template actually corresponds to the defaultValue property, so we need @@ -254,8 +258,8 @@ export function set_custom_element_data(node, prop, value) { /** * Spreads attributes onto a DOM element, taking into account the currently set attributes * @param {Element & ElementCSSInlineStyle} element - * @param {Record<string, any> | undefined} prev - * @param {Record<string, any>} next New attributes - this function mutates this object + * @param {Record<string | symbol, any> | undefined} prev + * @param {Record<string | symbol, any>} next New attributes - this function mutates this object * @param {string} [css_hash] * @param {boolean} [preserve_attribute_case] * @param {boolean} [is_custom_element] @@ -289,10 +293,8 @@ export function set_attributes( if (next.class) { next.class = clsx(next.class); - } - - if (css_hash !== undefined) { - next.class = next.class ? next.class + ' ' + css_hash : css_hash; + } else if (css_hash || next[CLASS]) { + next.class = null; /* force call to set_class() */ } var setters = get_setters(element); @@ -325,7 +327,7 @@ export function set_attributes( } var prev_value = current[key]; - if (value === prev_value) continue; + if (value === prev_value && key !== 'class') continue; current[key] = value; @@ -375,6 +377,9 @@ export function set_attributes( // @ts-ignore element[`__${event_name}`] = undefined; } + } else if (key === 'class') { + var is_html = element.namespaceURI === 'http://www.w3.org/1999/xhtml'; + set_class(element, is_html, value, css_hash, prev?.[CLASS], next[CLASS]); } else if (key === 'style' && value != null) { element.style.cssText = value + ''; } else if (key === 'autofocus') { diff --git a/packages/svelte/src/internal/client/dom/elements/class.js b/packages/svelte/src/internal/client/dom/elements/class.js index 62ffb6d14b..3308709a24 100644 --- a/packages/svelte/src/internal/client/dom/elements/class.js +++ b/packages/svelte/src/internal/client/dom/elements/class.js @@ -1,120 +1,49 @@ +import { to_class } from '../../../shared/attributes.js'; import { hydrating } from '../hydration.js'; /** - * @param {SVGElement} dom - * @param {string} value - * @param {string} [hash] - * @returns {void} - */ -export function set_svg_class(dom, value, hash) { - // @ts-expect-error need to add __className to patched prototype - var prev_class_name = dom.__className; - var next_class_name = to_class(value, hash); - - if (hydrating && dom.getAttribute('class') === next_class_name) { - // In case of hydration don't reset the class as it's already correct. - // @ts-expect-error need to add __className to patched prototype - dom.__className = next_class_name; - } else if ( - prev_class_name !== next_class_name || - (hydrating && dom.getAttribute('class') !== next_class_name) - ) { - if (next_class_name === '') { - dom.removeAttribute('class'); - } else { - dom.setAttribute('class', next_class_name); - } - - // @ts-expect-error need to add __className to patched prototype - dom.__className = next_class_name; - } -} - -/** - * @param {MathMLElement} dom - * @param {string} value + * @param {Element} dom + * @param {boolean | number} is_html + * @param {string | null} value * @param {string} [hash] - * @returns {void} + * @param {Record<string, boolean>} [prev_classes] + * @param {Record<string, boolean>} [next_classes] + * @returns {Record<string, boolean> | undefined} */ -export function set_mathml_class(dom, value, hash) { +export function set_class(dom, is_html, value, hash, prev_classes, next_classes) { // @ts-expect-error need to add __className to patched prototype - var prev_class_name = dom.__className; - var next_class_name = to_class(value, hash); - - if (hydrating && dom.getAttribute('class') === next_class_name) { - // In case of hydration don't reset the class as it's already correct. - // @ts-expect-error need to add __className to patched prototype - dom.__className = next_class_name; - } else if ( - prev_class_name !== next_class_name || - (hydrating && dom.getAttribute('class') !== next_class_name) - ) { - if (next_class_name === '') { - dom.removeAttribute('class'); - } else { - dom.setAttribute('class', next_class_name); + var prev = dom.__className; + + if (hydrating || prev !== value) { + var next_class_name = to_class(value, hash, next_classes); + + if (!hydrating || next_class_name !== dom.getAttribute('class')) { + // Removing the attribute when the value is only an empty string causes + // performance issues vs simply making the className an empty string. So + // we should only remove the class if the the value is nullish + // and there no hash/directives : + if (next_class_name == null) { + dom.removeAttribute('class'); + } else if (is_html) { + dom.className = next_class_name; + } else { + dom.setAttribute('class', next_class_name); + } } // @ts-expect-error need to add __className to patched prototype - dom.__className = next_class_name; - } -} + dom.__className = value; + } else if (next_classes) { + prev_classes ??= {}; -/** - * @param {HTMLElement} dom - * @param {string} value - * @param {string} [hash] - * @returns {void} - */ -export function set_class(dom, value, hash) { - // @ts-expect-error need to add __className to patched prototype - var prev_class_name = dom.__className; - var next_class_name = to_class(value, hash); + for (var key in next_classes) { + var is_present = !!next_classes[key]; - if (hydrating && dom.className === next_class_name) { - // In case of hydration don't reset the class as it's already correct. - // @ts-expect-error need to add __className to patched prototype - dom.__className = next_class_name; - } else if ( - prev_class_name !== next_class_name || - (hydrating && dom.className !== next_class_name) - ) { - // Removing the attribute when the value is only an empty string causes - // peformance issues vs simply making the className an empty string. So - // we should only remove the class if the the value is nullish. - if (value == null && !hash) { - dom.removeAttribute('class'); - } else { - dom.className = next_class_name; + if (is_present !== !!prev_classes[key]) { + dom.classList.toggle(key, is_present); + } } - - // @ts-expect-error need to add __className to patched prototype - dom.__className = next_class_name; } -} -/** - * @template V - * @param {V} value - * @param {string} [hash] - * @returns {string | V} - */ -function to_class(value, hash) { - return (value == null ? '' : value) + (hash ? ' ' + hash : ''); -} - -/** - * @param {Element} dom - * @param {string} class_name - * @param {boolean} value - * @returns {void} - */ -export function toggle_class(dom, class_name, value) { - if (value) { - if (dom.classList.contains(class_name)) return; - dom.classList.add(class_name); - } else { - if (!dom.classList.contains(class_name)) return; - dom.classList.remove(class_name); - } + return next_classes; } diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index 83565d17ae..f6ac92456e 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -44,7 +44,7 @@ export function init_operations() { // @ts-expect-error element_prototype.__click = undefined; // @ts-expect-error - element_prototype.__className = ''; + element_prototype.__className = undefined; // @ts-expect-error element_prototype.__attributes = null; // @ts-expect-error diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index d78f6d452e..431ac8cf24 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -39,9 +39,11 @@ export { set_checked, set_selected, set_default_checked, - set_default_value + set_default_value, + CLASS, + STYLE } from './dom/elements/attributes.js'; -export { set_class, set_svg_class, set_mathml_class, toggle_class } from './dom/elements/class.js'; +export { set_class } from './dom/elements/class.js'; export { apply, event, delegate, replay_events } from './dom/elements/events.js'; export { autofocus, remove_textarea_child } from './dom/elements/misc.js'; export { set_style } from './dom/elements/style.js'; diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index e8ffeed2fe..160a1faa65 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -2,7 +2,7 @@ /** @import { Component, Payload, RenderOutput } from '#server' */ /** @import { Store } from '#shared' */ export { FILENAME, HMR } from '../../constants.js'; -import { attr, clsx } from '../shared/attributes.js'; +import { attr, clsx, to_class } from '../shared/attributes.js'; import { is_promise, noop } from '../shared/utils.js'; import { subscribe_to_store } from '../../store/utils.js'; import { @@ -10,7 +10,6 @@ import { ELEMENT_PRESERVE_ATTRIBUTE_CASE, ELEMENT_IS_NAMESPACED } from '../../constants.js'; - import { escape_html } from '../../escaping.js'; import { DEV } from 'esm-env'; import { current_component, pop, push } from './context.js'; @@ -198,12 +197,13 @@ export function css_props(payload, is_html, props, component, dynamic = false) { /** * @param {Record<string, unknown>} attrs - * @param {Record<string, string>} [classes] + * @param {string | null} css_hash + * @param {Record<string, boolean>} [classes] * @param {Record<string, string>} [styles] * @param {number} [flags] * @returns {string} */ -export function spread_attributes(attrs, classes, styles, flags = 0) { +export function spread_attributes(attrs, css_hash, classes, styles, flags = 0) { if (styles) { attrs.style = attrs.style ? style_object_to_string(merge_styles(/** @type {string} */ (attrs.style), styles)) @@ -214,16 +214,8 @@ export function spread_attributes(attrs, classes, styles, flags = 0) { attrs.class = clsx(attrs.class); } - if (classes) { - const classlist = attrs.class ? [attrs.class] : []; - - for (const key in classes) { - if (classes[key]) { - classlist.push(key); - } - } - - attrs.class = classlist.join(' '); + if (css_hash || classes) { + attrs.class = to_class(attrs.class, css_hash, classes); } let attr_str = ''; @@ -552,7 +544,7 @@ export function props_id(payload) { return uid; } -export { attr, clsx }; +export { attr, clsx, to_class }; export { html } from './blocks/html.js'; diff --git a/packages/svelte/src/internal/shared/attributes.js b/packages/svelte/src/internal/shared/attributes.js index a561501bf4..89cc17e51b 100644 --- a/packages/svelte/src/internal/shared/attributes.js +++ b/packages/svelte/src/internal/shared/attributes.js @@ -40,3 +40,45 @@ export function clsx(value) { return value ?? ''; } } + +const whitespace = [...' \t\n\r\f\u00a0\u000b\ufeff']; + +/** + * @param {any} value + * @param {string | null} [hash] + * @param {Record<string, boolean>} [directives] + * @returns {string | null} + */ +export function to_class(value, hash, directives) { + var classname = value == null ? '' : '' + value; + + if (hash) { + classname = classname ? classname + ' ' + hash : hash; + } + + if (directives) { + for (var key in directives) { + if (directives[key]) { + classname = classname ? classname + ' ' + key : key; + } else if (classname.length) { + var len = key.length; + var a = 0; + + while ((a = classname.indexOf(key, a)) >= 0) { + var b = a + len; + + if ( + (a === 0 || whitespace.includes(classname[a - 1])) && + (b === classname.length || whitespace.includes(classname[b])) + ) { + classname = (a === 0 ? '' : classname.substring(0, a)) + classname.substring(b + 1); + } else { + a = b; + } + } + } + } + } + + return classname === '' ? null : classname; +} diff --git a/packages/svelte/tests/css/samples/undefined-with-scope/expected.html b/packages/svelte/tests/css/samples/undefined-with-scope/expected.html index ddb9429bc8..5eecaa9bb2 100644 --- a/packages/svelte/tests/css/samples/undefined-with-scope/expected.html +++ b/packages/svelte/tests/css/samples/undefined-with-scope/expected.html @@ -1 +1 @@ -<p class=" svelte-xyz">Foo</p> \ No newline at end of file +<p class="svelte-xyz">Foo</p> \ No newline at end of file diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js index c8710f9038..cbd0456e13 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-classname-with-style/_config.js @@ -1,17 +1,17 @@ import { ok, test } from '../../test'; export default test({ - html: '<div class=" svelte-x1o6ra"></div>', + html: '<div class="svelte-x1o6ra"></div>', test({ assert, component, target }) { const div = target.querySelector('div'); ok(div); component.testName = null; - assert.equal(div.className, ' svelte-x1o6ra'); + assert.equal(div.className, 'svelte-x1o6ra'); component.testName = undefined; - assert.equal(div.className, ' svelte-x1o6ra'); + assert.equal(div.className, 'svelte-x1o6ra'); component.testName = undefined + ''; assert.equal(div.className, 'undefined svelte-x1o6ra'); @@ -32,10 +32,10 @@ export default test({ assert.equal(div.className, 'true svelte-x1o6ra'); component.testName = {}; - assert.equal(div.className, ' svelte-x1o6ra'); + assert.equal(div.className, 'svelte-x1o6ra'); component.testName = ''; - assert.equal(div.className, ' svelte-x1o6ra'); + assert.equal(div.className, 'svelte-x1o6ra'); component.testName = 'testClassName'; assert.equal(div.className, 'testClassName svelte-x1o6ra'); diff --git a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js index 8d0f411b8f..081fceecf2 100644 --- a/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/attribute-null-func-classname-with-style/_config.js @@ -16,10 +16,10 @@ export default test({ assert.equal(div.className, 'testClassName svelte-x1o6ra'); component.testName = null; - assert.equal(div.className, ' svelte-x1o6ra'); + assert.equal(div.className, 'svelte-x1o6ra'); component.testName = undefined; - assert.equal(div.className, ' svelte-x1o6ra'); + assert.equal(div.className, 'svelte-x1o6ra'); component.testName = undefined + ''; assert.equal(div.className, 'undefined svelte-x1o6ra'); @@ -40,9 +40,9 @@ export default test({ assert.equal(div.className, 'true svelte-x1o6ra'); component.testName = {}; - assert.equal(div.className, ' svelte-x1o6ra'); + assert.equal(div.className, 'svelte-x1o6ra'); component.testName = ''; - assert.equal(div.className, ' svelte-x1o6ra'); + assert.equal(div.className, 'svelte-x1o6ra'); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/_config.js b/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/_config.js new file mode 100644 index 0000000000..dd1bc6ac1a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/_config.js @@ -0,0 +1,43 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +// This test counts mutations on hydration +// set_class() should not mutate class on hydration, except if mismatch +export default test({ + mode: ['server', 'hydrate'], + + server_props: { + browser: false + }, + + props: { + browser: true + }, + + html: ` + <main id="main" class="browser"> + <div class="custom svelte-1cjqok6 foo bar"></div> + <span class="svelte-1cjqok6 foo bar"></span> + <b class="custom foo bar"></b> + <i class="foo bar"></i> + </main> + `, + + ssrHtml: ` + <main id="main"> + <div class="custom svelte-1cjqok6 foo bar"></div> + <span class="svelte-1cjqok6 foo bar"></span> + <b class="custom foo bar"></b> + <i class="foo bar"></i> + </main> + `, + + async test({ assert, component, instance }) { + flushSync(); + assert.deepEqual(instance.get_and_clear_mutations(), ['MAIN']); + + component.foo = false; + flushSync(); + assert.deepEqual(instance.get_and_clear_mutations(), ['DIV', 'SPAN', 'B', 'I']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/main.svelte new file mode 100644 index 0000000000..825362dcaf --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/main.svelte @@ -0,0 +1,47 @@ +<script> + let { + classname = 'custom', + foo = true, + bar = true, + browser + } = $props(); + + let mutations = []; + let observer; + + if (browser) { + observer = new MutationObserver(update_mutation_records); + observer.observe(document.querySelector('#main'), { attributes: true, subtree: true }); + + $effect(() => { + return () => observer.disconnect(); + }); + } + + function update_mutation_records(results) { + for (const r of results) { + mutations.push(r.target.nodeName); + } + } + + export function get_and_clear_mutations() { + update_mutation_records(observer.takeRecords()); + const result = mutations; + mutations = []; + return result; + } +</script> + +<main id="main" class:browser> + <div class={classname} class:foo class:bar></div> + <span class:foo class:bar></span> + <b class={classname} class:foo class:bar></b> + <i class:foo class:bar></i> +</main> + +<style> + div, + span { + color: red; + } +</style> diff --git a/packages/svelte/tests/runtime-runes/samples/class-directive/_config.js b/packages/svelte/tests/runtime-runes/samples/class-directive/_config.js new file mode 100644 index 0000000000..2756b40493 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-directive/_config.js @@ -0,0 +1,145 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: ` + <div class="svelte-tza1s0"></div> + <span></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0"></span></div> + + <div class="foo svelte-tza1s0"></div> + <span class="foo"></span> + <div class="svelte-tza1s0"><span class="foo svelte-tza1s0"></span></div> + + + <div class="foo svelte-tza1s0 bar"></div> + <span class="foo bar"></span> + <div class="svelte-tza1s0"><span class="foo svelte-tza1s0 bar"></span></div> + + <div class="svelte-tza1s0"></div> + <span></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0"></span></div> + + <div class="svelte-tza1s0 bar"></div> + <span class="bar"></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0 bar"></span></div> + + <div class="football svelte-tza1s0 bar"></div> + <span class="football bar"></span> + <div class="svelte-tza1s0"><span class="football svelte-tza1s0 bar"></span></div> + + <div class="svelte-tza1s0 bar not-foo"></div> + <span class="bar not-foo"></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0 bar not-foo"></span></div> + + `, + test({ assert, target, component }) { + component.foo = true; + flushSync(); + + assert.htmlEqual( + target.innerHTML, + ` + <div class="svelte-tza1s0"></div> + <span></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0"></span></div> + + <div class="foo svelte-tza1s0"></div> + <span class="foo"></span> + <div class="svelte-tza1s0"><span class="foo svelte-tza1s0"></span></div> + + <div class="foo svelte-tza1s0 bar"></div> + <span class="foo bar"></span> + <div class="svelte-tza1s0"><span class="foo svelte-tza1s0 bar"></span></div> + + <div class="svelte-tza1s0 foo"></div> + <span class="foo"></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0 foo"></span></div> + + <div class="svelte-tza1s0 bar foo"></div> + <span class="bar foo"></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0 bar foo"></span></div> + + <div class="football svelte-tza1s0 bar foo"></div> + <span class="football bar foo"></span> + <div class="svelte-tza1s0"><span class="football svelte-tza1s0 bar foo"></span></div> + + <div class="svelte-tza1s0 bar foo"></div> + <span class="bar foo"></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0 bar foo"></span></div> + ` + ); + + component.bar = false; + flushSync(); + + assert.htmlEqual( + target.innerHTML, + ` + <div class="svelte-tza1s0"></div> + <span></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0"></span></div> + + <div class="foo svelte-tza1s0"></div> + <span class="foo"></span> + <div class="svelte-tza1s0"><span class="foo svelte-tza1s0"></span></div> + + <div class="foo svelte-tza1s0"></div> + <span class="foo"></span> + <div class="svelte-tza1s0"><span class="foo svelte-tza1s0"></span></div> + + <div class="svelte-tza1s0 foo"></div> + <span class="foo"></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0 foo"></span></div> + + <div class="svelte-tza1s0 foo"></div> + <span class="foo"></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0 foo"></span></div> + + <div class="football svelte-tza1s0 foo"></div> + <span class="football foo"></span> + <div class="svelte-tza1s0"><span class="football svelte-tza1s0 foo"></span></div> + + <div class="svelte-tza1s0 foo"></div> + <span class="foo"></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0 foo"></span></div> + ` + ); + + component.foo = false; + flushSync(); + + assert.htmlEqual( + target.innerHTML, + ` + <div class="svelte-tza1s0"></div> + <span></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0"></span></div> + + <div class="foo svelte-tza1s0"></div> + <span class="foo"></span> + <div class="svelte-tza1s0"><span class="foo svelte-tza1s0"></span></div> + + <div class="foo svelte-tza1s0"></div> + <span class="foo"></span> + <div class="svelte-tza1s0"><span class="foo svelte-tza1s0"></span></div> + + <div class="svelte-tza1s0"></div> + <span class=""></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0"></span></div> + + <div class="svelte-tza1s0"></div> + <span class=""></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0"></span></div> + + <div class="football svelte-tza1s0"></div> + <span class="football"></span> + <div class="svelte-tza1s0"><span class="football svelte-tza1s0"></span></div> + + <div class="svelte-tza1s0 not-foo"></div> + <span class="not-foo"></span> + <div class="svelte-tza1s0"><span class="svelte-tza1s0 not-foo"></span></div> + ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-directive/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-directive/main.svelte new file mode 100644 index 0000000000..966c07a78e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-directive/main.svelte @@ -0,0 +1,40 @@ +<script> + let { foo = false, bar = true } = $props(); +</script> + +<div></div> +<span></span> +<div><span></span></div> + +<div class="foo"></div> +<span class="foo"></span> +<div><span class="foo"></span></div> + +<div class="foo" class:bar></div> +<span class="foo" class:bar></span> +<div><span class="foo" class:bar></span></div> + +<div class="foo" class:foo></div> +<span class="foo" class:foo></span> +<div><span class="foo" class:foo></span></div> + +<div class="foo" class:bar class:foo></div> +<span class="foo" class:bar class:foo></span> +<div><span class="foo" class:bar class:foo></span></div> + +<div class="football" class:bar class:foo></div> +<span class="football" class:bar class:foo></span> +<div><span class="football" class:bar class:foo></span></div> + +<div class="foo" class:bar class:foo class:not-foo={!foo}></div> +<span class="foo" class:bar class:foo class:not-foo={!foo}></span> +<div><span class="foo" class:bar class:foo class:not-foo={!foo}></span></div> + +<style> + div { + color: red; + } + div > span { + font-weight: bold; + } +</style> From 220c2418d167f0af732521d5ed34abf6d873760f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 07:52:46 -0500 Subject: [PATCH 0198/1429] Version Packages (#15358) * Version Packages * tweak --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/healthy-guests-itch.md | 5 ----- .changeset/serious-glasses-kiss.md | 5 ----- .changeset/stale-plums-drop.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 .changeset/healthy-guests-itch.md delete mode 100644 .changeset/serious-glasses-kiss.md delete mode 100644 .changeset/stale-plums-drop.md diff --git a/.changeset/healthy-guests-itch.md b/.changeset/healthy-guests-itch.md deleted file mode 100644 index 24b33f761d..0000000000 --- a/.changeset/healthy-guests-itch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -Allow `@const` inside `#key` diff --git a/.changeset/serious-glasses-kiss.md b/.changeset/serious-glasses-kiss.md deleted file mode 100644 index 29a6fafeae..0000000000 --- a/.changeset/serious-glasses-kiss.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: remove unnecessary `?? ''` on some expressions diff --git a/.changeset/stale-plums-drop.md b/.changeset/stale-plums-drop.md deleted file mode 100644 index d39268eb72..0000000000 --- a/.changeset/stale-plums-drop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly override class attributes with class directives diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index ab190e1cc2..5ebb088924 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.20.3 + +### Patch Changes + +- fix: allow `@const` inside `#key` ([#15377](https://github.com/sveltejs/svelte/pull/15377)) + +- fix: remove unnecessary `?? ''` on some expressions ([#15287](https://github.com/sveltejs/svelte/pull/15287)) + +- fix: correctly override class attributes with class directives ([#15352](https://github.com/sveltejs/svelte/pull/15352)) + ## 5.20.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 579660f9d7..5f16c54330 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.20.2", + "version": "5.20.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 0803fae736..12468d227d 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.20.2'; +export const VERSION = '5.20.3'; export const PUBLIC_VERSION = '5'; From 52d6ed1c7323b5896b37b62d42973c7365ae4b85 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Mon, 24 Feb 2025 08:40:21 -0500 Subject: [PATCH 0199/1429] remove instance_scope (#15364) --- packages/svelte/src/compiler/phases/2-analyze/index.js | 3 --- packages/svelte/src/compiler/phases/2-analyze/types.d.ts | 1 - 2 files changed, 4 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index ca9297279e..d7956c00b4 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -267,7 +267,6 @@ export function analyze_module(ast, options) { expression: null, function_depth: 0, has_props_rune: false, - instance_scope: /** @type {any} */ (null), options: /** @type {ValidatedCompileOptions} */ (options), parent_element: null, reactive_statement: null, @@ -620,7 +619,6 @@ export function analyze_component(root, source, options) { expression: null, derived_state: [], function_depth: scope.function_depth, - instance_scope: instance.scope, reactive_statement: null, reactive_statements: new Map() }; @@ -684,7 +682,6 @@ export function analyze_component(root, source, options) { parent_element: null, has_props_rune: false, ast_type: ast === instance.ast ? 'instance' : ast === template.ast ? 'template' : 'module', - instance_scope: instance.scope, reactive_statement: null, reactive_statements: analysis.reactive_statements, component_slots: new Set(), diff --git a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts index 70796a0d59..a114288345 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts +++ b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts @@ -23,7 +23,6 @@ export interface AnalysisState { function_depth: number; // legacy stuff - instance_scope: Scope; reactive_statement: null | ReactiveStatement; reactive_statements: Map<LabeledStatement, ReactiveStatement>; } From 53d1b17ce9c193c7fb5903eb4693fc0b7b908241 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Mon, 24 Feb 2025 08:40:35 -0500 Subject: [PATCH 0200/1429] remove some unused exports (#15365) --- packages/svelte/src/internal/client/dom/task.js | 2 +- packages/svelte/src/internal/client/reactivity/deriveds.js | 2 +- packages/svelte/src/internal/client/runtime.js | 6 +++--- packages/svelte/tests/snapshot/_config.js | 3 --- 4 files changed, 5 insertions(+), 8 deletions(-) delete mode 100644 packages/svelte/tests/snapshot/_config.js diff --git a/packages/svelte/src/internal/client/dom/task.js b/packages/svelte/src/internal/client/dom/task.js index acb5a5b117..95526b27a7 100644 --- a/packages/svelte/src/internal/client/dom/task.js +++ b/packages/svelte/src/internal/client/dom/task.js @@ -1,7 +1,7 @@ import { run_all } from '../../shared/utils.js'; // Fallback for when requestIdleCallback is not available -export const request_idle_callback = +const request_idle_callback = typeof requestIdleCallback === 'undefined' ? (/** @type {() => void} */ cb) => setTimeout(cb, 1) : requestIdleCallback; diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 59a7ed0f16..795417cc0f 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -116,7 +116,7 @@ function get_derived_parent_effect(derived) { * @param {Derived} derived * @returns {T} */ -export function execute_derived(derived) { +function execute_derived(derived) { var value; var prev_active_effect = active_effect; diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 8c98f948f3..a829aa13af 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -46,7 +46,7 @@ const FLUSH_SYNC = 1; // Used for DEV time error handling /** @param {WeakSet<Error>} value */ const handled_errors = new WeakSet(); -export let is_throwing_error = false; +let is_throwing_error = false; // Used for controlling the flush of effects. let scheduler_mode = FLUSH_MICROTASK; @@ -117,7 +117,7 @@ export function set_derived_sources(sources) { * and until a new dependency is accessed — we track this via `skipped_deps` * @type {null | Value[]} */ -export let new_deps = null; +let new_deps = null; let skipped_deps = 0; @@ -994,7 +994,7 @@ export function safe_get(signal) { * @template T * @param {() => T} fn */ -export function capture_signals(fn) { +function capture_signals(fn) { var previous_captured_signals = captured_signals; captured_signals = new Set(); diff --git a/packages/svelte/tests/snapshot/_config.js b/packages/svelte/tests/snapshot/_config.js deleted file mode 100644 index f47bee71df..0000000000 --- a/packages/svelte/tests/snapshot/_config.js +++ /dev/null @@ -1,3 +0,0 @@ -import { test } from '../../test'; - -export default test({}); From bfeb9ad448c649bc08952f9e998ecdadf187b5e6 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Mon, 24 Feb 2025 08:42:13 -0500 Subject: [PATCH 0201/1429] chore: create `binding.updated` getter (#15362) --- packages/svelte/src/compiler/phases/2-analyze/index.js | 2 +- .../compiler/phases/2-analyze/visitors/ExportSpecifier.js | 2 +- packages/svelte/src/compiler/phases/scope.js | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index d7956c00b4..54d05b46be 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -565,7 +565,7 @@ export function analyze_component(root, source, options) { binding.declaration_kind !== 'import' ) { binding.kind = 'state'; - binding.mutated = binding.updated = true; + binding.mutated = true; } } } diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js index cfb24970de..2a05ffb926 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportSpecifier.js @@ -22,7 +22,7 @@ export function ExportSpecifier(node, context) { }); const binding = context.state.scope.get(local_name); - if (binding) binding.reassigned = binding.updated = true; + if (binding) binding.reassigned = true; } } else { validate_export(node, context.state.scope, local_name); diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 41e1c78c05..5d2db43b92 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -60,7 +60,6 @@ export class Binding { is_called = false; mutated = false; reassigned = false; - updated = false; /** * @@ -77,6 +76,10 @@ export class Binding { this.kind = kind; this.declaration_kind = declaration_kind; } + + get updated() { + return this.mutated || this.reassigned; + } } export class Scope { @@ -738,8 +741,6 @@ export function create_scopes(ast, root, allow_reactive_declarations, parent) { const binding = left && scope.get(left.name); if (binding !== null && left !== binding.node) { - binding.updated = true; - if (left === expression) { binding.reassigned = true; } else { From 1b882fb1b5409da99261a1bf3a7aca6d32a1f9cf Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Mon, 24 Feb 2025 08:45:40 -0500 Subject: [PATCH 0202/1429] chore: remove reactive_statements from state (#15363) --- packages/svelte/src/compiler/phases/2-analyze/index.js | 7 ++----- packages/svelte/src/compiler/phases/2-analyze/types.d.ts | 2 -- .../compiler/phases/2-analyze/visitors/LabeledStatement.js | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 54d05b46be..322293bf6b 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -269,8 +269,7 @@ export function analyze_module(ast, options) { has_props_rune: false, options: /** @type {ValidatedCompileOptions} */ (options), parent_element: null, - reactive_statement: null, - reactive_statements: new Map() + reactive_statement: null }, visitors ); @@ -619,8 +618,7 @@ export function analyze_component(root, source, options) { expression: null, derived_state: [], function_depth: scope.function_depth, - reactive_statement: null, - reactive_statements: new Map() + reactive_statement: null }; walk(/** @type {AST.SvelteNode} */ (ast), state, visitors); @@ -683,7 +681,6 @@ export function analyze_component(root, source, options) { has_props_rune: false, ast_type: ast === instance.ast ? 'instance' : ast === template.ast ? 'template' : 'module', reactive_statement: null, - reactive_statements: analysis.reactive_statements, component_slots: new Set(), expression: null, derived_state: [], diff --git a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts index a114288345..17c8123de1 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/types.d.ts +++ b/packages/svelte/src/compiler/phases/2-analyze/types.d.ts @@ -1,7 +1,6 @@ import type { Scope } from '../scope.js'; import type { ComponentAnalysis, ReactiveStatement } from '../types.js'; import type { AST, ExpressionMetadata, ValidatedCompileOptions } from '#compiler'; -import type { LabeledStatement } from 'estree'; export interface AnalysisState { scope: Scope; @@ -24,7 +23,6 @@ export interface AnalysisState { // legacy stuff reactive_statement: null | ReactiveStatement; - reactive_statements: Map<LabeledStatement, ReactiveStatement>; } export type Context<State extends AnalysisState = AnalysisState> = import('zimmerframe').Context< diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/LabeledStatement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/LabeledStatement.js index a63480feaa..514cfae53c 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/LabeledStatement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/LabeledStatement.js @@ -64,7 +64,7 @@ export function LabeledStatement(node, context) { } } - context.state.reactive_statements.set(node, reactive_statement); + context.state.analysis.reactive_statements.set(node, reactive_statement); if ( node.body.type === 'ExpressionStatement' && From 7958eb74df364510bed706a72d671e94a330c17e Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Mon, 24 Feb 2025 08:46:35 -0500 Subject: [PATCH 0203/1429] chore: remove unnecessary `binding.is_called` property (#15361) --- .changeset/green-geckos-tickle.md | 5 +++++ .../src/compiler/phases/2-analyze/visitors/Attribute.js | 2 +- .../compiler/phases/2-analyze/visitors/CallExpression.js | 8 -------- .../2-analyze/visitors/TaggedTemplateExpression.js | 9 +-------- packages/svelte/src/compiler/phases/scope.js | 1 - 5 files changed, 7 insertions(+), 18 deletions(-) create mode 100644 .changeset/green-geckos-tickle.md diff --git a/.changeset/green-geckos-tickle.md b/.changeset/green-geckos-tickle.md new file mode 100644 index 0000000000..843b3d1bda --- /dev/null +++ b/.changeset/green-geckos-tickle.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: remove unnecessary `binding.is_called` property diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js index 42e4498969..561a004526 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -162,7 +162,7 @@ function get_delegated_event(event_name, handler, context) { return unhoisted; } - if (binding !== null && binding.initial !== null && !binding.updated && !binding.is_called) { + if (binding !== null && binding.initial !== null && !binding.updated) { const binding_type = binding.initial.type; if ( diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index ce520cc980..4d09d9293f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -213,14 +213,6 @@ export function CallExpression(node, context) { break; } - if (node.callee.type === 'Identifier') { - const binding = context.state.scope.get(node.callee.name); - - if (binding !== null) { - binding.is_called = true; - } - } - // `$inspect(foo)` or `$derived(foo) should not trigger the `static-state-reference` warning if (rune === '$inspect' || rune === '$derived') { context.next({ ...context.state, function_depth: context.state.function_depth + 1 }); diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js index eacb8a342a..881ee5a85e 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js @@ -1,4 +1,4 @@ -/** @import { TaggedTemplateExpression, VariableDeclarator } from 'estree' */ +/** @import { TaggedTemplateExpression } from 'estree' */ /** @import { Context } from '../types' */ import { is_pure } from './shared/utils.js'; @@ -12,12 +12,5 @@ export function TaggedTemplateExpression(node, context) { context.state.expression.has_state = true; } - if (node.tag.type === 'Identifier') { - const binding = context.state.scope.get(node.tag.name); - - if (binding !== null) { - binding.is_called = true; - } - } context.next(); } diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 5d2db43b92..f46adf4900 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -57,7 +57,6 @@ export class Binding { */ metadata = null; - is_called = false; mutated = false; reassigned = false; From 51337f22bddcbd2e2d2366f53aa4404f58b71403 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Mon, 24 Feb 2025 08:49:33 -0500 Subject: [PATCH 0204/1429] chore: simplify flushing (#15348) * this appears to be unnecessary * DRY out * this doesnt appear to do anything useful * simplify * remove unused if block * simplify, make non-recursive * unused * DRY * simplify * tidy up * simplify * changeset * unused * Revert "changeset" This reverts commit 946e00dcf7f773a37ffd8a5746aa7ac36ff9fc03. * make flush_sync non-recursive * fix flushSync types * fix * unused * simplify * tidy up * tidy up * present unnecessary microtasks, avoid flushing if no function provided * simplify --- .changeset/violet-camels-heal.md | 5 + .../reactivity/kairo/kairo_avoidable.js | 4 +- .../reactivity/kairo/kairo_broad.js | 4 +- .../benchmarks/reactivity/kairo/kairo_deep.js | 4 +- .../reactivity/kairo/kairo_diamond.js | 4 +- .../benchmarks/reactivity/kairo/kairo_mux.js | 4 +- .../reactivity/kairo/kairo_repeated.js | 4 +- .../reactivity/kairo/kairo_triangle.js | 4 +- .../reactivity/kairo/kairo_unstable.js | 4 +- .../benchmarks/reactivity/mol_bench.js | 4 +- .../3-transform/client/transform-client.js | 2 +- packages/svelte/src/index-client.js | 12 +- .../src/internal/client/dom/blocks/await.js | 4 +- .../svelte/src/internal/client/dom/task.js | 47 ++-- packages/svelte/src/internal/client/index.js | 2 +- .../src/internal/client/reactivity/effects.js | 8 - .../src/internal/client/reactivity/sources.js | 26 +-- .../svelte/src/internal/client/runtime.js | 204 +++++++----------- packages/svelte/src/legacy/legacy-client.js | 6 +- .../samples/animation-css/_config.js | 2 + .../samples/animation-js-easing/_config.js | 2 + .../samples/animation-js/_config.js | 4 + .../dynamic-element-animation/_config.js | 2 + packages/svelte/tests/store/test.ts | 8 +- packages/svelte/types/index.d.ts | 45 ++-- 25 files changed, 182 insertions(+), 233 deletions(-) create mode 100644 .changeset/violet-camels-heal.md diff --git a/.changeset/violet-camels-heal.md b/.changeset/violet-camels-heal.md new file mode 100644 index 0000000000..31e72fa33d --- /dev/null +++ b/.changeset/violet-camels-heal.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: update types and inline docs for flushSync diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js b/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js index 6b058cdc3c..9daea6de99 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_avoidable.js @@ -20,12 +20,12 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(computed5) === 6); for (let i = 0; i < 1000; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(computed5) === 6); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js b/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js index d1cde5958e..8dc5710c87 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_broad.js @@ -25,12 +25,12 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); counter = 0; for (let i = 0; i < 50; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(last) === i + 50); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js b/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js index 149457ede1..8690c85f86 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_deep.js @@ -25,12 +25,12 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); counter = 0; for (let i = 0; i < iter; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(current) === len + i); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js b/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js index 958a1bcd78..bf4e07ee89 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_diamond.js @@ -28,13 +28,13 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(sum) === 2 * width); counter = 0; for (let i = 0; i < 500; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(sum) === (i + 1) * width); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js b/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js index b645051c09..fc252a27b5 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_mux.js @@ -22,13 +22,13 @@ function setup() { destroy, run() { for (let i = 0; i < 10; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(heads[i], i); }); assert($.get(splited[i]) === i + 1); } for (let i = 0; i < 10; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(heads[i], i * 2); }); assert($.get(splited[i]) === i * 2 + 1); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js b/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js index 53b85acd37..3bee06ca0e 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_repeated.js @@ -25,13 +25,13 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(current) === size); counter = 0; for (let i = 0; i < 100; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(current) === i * size); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js b/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js index b9e2ad9fa4..11a419a52e 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_triangle.js @@ -38,13 +38,13 @@ function setup() { destroy, run() { const constant = count(width); - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(sum) === constant); counter = 0; for (let i = 0; i < 100; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); assert($.get(sum) === constant - width + i * width); diff --git a/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js b/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js index 0e783732dc..54eb732cb2 100644 --- a/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js +++ b/benchmarking/benchmarks/reactivity/kairo/kairo_unstable.js @@ -25,13 +25,13 @@ function setup() { return { destroy, run() { - $.flush_sync(() => { + $.flush(() => { $.set(head, 1); }); assert($.get(current) === 40); counter = 0; for (let i = 0; i < 100; i++) { - $.flush_sync(() => { + $.flush(() => { $.set(head, i); }); } diff --git a/benchmarking/benchmarks/reactivity/mol_bench.js b/benchmarking/benchmarks/reactivity/mol_bench.js index c9f492f619..536b078d74 100644 --- a/benchmarking/benchmarks/reactivity/mol_bench.js +++ b/benchmarking/benchmarks/reactivity/mol_bench.js @@ -51,11 +51,11 @@ function setup() { */ run(i) { res.length = 0; - $.flush_sync(() => { + $.flush(() => { $.set(B, 1); $.set(A, 1 + i * 2); }); - $.flush_sync(() => { + $.flush(() => { $.set(A, 2 + i * 2); $.set(B, 2); }); diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 2e6307a4b7..cf5ba285cb 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -312,7 +312,7 @@ export function client_component(analysis, options) { const setter = b.set(key, [ b.stmt(b.call(b.id(name), b.id('$$value'))), - b.stmt(b.call('$.flush_sync')) + b.stmt(b.call('$.flush')) ]); if (analysis.runes && binding.initial) { diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index ca29d5bfbe..efcf7b727b 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -1,7 +1,7 @@ /** @import { ComponentContext, ComponentContextLegacy } from '#client' */ /** @import { EventDispatcher } from './index.js' */ /** @import { NotFunction } from './internal/types.js' */ -import { flush_sync, untrack } from './internal/client/runtime.js'; +import { untrack } from './internal/client/runtime.js'; import { is_array } from './internal/shared/utils.js'; import { user_effect } from './internal/client/index.js'; import * as e from './internal/client/errors.js'; @@ -206,15 +206,7 @@ function init_update_callbacks(context) { return (l.u ??= { a: [], b: [], m: [] }); } -/** - * Synchronously flushes any pending state changes and those that result from it. - * @param {() => void} [fn] - * @returns {void} - */ -export function flushSync(fn) { - flush_sync(fn); -} - +export { flushSync } from './internal/client/runtime.js'; export { getContext, getAllContexts, hasContext, setContext } from './internal/client/context.js'; export { hydrate, mount, unmount } from './internal/client/render.js'; export { tick, untrack } from './internal/client/runtime.js'; diff --git a/packages/svelte/src/internal/client/dom/blocks/await.js b/packages/svelte/src/internal/client/dom/blocks/await.js index c8c7c1c0ea..2e3d229779 100644 --- a/packages/svelte/src/internal/client/dom/blocks/await.js +++ b/packages/svelte/src/internal/client/dom/blocks/await.js @@ -3,7 +3,7 @@ import { DEV } from 'esm-env'; import { is_promise } from '../../../shared/utils.js'; import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js'; import { internal_set, mutable_source, source } from '../../reactivity/sources.js'; -import { flush_sync, set_active_effect, set_active_reaction } from '../../runtime.js'; +import { flushSync, set_active_effect, set_active_reaction } from '../../runtime.js'; import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; import { queue_micro_task } from '../task.js'; import { UNINITIALIZED } from '../../../../constants.js'; @@ -105,7 +105,7 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) { // without this, the DOM does not update until two ticks after the promise // resolves, which is unexpected behaviour (and somewhat irksome to test) - flush_sync(); + flushSync(); } } } diff --git a/packages/svelte/src/internal/client/dom/task.js b/packages/svelte/src/internal/client/dom/task.js index 95526b27a7..48a2fbe660 100644 --- a/packages/svelte/src/internal/client/dom/task.js +++ b/packages/svelte/src/internal/client/dom/task.js @@ -6,25 +6,21 @@ const request_idle_callback = ? (/** @type {() => void} */ cb) => setTimeout(cb, 1) : requestIdleCallback; -let is_micro_task_queued = false; -let is_idle_task_queued = false; - /** @type {Array<() => void>} */ -let current_queued_micro_tasks = []; +let micro_tasks = []; + /** @type {Array<() => void>} */ -let current_queued_idle_tasks = []; +let idle_tasks = []; -function process_micro_tasks() { - is_micro_task_queued = false; - const tasks = current_queued_micro_tasks.slice(); - current_queued_micro_tasks = []; +function run_micro_tasks() { + var tasks = micro_tasks; + micro_tasks = []; run_all(tasks); } -function process_idle_tasks() { - is_idle_task_queued = false; - const tasks = current_queued_idle_tasks.slice(); - current_queued_idle_tasks = []; +function run_idle_tasks() { + var tasks = idle_tasks; + idle_tasks = []; run_all(tasks); } @@ -32,32 +28,33 @@ function process_idle_tasks() { * @param {() => void} fn */ export function queue_micro_task(fn) { - if (!is_micro_task_queued) { - is_micro_task_queued = true; - queueMicrotask(process_micro_tasks); + if (micro_tasks.length === 0) { + queueMicrotask(run_micro_tasks); } - current_queued_micro_tasks.push(fn); + + micro_tasks.push(fn); } /** * @param {() => void} fn */ export function queue_idle_task(fn) { - if (!is_idle_task_queued) { - is_idle_task_queued = true; - request_idle_callback(process_idle_tasks); + if (idle_tasks.length === 0) { + request_idle_callback(run_idle_tasks); } - current_queued_idle_tasks.push(fn); + + idle_tasks.push(fn); } /** * Synchronously run any queued tasks. */ export function flush_tasks() { - if (is_micro_task_queued) { - process_micro_tasks(); + if (micro_tasks.length > 0) { + run_micro_tasks(); } - if (is_idle_task_queued) { - process_idle_tasks(); + + if (idle_tasks.length > 0) { + run_idle_tasks(); } } diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 431ac8cf24..31da00dbb4 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -139,7 +139,7 @@ export { get, safe_get, invalidate_inner_signals, - flush_sync, + flushSync as flush, tick, untrack, exclude_from_object, diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 9d7b5e9de6..28589ce94d 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -6,15 +6,12 @@ import { update_effect, get, is_destroying_effect, - is_flushing_effect, remove_reactions, schedule_effect, set_active_reaction, set_is_destroying_effect, - set_is_flushing_effect, set_signal_status, untrack, - skip_reaction, untracking } from '../runtime.js'; import { @@ -118,17 +115,12 @@ function create_effect(type, fn, sync, push = true) { } if (sync) { - var previously_flushing_effect = is_flushing_effect; - try { - set_is_flushing_effect(true); update_effect(effect); effect.f |= EFFECT_RAN; } catch (e) { destroy_effect(effect); throw e; - } finally { - set_is_flushing_effect(previously_flushing_effect); } } else if (fn !== null) { schedule_effect(effect); diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index ded0ca0584..f6a3fd7e33 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -14,8 +14,6 @@ import { derived_sources, set_derived_sources, check_dirtiness, - set_is_flushing_effect, - is_flushing_effect, untracking } from '../runtime.js'; import { equals, safe_equals } from './equality.js'; @@ -202,22 +200,18 @@ export function internal_set(source, value) { if (DEV && inspect_effects.size > 0) { const inspects = Array.from(inspect_effects); - var previously_flushing_effect = is_flushing_effect; - set_is_flushing_effect(true); - try { - for (const effect of inspects) { - // Mark clean inspect-effects as maybe dirty and then check their dirtiness - // instead of just updating the effects - this way we avoid overfiring. - if ((effect.f & CLEAN) !== 0) { - set_signal_status(effect, MAYBE_DIRTY); - } - if (check_dirtiness(effect)) { - update_effect(effect); - } + + for (const effect of inspects) { + // Mark clean inspect-effects as maybe dirty and then check their dirtiness + // instead of just updating the effects - this way we avoid overfiring. + if ((effect.f & CLEAN) !== 0) { + set_signal_status(effect, MAYBE_DIRTY); + } + if (check_dirtiness(effect)) { + update_effect(effect); } - } finally { - set_is_flushing_effect(previously_flushing_effect); } + inspect_effects.clear(); } } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index a829aa13af..fe4104c108 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -9,7 +9,6 @@ import { } from './reactivity/effects.js'; import { EFFECT, - RENDER_EFFECT, DIRTY, MAYBE_DIRTY, CLEAN, @@ -41,28 +40,19 @@ import { } from './context.js'; import { is_firefox } from './dom/operations.js'; -const FLUSH_MICROTASK = 0; -const FLUSH_SYNC = 1; // Used for DEV time error handling /** @param {WeakSet<Error>} value */ const handled_errors = new WeakSet(); let is_throwing_error = false; -// Used for controlling the flush of effects. -let scheduler_mode = FLUSH_MICROTASK; -// Used for handling scheduling -let is_micro_task_queued = false; +let is_flushing = false; /** @type {Effect | null} */ let last_scheduled_effect = null; -export let is_flushing_effect = false; -export let is_destroying_effect = false; +let is_updating_effect = false; -/** @param {boolean} value */ -export function set_is_flushing_effect(value) { - is_flushing_effect = value; -} +export let is_destroying_effect = false; /** @param {boolean} value */ export function set_is_destroying_effect(value) { @@ -74,7 +64,6 @@ export function set_is_destroying_effect(value) { /** @type {Effect[]} */ let queued_root_effects = []; -let flush_count = 0; /** @type {Effect[]} Stack of effects, dev only */ let dev_effect_stack = []; // Handle signal reactivity tree dependencies and reactions @@ -410,10 +399,9 @@ export function update_reaction(reaction) { new_deps = /** @type {null | Value[]} */ (null); skipped_deps = 0; untracked_writes = null; - active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; skip_reaction = - (flags & UNOWNED) !== 0 && - (!is_flushing_effect || previous_reaction === null || previous_untracking); + (flags & UNOWNED) !== 0 && (untracking || !is_updating_effect || active_reaction === null); + active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; derived_sources = null; set_component_context(reaction.ctx); @@ -559,8 +547,10 @@ export function update_effect(effect) { var previous_effect = active_effect; var previous_component_context = component_context; + var was_updating_effect = is_updating_effect; active_effect = effect; + is_updating_effect = true; if (DEV) { var previous_component_fn = dev_current_component_function; @@ -602,6 +592,7 @@ export function update_effect(effect) { } catch (error) { handle_error(error, effect, previous_effect, previous_component_context || effect.ctx); } finally { + is_updating_effect = was_updating_effect; active_effect = previous_effect; if (DEV) { @@ -620,69 +611,70 @@ function log_effect_stack() { } function infinite_loop_guard() { - if (flush_count > 1000) { - flush_count = 0; - try { - e.effect_update_depth_exceeded(); - } catch (error) { + try { + e.effect_update_depth_exceeded(); + } catch (error) { + if (DEV) { + // stack is garbage, ignore. Instead add a console.error message. + define_property(error, 'stack', { + value: '' + }); + } + // Try and handle the error so it can be caught at a boundary, that's + // if there's an effect available from when it was last scheduled + if (last_scheduled_effect !== null) { if (DEV) { - // stack is garbage, ignore. Instead add a console.error message. - define_property(error, 'stack', { - value: '' - }); - } - // Try and handle the error so it can be caught at a boundary, that's - // if there's an effect available from when it was last scheduled - if (last_scheduled_effect !== null) { - if (DEV) { - try { - handle_error(error, last_scheduled_effect, null, null); - } catch (e) { - // Only log the effect stack if the error is re-thrown - log_effect_stack(); - throw e; - } - } else { + try { handle_error(error, last_scheduled_effect, null, null); - } - } else { - if (DEV) { + } catch (e) { + // Only log the effect stack if the error is re-thrown log_effect_stack(); + throw e; } - throw error; + } else { + handle_error(error, last_scheduled_effect, null, null); } + } else { + if (DEV) { + log_effect_stack(); + } + throw error; } } - flush_count++; } -/** - * @param {Array<Effect>} root_effects - * @returns {void} - */ -function flush_queued_root_effects(root_effects) { - var length = root_effects.length; - if (length === 0) { - return; - } - infinite_loop_guard(); - - var previously_flushing_effect = is_flushing_effect; - is_flushing_effect = true; - +function flush_queued_root_effects() { try { - for (var i = 0; i < length; i++) { - var effect = root_effects[i]; + var flush_count = 0; - if ((effect.f & CLEAN) === 0) { - effect.f ^= CLEAN; + while (queued_root_effects.length > 0) { + if (flush_count++ > 1000) { + infinite_loop_guard(); } - var collected_effects = process_effects(effect); - flush_queued_effects(collected_effects); + var root_effects = queued_root_effects; + var length = root_effects.length; + + queued_root_effects = []; + + for (var i = 0; i < length; i++) { + var root = root_effects[i]; + + if ((root.f & CLEAN) === 0) { + root.f ^= CLEAN; + } + + var collected_effects = process_effects(root); + flush_queued_effects(collected_effects); + } } } finally { - is_flushing_effect = previously_flushing_effect; + is_flushing = false; + + last_scheduled_effect = null; + if (DEV) { + dev_effect_stack = []; + } } } @@ -724,39 +716,17 @@ function flush_queued_effects(effects) { } } -function process_deferred() { - is_micro_task_queued = false; - if (flush_count > 1001) { - return; - } - const previous_queued_root_effects = queued_root_effects; - queued_root_effects = []; - flush_queued_root_effects(previous_queued_root_effects); - - if (!is_micro_task_queued) { - flush_count = 0; - last_scheduled_effect = null; - if (DEV) { - dev_effect_stack = []; - } - } -} - /** * @param {Effect} signal * @returns {void} */ export function schedule_effect(signal) { - if (scheduler_mode === FLUSH_MICROTASK) { - if (!is_micro_task_queued) { - is_micro_task_queued = true; - queueMicrotask(process_deferred); - } + if (!is_flushing) { + is_flushing = true; + queueMicrotask(flush_queued_root_effects); } - last_scheduled_effect = signal; - - var effect = signal; + var effect = (last_scheduled_effect = signal); while (effect.parent !== null) { effect = effect.parent; @@ -846,42 +816,30 @@ function process_effects(effect) { } /** - * Internal version of `flushSync` with the option to not flush previous effects. - * Returns the result of the passed function, if given. - * @param {() => any} [fn] - * @returns {any} + * Synchronously flush any pending updates. + * Returns void if no callback is provided, otherwise returns the result of calling the callback. + * @template [T=void] + * @param {(() => T) | undefined} [fn] + * @returns {T} */ -export function flush_sync(fn) { - var previous_scheduler_mode = scheduler_mode; - var previous_queued_root_effects = queued_root_effects; - - try { - infinite_loop_guard(); +export function flushSync(fn) { + var result; - scheduler_mode = FLUSH_SYNC; - queued_root_effects = []; - is_micro_task_queued = false; - - flush_queued_root_effects(previous_queued_root_effects); + if (fn) { + is_flushing = true; + flush_queued_root_effects(); + result = fn(); + } - var result = fn?.(); + flush_tasks(); + while (queued_root_effects.length > 0) { + is_flushing = true; + flush_queued_root_effects(); flush_tasks(); - if (queued_root_effects.length > 0) { - flush_sync(); - } - - flush_count = 0; - last_scheduled_effect = null; - if (DEV) { - dev_effect_stack = []; - } - - return result; - } finally { - scheduler_mode = previous_scheduler_mode; - queued_root_effects = previous_queued_root_effects; } + + return /** @type {T} */ (result); } /** @@ -890,9 +848,9 @@ export function flush_sync(fn) { */ export async function tick() { await Promise.resolve(); - // By calling flush_sync we guarantee that any pending state changes are applied after one tick. + // By calling flushSync we guarantee that any pending state changes are applied after one tick. // TODO look into whether we can make flushing subsequent updates synchronously in the future. - flush_sync(); + flushSync(); } /** diff --git a/packages/svelte/src/legacy/legacy-client.js b/packages/svelte/src/legacy/legacy-client.js index 3a05bc0496..bb9a5a9c03 100644 --- a/packages/svelte/src/legacy/legacy-client.js +++ b/packages/svelte/src/legacy/legacy-client.js @@ -3,7 +3,7 @@ import { DIRTY, LEGACY_PROPS, MAYBE_DIRTY } from '../internal/client/constants.j import { user_pre_effect } from '../internal/client/reactivity/effects.js'; import { mutable_source, set } from '../internal/client/reactivity/sources.js'; import { hydrate, mount, unmount } from '../internal/client/render.js'; -import { active_effect, flush_sync, get, set_signal_status } from '../internal/client/runtime.js'; +import { active_effect, flushSync, get, set_signal_status } from '../internal/client/runtime.js'; import { lifecycle_outside_component } from '../internal/shared/errors.js'; import { define_property, is_array } from '../internal/shared/utils.js'; import * as w from '../internal/client/warnings.js'; @@ -119,9 +119,9 @@ class Svelte4Component { recover: options.recover }); - // We don't flush_sync for custom element wrappers or if the user doesn't want it + // We don't flushSync for custom element wrappers or if the user doesn't want it if (!options?.props?.$$host || options.sync === false) { - flush_sync(); + flushSync(); } this.#events = props.$$events; diff --git a/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js b/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js index b6b601a96b..b6bd818e65 100644 --- a/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/animation-css/_config.js @@ -47,6 +47,8 @@ export default test({ { id: 1, name: 'a' } ]; + raf.tick(0); + divs = target.querySelectorAll('div'); assert.ok(divs[0].getAnimations().length > 0); assert.equal(divs[1].getAnimations().length, 0); diff --git a/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js b/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js index 5b7ed1c732..f4a3554b29 100644 --- a/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/animation-js-easing/_config.js @@ -46,6 +46,8 @@ export default test({ { id: 1, name: 'a' } ]; + raf.tick(0); + divs = document.querySelectorAll('div'); assert.equal(divs[0].dy, 120); assert.equal(divs[4].dy, -120); diff --git a/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js b/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js index 3606f7d17b..a2e17b49f8 100644 --- a/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/animation-js/_config.js @@ -46,6 +46,8 @@ export default test({ { id: 1, name: 'a' } ]; + raf.tick(0); + divs = document.querySelectorAll('div'); assert.equal(divs[0].dy, 120); assert.equal(divs[4].dy, -120); @@ -66,6 +68,8 @@ export default test({ { id: 5, name: 'e' } ]; + raf.tick(100); + divs = document.querySelectorAll('div'); assert.equal(divs[0].dy, 120); diff --git a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js index 3d127f1375..05c2dc7304 100644 --- a/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/dynamic-element-animation/_config.js @@ -50,6 +50,8 @@ export default test({ { id: 1, name: 'a' } ]; + raf.tick(0); + divs = target.querySelectorAll('div'); assert.equal(divs[0].style.transform, 'translate(0px, 120px)'); assert.equal(divs[1].style.transform, ''); diff --git a/packages/svelte/tests/store/test.ts b/packages/svelte/tests/store/test.ts index b23ea195d6..77cecca7e5 100644 --- a/packages/svelte/tests/store/test.ts +++ b/packages/svelte/tests/store/test.ts @@ -602,7 +602,7 @@ describe('toStore', () => { assert.deepEqual(log, [0]); set(count, 1); - $.flush_sync(); + $.flushSync(); assert.deepEqual(log, [0, 1]); unsubscribe(); @@ -625,7 +625,7 @@ describe('toStore', () => { assert.deepEqual(log, [0]); set(count, 1); - $.flush_sync(); + $.flushSync(); assert.deepEqual(log, [0, 1]); store.set(2); @@ -654,11 +654,11 @@ describe('fromStore', () => { assert.deepEqual(log, [0]); store.set(1); - $.flush_sync(); + $.flushSync(); assert.deepEqual(log, [0, 1]); count.current = 2; - $.flush_sync(); + $.flushSync(); assert.deepEqual(log, [0, 1, 2]); assert.equal(get(store), 2); diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index b9ab8a522c..4c47661af8 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -408,10 +408,6 @@ declare module 'svelte' { * @deprecated Use [`$effect`](https://svelte.dev/docs/svelte/$effect) instead * */ export function afterUpdate(fn: () => void): void; - /** - * Synchronously flushes any pending state changes and those that result from it. - * */ - export function flushSync(fn?: (() => void) | undefined): void; /** * Create a snippet programmatically * */ @@ -421,6 +417,29 @@ declare module 'svelte' { }): Snippet<Params>; /** Anything except a function */ type NotFunction<T> = T extends Function ? never : T; + /** + * Synchronously flush any pending updates. + * Returns void if no callback is provided, otherwise returns the result of calling the callback. + * */ + export function flushSync<T = void>(fn?: (() => T) | undefined): T; + /** + * Returns a promise that resolves once any pending state changes have been applied. + * */ + export function tick(): Promise<void>; + /** + * When used inside a [`$derived`](https://svelte.dev/docs/svelte/$derived) or [`$effect`](https://svelte.dev/docs/svelte/$effect), + * any state read inside `fn` will not be treated as a dependency. + * + * ```ts + * $effect(() => { + * // this will run when `data` changes, but not when `time` changes + * save(data, { + * timestamp: untrack(() => time) + * }); + * }); + * ``` + * */ + export function untrack<T>(fn: () => T): T; /** * Retrieves the context that belongs to the closest parent component with the specified `key`. * Must be called during component initialisation. @@ -494,24 +513,6 @@ declare module 'svelte' { export function unmount(component: Record<string, any>, options?: { outro?: boolean; } | undefined): Promise<void>; - /** - * Returns a promise that resolves once any pending state changes have been applied. - * */ - export function tick(): Promise<void>; - /** - * When used inside a [`$derived`](https://svelte.dev/docs/svelte/$derived) or [`$effect`](https://svelte.dev/docs/svelte/$effect), - * any state read inside `fn` will not be treated as a dependency. - * - * ```ts - * $effect(() => { - * // this will run when `data` changes, but not when `time` changes - * save(data, { - * timestamp: untrack(() => time) - * }); - * }); - * ``` - * */ - export function untrack<T>(fn: () => T): T; type Getters<T> = { [K in keyof T]: () => T[K]; }; From 5f3b4d54213e65185efac8c48fe07365b56948a8 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Mon, 24 Feb 2025 08:50:50 -0500 Subject: [PATCH 0205/1429] chore: DRY out assignment validation (#15360) --- .../phases/2-analyze/visitors/BindDirective.js | 12 ++---------- .../phases/2-analyze/visitors/shared/utils.js | 4 ++-- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js index 7719eee677..509fecf301 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js @@ -5,7 +5,7 @@ import { is_text_attribute, object } from '../../../utils/ast.js'; -import { validate_no_const_assignment } from './shared/utils.js'; +import { validate_assignment } from './shared/utils.js'; import * as e from '../../../errors.js'; import * as w from '../../../warnings.js'; import { binding_properties } from '../../bindings.js'; @@ -158,7 +158,7 @@ export function BindDirective(node, context) { return; } - validate_no_const_assignment(node, node.expression, context.state.scope, true); + validate_assignment(node, node.expression, context.state); const assignee = node.expression; const left = object(assignee); @@ -184,14 +184,6 @@ export function BindDirective(node, context) { ) { e.bind_invalid_value(node.expression); } - - if (context.state.analysis.runes && binding?.kind === 'each') { - e.each_item_invalid_assignment(node); - } - - if (binding?.kind === 'snippet') { - e.snippet_parameter_assignment(node); - } } if (node.name === 'group') { diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js index 1507123e13..04f4347a40 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js @@ -10,12 +10,12 @@ import * as b from '../../../../utils/builders.js'; import { get_rune } from '../../../scope.js'; /** - * @param {AssignmentExpression | UpdateExpression} node + * @param {AssignmentExpression | UpdateExpression | AST.BindDirective} node * @param {Pattern | Expression} argument * @param {AnalysisState} state */ export function validate_assignment(node, argument, state) { - validate_no_const_assignment(node, argument, state.scope, false); + validate_no_const_assignment(node, argument, state.scope, node.type === 'BindDirective'); if (argument.type === 'Identifier') { const binding = state.scope.get(argument.name); From bbeeed421bc975f81b192657537141d276fc4a67 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 08:53:14 -0500 Subject: [PATCH 0206/1429] Version Packages (#15380) * Version Packages * this changeset was unnecessary --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/green-geckos-tickle.md | 5 ----- .changeset/violet-camels-heal.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 8 insertions(+), 12 deletions(-) delete mode 100644 .changeset/green-geckos-tickle.md delete mode 100644 .changeset/violet-camels-heal.md diff --git a/.changeset/green-geckos-tickle.md b/.changeset/green-geckos-tickle.md deleted file mode 100644 index 843b3d1bda..0000000000 --- a/.changeset/green-geckos-tickle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: remove unnecessary `binding.is_called` property diff --git a/.changeset/violet-camels-heal.md b/.changeset/violet-camels-heal.md deleted file mode 100644 index 31e72fa33d..0000000000 --- a/.changeset/violet-camels-heal.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: update types and inline docs for flushSync diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 5ebb088924..907c5a3534 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.20.4 + +### Patch Changes + +- fix: update types and inline docs for flushSync ([#15348](https://github.com/sveltejs/svelte/pull/15348)) + ## 5.20.3 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 5f16c54330..399d908e7a 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.20.3", + "version": "5.20.4", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 12468d227d..e893def326 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.20.3'; +export const VERSION = '5.20.4'; export const PUBLIC_VERSION = '5'; From c2ec0d9ac29441ca387d74f0814dd27becf64369 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Mon, 24 Feb 2025 23:38:01 +0100 Subject: [PATCH 0207/1429] fix: allow for duplicate `var` declarations (#15382) --- .changeset/spotty-drinks-tan.md | 5 +++++ packages/svelte/src/compiler/phases/scope.js | 8 ++++++-- .../validator/samples/multiple-var-same-name/errors.json | 1 + .../validator/samples/multiple-var-same-name/input.svelte | 6 ++++++ 4 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 .changeset/spotty-drinks-tan.md create mode 100644 packages/svelte/tests/validator/samples/multiple-var-same-name/errors.json create mode 100644 packages/svelte/tests/validator/samples/multiple-var-same-name/input.svelte diff --git a/.changeset/spotty-drinks-tan.md b/.changeset/spotty-drinks-tan.md new file mode 100644 index 0000000000..2150c8cffd --- /dev/null +++ b/.changeset/spotty-drinks-tan.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow for duplicate `var` declarations diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index f46adf4900..7d9f90982a 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -161,8 +161,12 @@ export class Scope { } if (this.declarations.has(node.name)) { - // This also errors on var/function types, but that's arguably a good thing - e.declaration_duplicate(node, node.name); + const binding = this.declarations.get(node.name); + if (binding && binding.declaration_kind !== 'var' && declaration_kind !== 'var') { + // This also errors on function types, but that's arguably a good thing + // declaring function twice is also caught by acorn in the parse phase + e.declaration_duplicate(node, node.name); + } } const binding = new Binding(this, node, kind, declaration_kind, initial); diff --git a/packages/svelte/tests/validator/samples/multiple-var-same-name/errors.json b/packages/svelte/tests/validator/samples/multiple-var-same-name/errors.json new file mode 100644 index 0000000000..fe51488c70 --- /dev/null +++ b/packages/svelte/tests/validator/samples/multiple-var-same-name/errors.json @@ -0,0 +1 @@ +[] diff --git a/packages/svelte/tests/validator/samples/multiple-var-same-name/input.svelte b/packages/svelte/tests/validator/samples/multiple-var-same-name/input.svelte new file mode 100644 index 0000000000..19a8ef7722 --- /dev/null +++ b/packages/svelte/tests/validator/samples/multiple-var-same-name/input.svelte @@ -0,0 +1,6 @@ +<script> + var test = ""; + var test = 42; +</script> + +{test} \ No newline at end of file From 3d59e84a65c855747ff17a226716ffa628d91aeb Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Tue, 25 Feb 2025 00:22:39 +0100 Subject: [PATCH 0208/1429] fix: allow double hyphen css selector names (#15384) --- .changeset/fast-pants-decide.md | 5 +++++ packages/svelte/src/compiler/phases/1-parse/read/style.js | 4 ++-- .../svelte/tests/css/samples/double-hyphen/expected.css | 4 ++++ .../svelte/tests/css/samples/double-hyphen/input.svelte | 7 +++++++ 4 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 .changeset/fast-pants-decide.md create mode 100644 packages/svelte/tests/css/samples/double-hyphen/expected.css create mode 100644 packages/svelte/tests/css/samples/double-hyphen/input.svelte diff --git a/.changeset/fast-pants-decide.md b/.changeset/fast-pants-decide.md new file mode 100644 index 0000000000..396b80c0e0 --- /dev/null +++ b/.changeset/fast-pants-decide.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow double hyphen css selector names diff --git a/packages/svelte/src/compiler/phases/1-parse/read/style.js b/packages/svelte/src/compiler/phases/1-parse/read/style.js index 29e8a0e541..f8c39f1b1d 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/style.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/style.js @@ -566,7 +566,7 @@ function read_attribute_value(parser) { } /** - * https://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + * https://www.w3.org/TR/css-syntax-3/#ident-token-diagram * @param {Parser} parser */ function read_identifier(parser) { @@ -574,7 +574,7 @@ function read_identifier(parser) { let identifier = ''; - if (parser.match('--') || parser.match_regex(REGEX_LEADING_HYPHEN_OR_DIGIT)) { + if (parser.match_regex(REGEX_LEADING_HYPHEN_OR_DIGIT)) { e.css_expected_identifier(start); } diff --git a/packages/svelte/tests/css/samples/double-hyphen/expected.css b/packages/svelte/tests/css/samples/double-hyphen/expected.css new file mode 100644 index 0000000000..ab4921b657 --- /dev/null +++ b/packages/svelte/tests/css/samples/double-hyphen/expected.css @@ -0,0 +1,4 @@ + + .--foo.svelte-xyz { + color: red; + } diff --git a/packages/svelte/tests/css/samples/double-hyphen/input.svelte b/packages/svelte/tests/css/samples/double-hyphen/input.svelte new file mode 100644 index 0000000000..e2907f9344 --- /dev/null +++ b/packages/svelte/tests/css/samples/double-hyphen/input.svelte @@ -0,0 +1,7 @@ +<div class="--foo"></div> + +<style> + .--foo { + color: red; + } +</style> \ No newline at end of file From da98c894b82c9265c57f5a7a5d596369d7c1a05f Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 25 Feb 2025 09:33:31 -0800 Subject: [PATCH 0209/1429] chore: switch to `tinyglobby` (#15388) * chore: switch to tinyglobby * update snapshot * update parameter name * changeset * delete changeset --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- packages/svelte/package.json | 4 +- packages/svelte/tests/helpers.js | 4 +- .../samples/script/expected_map.json | 2 +- .../svelte/tests/runtime-legacy/shared.ts | 4 +- packages/svelte/tests/snapshot/test.ts | 6 +- playgrounds/sandbox/package.json | 2 +- playgrounds/sandbox/run.js | 6 +- pnpm-lock.yaml | 247 +++++------------- 8 files changed, 77 insertions(+), 198 deletions(-) diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 399d908e7a..dd472e4413 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -137,11 +137,11 @@ "@rollup/plugin-virtual": "^3.0.2", "@types/aria-query": "^5.0.4", "@types/node": "^20.11.5", - "dts-buddy": "^0.5.3", + "dts-buddy": "^0.5.5", "esbuild": "^0.21.5", "rollup": "^4.22.4", "source-map": "^0.7.4", - "tiny-glob": "^0.2.9", + "tinyglobby": "^0.2.12", "typescript": "^5.5.4", "vitest": "^2.1.9" }, diff --git a/packages/svelte/tests/helpers.js b/packages/svelte/tests/helpers.js index 9d7f71c9bd..87bcb473e7 100644 --- a/packages/svelte/tests/helpers.js +++ b/packages/svelte/tests/helpers.js @@ -1,7 +1,7 @@ /** @import { CompileOptions } from '#compiler' */ import * as fs from 'node:fs'; import * as path from 'node:path'; -import glob from 'tiny-glob/sync.js'; +import { globSync } from 'tinyglobby'; import { VERSION, compile, compileModule, preprocess } from 'svelte/compiler'; import { vi } from 'vitest'; @@ -70,7 +70,7 @@ export async function compile_directory( fs.rmSync(output_dir, { recursive: true, force: true }); - for (let file of glob('**', { cwd, filesOnly: true })) { + for (let file of globSync('**', { cwd, onlyFiles: true })) { if (file.startsWith('_')) continue; let text = fs.readFileSync(`${cwd}/${file}`, 'utf-8').replace(/\r\n/g, '\n'); diff --git a/packages/svelte/tests/preprocess/samples/script/expected_map.json b/packages/svelte/tests/preprocess/samples/script/expected_map.json index d5bf98483f..22275b5338 100644 --- a/packages/svelte/tests/preprocess/samples/script/expected_map.json +++ b/packages/svelte/tests/preprocess/samples/script/expected_map.json @@ -1,6 +1,6 @@ { "version": 3, - "mappings": "AAAA,CAAC,MAAM,CAAC;AACR,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAc,CAAC,CAAC;AAC7B,CAAC,CAAC,MAAM", + "mappings": "AAAA,CAAC,MAAM;AACP,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAc,CAAC;AAC5B,CAAC,CAAC,MAAM", "names": [], "sources": [ "input.svelte" diff --git a/packages/svelte/tests/runtime-legacy/shared.ts b/packages/svelte/tests/runtime-legacy/shared.ts index 76036852c2..3ffb3092a4 100644 --- a/packages/svelte/tests/runtime-legacy/shared.ts +++ b/packages/svelte/tests/runtime-legacy/shared.ts @@ -1,6 +1,6 @@ import * as fs from 'node:fs'; import { setImmediate } from 'node:timers/promises'; -import glob from 'tiny-glob/sync.js'; +import { globSync } from 'tinyglobby'; import { createClassComponent } from 'svelte/legacy'; import { proxy } from 'svelte/internal/client'; import { flushSync, hydrate, mount, unmount } from 'svelte'; @@ -257,7 +257,7 @@ async function run_test_variant( raf.reset(); // Put things we need on window for testing - const styles = glob('**/*.css', { cwd: `${cwd}/_output/client` }) + const styles = globSync('**/*.css', { cwd: `${cwd}/_output/client` }) .map((file) => fs.readFileSync(`${cwd}/_output/client/${file}`, 'utf-8')) .join('\n') .replace(/\/\*<\/?style>\*\//g, ''); diff --git a/packages/svelte/tests/snapshot/test.ts b/packages/svelte/tests/snapshot/test.ts index 88cf9193c3..0a591c6e2a 100644 --- a/packages/svelte/tests/snapshot/test.ts +++ b/packages/svelte/tests/snapshot/test.ts @@ -1,6 +1,6 @@ import * as fs from 'node:fs'; import { assert, expect } from 'vitest'; -import glob from 'tiny-glob/sync.js'; +import { globSync } from 'tinyglobby'; import { compile_directory } from '../helpers.js'; import { suite, type BaseTest } from '../suite.js'; import { VERSION } from 'svelte/compiler'; @@ -18,8 +18,8 @@ const { test, run } = suite<SnapshotTest>(async (config, cwd) => { fs.rmSync(`${cwd}/_expected`, { recursive: true, force: true }); fs.cpSync(`${cwd}/_output`, `${cwd}/_expected`, { recursive: true, force: true }); } else { - const actual = glob('**', { cwd: `${cwd}/_output`, filesOnly: true }); - const expected = glob('**', { cwd: `${cwd}/_expected`, filesOnly: true }); + const actual = globSync('**', { cwd: `${cwd}/_output`, onlyFiles: true }); + const expected = globSync('**', { cwd: `${cwd}/_expected`, onlyFiles: true }); assert.deepEqual(actual, expected); diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index b5bd8ab2f9..9e2e5f6738 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -17,7 +17,7 @@ "@sveltejs/vite-plugin-svelte": "^4.0.0-next.6", "polka": "^1.0.0-next.25", "svelte": "workspace:*", - "tiny-glob": "^0.2.9", + "tinyglobby": "^0.2.12", "vite": "^5.4.14", "vite-plugin-inspect": "^0.8.4" } diff --git a/playgrounds/sandbox/run.js b/playgrounds/sandbox/run.js index 4cb8314532..8fa2c2a2da 100644 --- a/playgrounds/sandbox/run.js +++ b/playgrounds/sandbox/run.js @@ -2,7 +2,7 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; import { fileURLToPath } from 'node:url'; import { parseArgs } from 'node:util'; -import glob from 'tiny-glob/sync.js'; +import { globSync } from 'tinyglobby'; import { compile, compileModule, parse, migrate } from 'svelte/compiler'; const argv = parseArgs({ options: { runes: { type: 'boolean' } }, args: process.argv.slice(2) }); @@ -35,8 +35,8 @@ function write(file, contents) { fs.writeFileSync(file, contents); } -const svelte_modules = glob('**/*.svelte', { cwd: `${cwd}/src` }); -const js_modules = glob('**/*.js', { cwd: `${cwd}/src` }); +const svelte_modules = globSync('**/*.svelte', { cwd: `${cwd}/src` }); +const js_modules = globSync('**/*.js', { cwd: `${cwd}/src` }); for (const generate of /** @type {const} */ (['client', 'server'])) { console.error(`\n--- generating ${generate} ---\n`); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f7700cd65f..2457a94041 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,10 +70,10 @@ importers: version: 1.0.6 acorn: specifier: ^8.12.1 - version: 8.12.1 + version: 8.14.0 acorn-typescript: specifier: ^1.4.13 - version: 1.4.13(acorn@8.12.1) + version: 1.4.13(acorn@8.14.0) aria-query: specifier: ^5.3.1 version: 5.3.1 @@ -97,7 +97,7 @@ importers: version: 3.0.0 magic-string: specifier: ^0.30.11 - version: 0.30.11 + version: 0.30.17 zimmerframe: specifier: ^1.1.2 version: 1.1.2 @@ -127,8 +127,8 @@ importers: specifier: ^20.11.5 version: 20.12.7 dts-buddy: - specifier: ^0.5.3 - version: 0.5.3(typescript@5.5.4) + specifier: ^0.5.5 + version: 0.5.5(typescript@5.5.4) esbuild: specifier: ^0.21.5 version: 0.21.5 @@ -138,9 +138,9 @@ importers: source-map: specifier: ^0.7.4 version: 0.7.4 - tiny-glob: - specifier: ^0.2.9 - version: 0.2.9 + tinyglobby: + specifier: ^0.2.12 + version: 0.2.12 typescript: specifier: ^5.5.4 version: 5.5.4 @@ -159,9 +159,9 @@ importers: svelte: specifier: workspace:* version: link:../../packages/svelte - tiny-glob: - specifier: ^0.2.9 - version: 0.2.9 + tinyglobby: + specifier: ^0.2.12 + version: 0.2.12 vite: specifier: ^5.4.14 version: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) @@ -402,22 +402,12 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.4.0': - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/eslint-utils@4.4.1': resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.11.0': - resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint-community/regexpp@4.12.1': resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -794,11 +784,6 @@ packages: peerDependencies: acorn: '>=8.9.0' - acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true - acorn@8.14.0: resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} @@ -877,10 +862,6 @@ packages: brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - braces@3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} - engines: {node: '>=8'} - braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -945,7 +926,7 @@ packages: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} cross-spawn@5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} @@ -970,15 +951,6 @@ packages: dataloader@1.4.0: resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} - debug@4.3.6: - resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.4.0: resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} engines: {node: '>=6.0'} @@ -1035,11 +1007,11 @@ packages: resolution: {integrity: sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ==} engines: {node: '>=12'} - dts-buddy@0.5.3: - resolution: {integrity: sha512-wS2DC5T+F6R+sG/YNlJ21yn8CKVhy1QQlpKA34G+uO4PUXkwz+JQWbGcIryUByxoJgbH98O0dTGzE2RqsRR3KA==} + dts-buddy@0.5.5: + resolution: {integrity: sha512-Mu5PJuP7C+EqZIwDtW/bG1tVli1UFhRIyW/dERBVBYk28OviTkribu9S2LpDQ0HF2MbkqnjQIkbbE6HnepdNTQ==} hasBin: true peerDependencies: - typescript: '>=5.0.4 <5.6' + typescript: '>=5.0.4 <5.8' eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -1208,8 +1180,8 @@ packages: fastq@1.16.0: resolution: {integrity: sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==} - fdir@6.3.0: - resolution: {integrity: sha512-QOnuT+BOtivR77wYvCWHfGt9s4Pz1VIMbD463vegT5MLqNXy8rYFT/lPVEqf/bhYeT6qmqrNHhsX+rWwe3rOCQ==} + fdir@6.4.3: + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -1220,10 +1192,6 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - fill-range@7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} - engines: {node: '>=8'} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1299,20 +1267,10 @@ packages: resolution: {integrity: sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==} engines: {node: '>=18'} - globals@15.9.0: - resolution: {integrity: sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA==} - engines: {node: '>=18'} - - globalyzer@0.1.0: - resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} - globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} - globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -1585,9 +1543,6 @@ packages: lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} - magic-string@0.30.11: - resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} - magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} @@ -1633,17 +1588,9 @@ packages: resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} engines: {node: '>=10'} - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - nanoid@3.3.8: resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1753,9 +1700,6 @@ packages: perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} - picocolors@1.1.0: - resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1813,10 +1757,6 @@ packages: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} - postcss@8.4.47: - resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.1: resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} engines: {node: ^10 || ^12 || >=14} @@ -1922,11 +1862,6 @@ packages: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} - semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} - engines: {node: '>=10'} - hasBin: true - semver@7.7.1: resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} engines: {node: '>=10'} @@ -1996,9 +1931,6 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - std-env@3.8.0: resolution: {integrity: sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==} @@ -2066,15 +1998,16 @@ packages: text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - tiny-glob@0.2.9: - resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} - tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyglobby@0.2.12: + resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} + engines: {node: '>=12.0.0'} + tinypool@1.0.2: resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -2375,7 +2308,7 @@ snapshots: outdent: 0.5.0 prettier: 2.8.8 resolve-from: 5.0.0 - semver: 7.6.3 + semver: 7.7.1 '@changesets/assemble-release-plan@6.0.4': dependencies: @@ -2384,7 +2317,7 @@ snapshots: '@changesets/should-skip-package': 0.1.1 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 - semver: 7.6.3 + semver: 7.7.1 '@changesets/changelog-git@0.2.0': dependencies: @@ -2417,9 +2350,9 @@ snapshots: outdent: 0.5.0 p-limit: 2.3.0 package-manager-detector: 0.2.0 - picocolors: 1.1.0 + picocolors: 1.1.1 resolve-from: 5.0.0 - semver: 7.6.3 + semver: 7.7.1 spawndamnit: 2.0.0 term-size: 2.2.1 @@ -2441,8 +2374,8 @@ snapshots: dependencies: '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 - picocolors: 1.1.0 - semver: 7.6.3 + picocolors: 1.1.1 + semver: 7.7.1 '@changesets/get-github-info@0.5.2': dependencies: @@ -2472,7 +2405,7 @@ snapshots: '@changesets/logger@0.1.1': dependencies: - picocolors: 1.1.0 + picocolors: 1.1.1 '@changesets/parse@0.4.0': dependencies: @@ -2494,7 +2427,7 @@ snapshots: '@changesets/types': 6.0.0 fs-extra: 7.0.1 p-filter: 2.1.0 - picocolors: 1.1.0 + picocolors: 1.1.1 '@changesets/should-skip-package@0.1.1': dependencies: @@ -2581,18 +2514,11 @@ snapshots: '@esbuild/win32-x64@0.21.5': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@9.9.1)': - dependencies: - eslint: 9.9.1 - eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.4.1(eslint@9.9.1)': dependencies: eslint: 9.9.1 eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.11.0': {} - '@eslint-community/regexpp@4.12.1': {} '@eslint/config-array@0.18.0': @@ -2700,9 +2626,9 @@ snapshots: '@rollup/pluginutils': 5.1.0(rollup@4.22.4) commondir: 1.0.1 estree-walker: 2.0.2 - fdir: 6.3.0(picomatch@4.0.2) + fdir: 6.4.3(picomatch@4.0.2) is-reference: 1.2.1 - magic-string: 0.30.11 + magic-string: 0.30.17 picomatch: 4.0.2 optionalDependencies: rollup: 4.22.4 @@ -2801,14 +2727,14 @@ snapshots: eslint-config-prettier: 9.1.0(eslint@9.9.1) eslint-plugin-n: 17.9.0(eslint@9.9.1) eslint-plugin-svelte: 2.38.0(eslint@9.9.1)(svelte@packages+svelte) - globals: 15.9.0 + globals: 15.14.0 typescript: 5.5.4 typescript-eslint: 8.2.0(eslint@9.9.1)(typescript@5.5.4) '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) - debug: 4.3.6 + debug: 4.4.0 svelte: link:packages/svelte vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: @@ -2817,10 +2743,10 @@ snapshots: '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) - debug: 4.3.6 + debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 - magic-string: 0.30.11 + magic-string: 0.30.17 svelte: link:packages/svelte vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vitefu: 0.2.5(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) @@ -2859,7 +2785,7 @@ snapshots: '@typescript-eslint/eslint-plugin@8.2.0(@typescript-eslint/parser@8.2.0(eslint@9.9.1)(typescript@5.5.4))(eslint@9.9.1)(typescript@5.5.4)': dependencies: - '@eslint-community/regexpp': 4.11.0 + '@eslint-community/regexpp': 4.12.1 '@typescript-eslint/parser': 8.2.0(eslint@9.9.1)(typescript@5.5.4) '@typescript-eslint/scope-manager': 8.2.0 '@typescript-eslint/type-utils': 8.2.0(eslint@9.9.1)(typescript@5.5.4) @@ -2915,7 +2841,7 @@ snapshots: globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.6.3 + semver: 7.7.1 ts-api-utils: 1.3.0(typescript@5.5.4) optionalDependencies: typescript: 5.5.4 @@ -2924,7 +2850,7 @@ snapshots: '@typescript-eslint/utils@8.2.0(eslint@9.9.1)(typescript@5.5.4)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1) + '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) '@typescript-eslint/scope-manager': 8.2.0 '@typescript-eslint/types': 8.2.0 '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) @@ -2942,14 +2868,14 @@ snapshots: dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 - debug: 4.3.6 + debug: 4.4.0 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 istanbul-reports: 3.1.7 - magic-string: 0.30.11 + magic-string: 0.30.17 magicast: 0.3.4 - std-env: 3.7.0 + std-env: 3.8.0 test-exclude: 7.0.1 tinyrainbow: 1.2.0 vitest: 2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) @@ -2996,19 +2922,13 @@ snapshots: loupe: 3.1.3 tinyrainbow: 1.2.0 - acorn-jsx@5.3.2(acorn@8.12.1): - dependencies: - acorn: 8.12.1 - acorn-jsx@5.3.2(acorn@8.14.0): dependencies: acorn: 8.14.0 - acorn-typescript@1.4.13(acorn@8.12.1): + acorn-typescript@1.4.13(acorn@8.14.0): dependencies: - acorn: 8.12.1 - - acorn@8.12.1: {} + acorn: 8.14.0 acorn@8.14.0: {} @@ -3077,14 +2997,9 @@ snapshots: dependencies: balanced-match: 1.0.2 - braces@3.0.2: - dependencies: - fill-range: 7.0.1 - braces@3.0.3: dependencies: fill-range: 7.1.1 - optional: true buffer-from@1.1.2: {} @@ -3171,10 +3086,6 @@ snapshots: dataloader@1.4.0: {} - debug@4.3.6: - dependencies: - ms: 2.1.2 - debug@4.4.0: dependencies: ms: 2.1.3 @@ -3209,16 +3120,15 @@ snapshots: dotenv@16.3.2: {} - dts-buddy@0.5.3(typescript@5.5.4): + dts-buddy@0.5.5(typescript@5.5.4): dependencies: '@jridgewell/source-map': 0.3.6 '@jridgewell/sourcemap-codec': 1.5.0 - globrex: 0.1.2 kleur: 4.1.5 locate-character: 3.0.0 - magic-string: 0.30.11 + magic-string: 0.30.17 sade: 1.8.1 - tiny-glob: 0.2.9 + tinyglobby: 0.2.12 ts-api-utils: 1.3.0(typescript@5.5.4) typescript: 5.5.4 @@ -3339,8 +3249,8 @@ snapshots: eslint@9.9.1: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@9.9.1) - '@eslint-community/regexpp': 4.11.0 + '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) + '@eslint-community/regexpp': 4.12.1 '@eslint/config-array': 0.18.0 '@eslint/eslintrc': 3.1.0 '@eslint/js': 9.9.1 @@ -3350,7 +3260,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.6 + debug: 4.4.0 escape-string-regexp: 4.0.0 eslint-scope: 8.0.2 eslint-visitor-keys: 4.0.0 @@ -3380,8 +3290,8 @@ snapshots: espree@10.1.0: dependencies: - acorn: 8.12.1 - acorn-jsx: 5.3.2(acorn@8.12.1) + acorn: 8.14.0 + acorn-jsx: 5.3.2(acorn@8.14.0) eslint-visitor-keys: 4.0.0 espree@9.6.1: @@ -3442,7 +3352,7 @@ snapshots: dependencies: reusify: 1.0.4 - fdir@6.3.0(picomatch@4.0.2): + fdir@6.4.3(picomatch@4.0.2): optionalDependencies: picomatch: 4.0.2 @@ -3450,14 +3360,9 @@ snapshots: dependencies: flat-cache: 4.0.1 - fill-range@7.0.1: - dependencies: - to-regex-range: 5.0.1 - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - optional: true find-up@4.1.0: dependencies: @@ -3538,10 +3443,6 @@ snapshots: globals@15.14.0: {} - globals@15.9.0: {} - - globalyzer@0.1.0: {} - globby@11.1.0: dependencies: array-union: 2.1.0 @@ -3551,8 +3452,6 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 - globrex@0.1.2: {} - graceful-fs@4.2.11: {} graphemer@1.4.0: {} @@ -3816,10 +3715,6 @@ snapshots: pseudomap: 1.0.2 yallist: 2.1.2 - magic-string@0.30.11: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -3832,13 +3727,13 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.6.3 + semver: 7.7.1 merge2@1.4.1: {} micromatch@4.0.5: dependencies: - braces: 3.0.2 + braces: 3.0.3 picomatch: 2.3.1 mime-db@1.52.0: {} @@ -3861,12 +3756,8 @@ snapshots: mrmime@2.0.0: {} - ms@2.1.2: {} - ms@2.1.3: {} - nanoid@3.3.7: {} - nanoid@3.3.8: {} natural-compare@1.4.0: {} @@ -3955,8 +3846,6 @@ snapshots: perfect-debounce@1.0.0: {} - picocolors@1.1.0: {} - picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -3998,12 +3887,6 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss@8.4.47: - dependencies: - nanoid: 3.3.7 - picocolors: 1.1.0 - source-map-js: 1.2.1 - postcss@8.5.1: dependencies: nanoid: 3.3.8 @@ -4110,8 +3993,6 @@ snapshots: dependencies: xmlchars: 2.2.0 - semver@7.6.3: {} - semver@7.7.1: {} serialize-javascript@6.0.2: @@ -4166,8 +4047,6 @@ snapshots: stackback@0.0.2: {} - std-env@3.7.0: {} - std-env@3.8.0: {} string-width@4.2.3: @@ -4219,7 +4098,7 @@ snapshots: terser@5.27.0: dependencies: '@jridgewell/source-map': 0.3.6 - acorn: 8.12.1 + acorn: 8.14.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -4231,15 +4110,15 @@ snapshots: text-table@0.2.0: {} - tiny-glob@0.2.9: - dependencies: - globalyzer: 0.1.0 - globrex: 0.1.2 - tinybench@2.9.0: {} tinyexec@0.3.2: {} + tinyglobby@0.2.12: + dependencies: + fdir: 6.4.3(picomatch@4.0.2) + picomatch: 4.0.2 + tinypool@1.0.2: {} tinyrainbow@1.2.0: {} @@ -4335,12 +4214,12 @@ snapshots: dependencies: '@antfu/utils': 0.7.8 '@rollup/pluginutils': 5.1.0(rollup@4.22.4) - debug: 4.3.6 + debug: 4.4.0 error-stack-parser-es: 0.1.1 fs-extra: 11.2.0 open: 10.1.0 perfect-debounce: 1.0.0 - picocolors: 1.1.0 + picocolors: 1.1.1 sirv: 2.0.4 vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: @@ -4350,7 +4229,7 @@ snapshots: vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: esbuild: 0.21.5 - postcss: 8.4.47 + postcss: 8.5.1 rollup: 4.22.4 optionalDependencies: '@types/node': 20.12.7 From cf56973bf0f8b2c0e9c87a1ae5393edd42911b90 Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Wed, 26 Feb 2025 11:33:44 +0100 Subject: [PATCH 0210/1429] fix: class:directive not working with $$restProps (#15389) * add spread to test * fix #15386 * do no set cssHash on non-scoped element * changeset --- .changeset/lemon-cougars-buy.md | 6 ++ .../client/visitors/shared/element.js | 4 +- .../server/visitors/shared/element.js | 7 +- .../client/dom/elements/attributes.js | 13 ++- .../src/internal/client/dom/elements/class.js | 8 +- .../class-directive-mutations/_config.js | 93 ++++++++++++++++++- .../class-directive-mutations/main.svelte | 7 +- 7 files changed, 119 insertions(+), 19 deletions(-) create mode 100644 .changeset/lemon-cougars-buy.md diff --git a/.changeset/lemon-cougars-buy.md b/.changeset/lemon-cougars-buy.md new file mode 100644 index 0000000000..382b3cdeb2 --- /dev/null +++ b/.changeset/lemon-cougars-buy.md @@ -0,0 +1,6 @@ +--- +'svelte': patch +--- + +fix: class:directive not working with $$restProps #15386 +fix: spread add an useless cssHash on non-scoped element diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index fc5fd2cc4c..81a4b45288 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -88,7 +88,9 @@ export function build_set_attributes( element_id, is_dynamic ? attributes_id : b.literal(null), b.object(values), - context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash), + element.metadata.scoped && + context.state.analysis.css.hash !== '' && + b.literal(context.state.analysis.css.hash), preserve_attribute_case, is_custom_element, is_ignored(element, 'hydration_attribute_changed') && b.true diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js index 57101af4b8..281d8f0617 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js @@ -368,9 +368,10 @@ function build_element_spread_attributes( }) ); - const css_hash = context.state.analysis.css.hash - ? b.literal(context.state.analysis.css.hash) - : b.null; + const css_hash = + element.metadata.scoped && context.state.analysis.css.hash + ? b.literal(context.state.analysis.css.hash) + : b.null; const args = [object, css_hash, classes, styles, flags ? b.literal(flags) : undefined]; context.state.template.push(b.call('$.spread_attributes', ...args)); diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 151024e85c..dd408dcf87 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -326,8 +326,16 @@ export function set_attributes( continue; } + if (key === 'class') { + var is_html = element.namespaceURI === 'http://www.w3.org/1999/xhtml'; + set_class(element, is_html, value, css_hash, prev?.[CLASS], next[CLASS]); + current[key] = value; + current[CLASS] = next[CLASS]; + continue; + } + var prev_value = current[key]; - if (value === prev_value && key !== 'class') continue; + if (value === prev_value) continue; current[key] = value; @@ -377,9 +385,6 @@ export function set_attributes( // @ts-ignore element[`__${event_name}`] = undefined; } - } else if (key === 'class') { - var is_html = element.namespaceURI === 'http://www.w3.org/1999/xhtml'; - set_class(element, is_html, value, css_hash, prev?.[CLASS], next[CLASS]); } else if (key === 'style' && value != null) { element.style.cssText = value + ''; } else if (key === 'autofocus') { diff --git a/packages/svelte/src/internal/client/dom/elements/class.js b/packages/svelte/src/internal/client/dom/elements/class.js index 3308709a24..7027c84f62 100644 --- a/packages/svelte/src/internal/client/dom/elements/class.js +++ b/packages/svelte/src/internal/client/dom/elements/class.js @@ -6,8 +6,8 @@ import { hydrating } from '../hydration.js'; * @param {boolean | number} is_html * @param {string | null} value * @param {string} [hash] - * @param {Record<string, boolean>} [prev_classes] - * @param {Record<string, boolean>} [next_classes] + * @param {Record<string, any>} [prev_classes] + * @param {Record<string, any>} [next_classes] * @returns {Record<string, boolean> | undefined} */ export function set_class(dom, is_html, value, hash, prev_classes, next_classes) { @@ -34,12 +34,10 @@ export function set_class(dom, is_html, value, hash, prev_classes, next_classes) // @ts-expect-error need to add __className to patched prototype dom.__className = value; } else if (next_classes) { - prev_classes ??= {}; - for (var key in next_classes) { var is_present = !!next_classes[key]; - if (is_present !== !!prev_classes[key]) { + if (prev_classes == null || is_present !== !!prev_classes[key]) { dom.classList.toggle(key, is_present); } } diff --git a/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/_config.js b/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/_config.js index dd1bc6ac1a..076efee994 100644 --- a/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/_config.js @@ -1,4 +1,4 @@ -import { flushSync } from 'svelte'; +import { flushSync, tick } from 'svelte'; import { test } from '../../test'; // This test counts mutations on hydration @@ -16,7 +16,12 @@ export default test({ html: ` <main id="main" class="browser"> - <div class="custom svelte-1cjqok6 foo bar"></div> + <div class="custom svelte-1cjqok6 foo bar" title="a title"></div> + <span class="svelte-1cjqok6 foo bar"></span> + <b class="custom foo bar"></b> + <i class="foo bar"></i> + + <div class="custom svelte-1cjqok6 foo bar" title="a title"></div> <span class="svelte-1cjqok6 foo bar"></span> <b class="custom foo bar"></b> <i class="foo bar"></i> @@ -25,19 +30,97 @@ export default test({ ssrHtml: ` <main id="main"> - <div class="custom svelte-1cjqok6 foo bar"></div> + <div class="custom svelte-1cjqok6 foo bar" title="a title"></div> + <span class="svelte-1cjqok6 foo bar"></span> + <b class="custom foo bar"></b> + <i class="foo bar"></i> + + <div class="custom svelte-1cjqok6 foo bar" title="a title"></div> <span class="svelte-1cjqok6 foo bar"></span> <b class="custom foo bar"></b> <i class="foo bar"></i> </main> `, - async test({ assert, component, instance }) { + async test({ target, assert, component, instance }) { flushSync(); + tick(); assert.deepEqual(instance.get_and_clear_mutations(), ['MAIN']); component.foo = false; flushSync(); - assert.deepEqual(instance.get_and_clear_mutations(), ['DIV', 'SPAN', 'B', 'I']); + tick(); + assert.deepEqual( + instance.get_and_clear_mutations(), + ['DIV', 'SPAN', 'B', 'I', 'DIV', 'SPAN', 'B', 'I'], + 'first mutation' + ); + + assert.htmlEqual( + target.innerHTML, + ` + <main id="main" class="browser"> + <div class="custom svelte-1cjqok6 bar" title="a title"></div> + <span class="svelte-1cjqok6 bar"></span> + <b class="custom bar"></b> + <i class="bar"></i> + + <div class="custom svelte-1cjqok6 bar" title="a title"></div> + <span class="svelte-1cjqok6 bar"></span> + <b class="custom bar"></b> + <i class="bar"></i> + </main> + ` + ); + + component.foo = true; + flushSync(); + assert.deepEqual( + instance.get_and_clear_mutations(), + ['DIV', 'SPAN', 'B', 'I', 'DIV', 'SPAN', 'B', 'I'], + 'second mutation' + ); + + assert.htmlEqual( + target.innerHTML, + ` + <main id="main" class="browser"> + <div class="custom svelte-1cjqok6 bar foo" title="a title"></div> + <span class="svelte-1cjqok6 bar foo"></span> + <b class="custom bar foo"></b> + <i class="bar foo"></i> + + <div class="custom svelte-1cjqok6 bar foo" title="a title"></div> + <span class="svelte-1cjqok6 bar foo"></span> + <b class="custom bar foo"></b> + <i class="bar foo"></i> + </main> + ` + ); + + component.classname = 'another'; + flushSync(); + assert.deepEqual( + instance.get_and_clear_mutations(), + ['DIV', 'B', 'DIV', 'B'], + 'class mutation' + ); + + assert.htmlEqual( + target.innerHTML, + ` + <main id="main" class="browser"> + <div class="another svelte-1cjqok6 foo bar" title="a title"></div> + <span class="svelte-1cjqok6 bar foo"></span> + <b class="another foo bar"></b> + <i class="bar foo"></i> + + <div class="another svelte-1cjqok6 foo bar" title="a title"></div> + <span class="svelte-1cjqok6 bar foo"></span> + <b class="another foo bar"></b> + <i class="bar foo"></i> + </main> + ` + ); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/main.svelte index 825362dcaf..d748988e21 100644 --- a/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/class-directive-mutations/main.svelte @@ -33,10 +33,15 @@ </script> <main id="main" class:browser> - <div class={classname} class:foo class:bar></div> + <div class={classname} title="a title" class:foo class:bar></div> <span class:foo class:bar></span> <b class={classname} class:foo class:bar></b> <i class:foo class:bar></i> + + <div {...{class:classname, title:"a title"}} class:foo class:bar></div> + <span {...{}} class:foo class:bar></span> + <b {...{class:classname}} class:foo class:bar></b> + <i {...{}} class:foo class:bar></i> </main> <style> From 8fb2fb70e1d89f3bb43962a790d6704a2a7002a0 Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Thu, 27 Feb 2025 10:38:49 +0100 Subject: [PATCH 0211/1429] fix : bug "$0 is not defined" with svelte:element and a single class with a function call (#15396) * add spread to test * fix #15386 * fix #15392 * test * changeset * use is_text_attribute --- .changeset/two-dragons-camp.md | 5 ++ .../client/visitors/SvelteElement.js | 5 +- .../svelte-element-css-hash/_config.js | 60 +++++++++++++++++++ .../svelte-element-css-hash/main.svelte | 20 +++++++ 4 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 .changeset/two-dragons-camp.md create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-element-css-hash/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-element-css-hash/main.svelte diff --git a/.changeset/two-dragons-camp.md b/.changeset/two-dragons-camp.md new file mode 100644 index 0000000000..cb0635186f --- /dev/null +++ b/.changeset/two-dragons-camp.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix : bug "$0 is not defined" on svelte:element with a function call on class diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js index 7149f8d0e4..ac284c818d 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js @@ -83,9 +83,10 @@ export function SvelteElement(node, context) { if ( attributes.length === 1 && attributes[0].type === 'Attribute' && - attributes[0].name.toLowerCase() === 'class' + attributes[0].name.toLowerCase() === 'class' && + is_text_attribute(attributes[0]) ) { - // special case when there only a class attribute + // special case when there only a class attribute, without call expression let { value, has_state } = build_attribute_value( attributes[0].value, context, diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-element-css-hash/_config.js b/packages/svelte/tests/runtime-runes/samples/svelte-element-css-hash/_config.js new file mode 100644 index 0000000000..c7a6213d82 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-element-css-hash/_config.js @@ -0,0 +1,60 @@ +import { flushSync } from 'svelte'; +import { ok, test } from '../../test'; + +export default test({ + html: ` + <div></div> + <div class="red svelte-p153w3"></div> + <div></div> + <div class="red svelte-p153w3"></div> + <div class="blue svelte-p153w3"></div> + <div class="blue svelte-p153w3"></div> + `, + + async test({ assert, target, component }) { + component.active = true; + flushSync(); + + assert.htmlEqual( + target.innerHTML, + ` + <div></div> + <div class="red svelte-p153w3"></div> + <div class="active"></div> + <div class="red svelte-p153w3 active"></div> + <div class="blue svelte-p153w3"></div> + <div class="blue svelte-p153w3 active"></div> + ` + ); + + component.tag = 'span'; + flushSync(); + + assert.htmlEqual( + target.innerHTML, + ` + <span></span> + <span class="red svelte-p153w3"></span> + <span class="active"></span> + <span class="red svelte-p153w3 active"></span> + <span class="blue svelte-p153w3"></span> + <span class="blue svelte-p153w3 active"></span> + ` + ); + + component.active = false; + flushSync(); + + assert.htmlEqual( + target.innerHTML, + ` + <span></span> + <span class="red svelte-p153w3"></span> + <span class=""></span> + <span class="red svelte-p153w3"></span> + <span class="blue svelte-p153w3"></span> + <span class="blue svelte-p153w3"></span> + ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-element-css-hash/main.svelte b/packages/svelte/tests/runtime-runes/samples/svelte-element-css-hash/main.svelte new file mode 100644 index 0000000000..709f7b4fa1 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-element-css-hash/main.svelte @@ -0,0 +1,20 @@ +<script> + let { tag = "div", active = false } = $props(); + + function cn(classname) { + return classname; + } +</script> + +<svelte:element this={tag}></svelte:element> +<svelte:element this={tag} class="red"></svelte:element> +<svelte:element this={tag} class:active={active}></svelte:element> +<svelte:element this={tag} class="red" class:active={active}></svelte:element> +<svelte:element this={tag} class={cn("blue")}></svelte:element> +<svelte:element this={tag} class={cn("blue")} class:active={active}></svelte:element> + +<style> + .red { + color: red; + } +</style> From be82332ac851dc3491a72dfb7128588f98bea812 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Thu, 27 Feb 2025 04:42:51 -0500 Subject: [PATCH 0212/1429] chore: simplify `process_effects` (#15397) --- .../svelte/src/internal/client/runtime.js | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index fe4104c108..486c819f36 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -748,68 +748,57 @@ export function schedule_effect(signal) { * bitwise flag passed in only. The collected effects array will be populated with all the user * effects to be flushed. * - * @param {Effect} effect + * @param {Effect} root * @returns {Effect[]} */ -function process_effects(effect) { +function process_effects(root) { /** @type {Effect[]} */ var effects = []; - var current_effect = effect.first; + var effect = root.first; - main_loop: while (current_effect !== null) { - var flags = current_effect.f; + while (effect !== null) { + var flags = effect.f; var is_branch = (flags & BRANCH_EFFECT) !== 0; var is_skippable_branch = is_branch && (flags & CLEAN) !== 0; - var sibling = current_effect.next; if (!is_skippable_branch && (flags & INERT) === 0) { if ((flags & EFFECT) !== 0) { - effects.push(current_effect); + effects.push(effect); } else if (is_branch) { - current_effect.f ^= CLEAN; + effect.f ^= CLEAN; } else { // Ensure we set the effect to be the active reaction // to ensure that unowned deriveds are correctly tracked // because we're flushing the current effect var previous_active_reaction = active_reaction; try { - active_reaction = current_effect; - if (check_dirtiness(current_effect)) { - update_effect(current_effect); + active_reaction = effect; + if (check_dirtiness(effect)) { + update_effect(effect); } } catch (error) { - handle_error(error, current_effect, null, current_effect.ctx); + handle_error(error, effect, null, effect.ctx); } finally { active_reaction = previous_active_reaction; } } - var child = current_effect.first; + var child = effect.first; if (child !== null) { - current_effect = child; + effect = child; continue; } } - if (sibling === null) { - let parent = current_effect.parent; + var parent = effect.parent; + effect = effect.next; - while (parent !== null) { - if (effect === parent) { - break main_loop; - } - var parent_sibling = parent.next; - if (parent_sibling !== null) { - current_effect = parent_sibling; - continue main_loop; - } - parent = parent.parent; - } + while (effect === null && parent !== null) { + effect = parent.next; + parent = parent.parent; } - - current_effect = sibling; } return effects; From fe343e9c50ee14fb189356fdef82bb234f97ffc5 Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Thu, 27 Feb 2025 10:57:43 +0100 Subject: [PATCH 0213/1429] fix: catch error on @const tag in svelte:boundary in DEV mode (#15369) * fix: catch error on @const tag in svelte:boundary * changeset * edit changeset * test * fix * rollback * simpler way to do that * rollback * roolback again --------- Co-authored-by: paoloricciuti <ricciutipaolo@gmail.com> --- .changeset/sixty-cats-search.md | 5 ++ .../client/visitors/SvelteBoundary.js | 23 ++++++++- .../svelte-boundary-dev-const/_config.js | 33 +++++++++++++ .../svelte-boundary-dev-const/main.svelte | 49 +++++++++++++++++++ 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 .changeset/sixty-cats-search.md create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-boundary-dev-const/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-boundary-dev-const/main.svelte diff --git a/.changeset/sixty-cats-search.md b/.changeset/sixty-cats-search.md new file mode 100644 index 0000000000..6ffd007494 --- /dev/null +++ b/.changeset/sixty-cats-search.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: catch error on @const tag in svelte:boundary in DEV mode diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js index 6da650a591..9228df9703 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js @@ -1,6 +1,7 @@ /** @import { BlockStatement, Statement, Expression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ +import { dev } from '../../../../state.js'; import * as b from '../../../../utils/builders.js'; /** @@ -35,6 +36,9 @@ export function SvelteBoundary(node, context) { /** @type {Statement[]} */ const external_statements = []; + /** @type {Statement[]} */ + const internal_statements = []; + const snippets_visits = []; // Capture the `failed` implicit snippet prop @@ -53,7 +57,20 @@ export function SvelteBoundary(node, context) { /** @type {Statement[]} */ const init = []; context.visit(child, { ...context.state, init }); - external_statements.push(...init); + + if (dev) { + // In dev we must separate the declarations from the code + // that eagerly evaluate the expression... + for (const statement of init) { + if (statement.type === 'VariableDeclaration') { + external_statements.push(statement); + } else { + internal_statements.push(statement); + } + } + } else { + external_statements.push(...init); + } } else { nodes.push(child); } @@ -63,6 +80,10 @@ export function SvelteBoundary(node, context) { const block = /** @type {BlockStatement} */ (context.visit({ ...node.fragment, nodes })); + if (dev && internal_statements.length) { + block.body.unshift(...internal_statements); + } + const boundary = b.stmt( b.call('$.boundary', context.state.node, props, b.arrow([b.id('$$anchor')], block)) ); diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-boundary-dev-const/_config.js b/packages/svelte/tests/runtime-runes/samples/svelte-boundary-dev-const/_config.js new file mode 100644 index 0000000000..3c0195ce34 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-boundary-dev-const/_config.js @@ -0,0 +1,33 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +// https://github.com/sveltejs/svelte/issues/15368 +export default test({ + compileOptions: { + dev: true + }, + + mode: ['client'], + + html: ` + <p>BOOM</p> + <p>BOOM</p> + <div>OK</div> + <div>OK</div> + `, + + async test({ target, assert, component }) { + component.ok = false; + flushSync(); + + assert.htmlEqual( + target.innerHTML, + ` + <p>BOOM</p> + <p>BOOM</p> + <p>BOOM</p> + <p>BOOM</p> + ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-boundary-dev-const/main.svelte b/packages/svelte/tests/runtime-runes/samples/svelte-boundary-dev-const/main.svelte new file mode 100644 index 0000000000..30e074c762 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-boundary-dev-const/main.svelte @@ -0,0 +1,49 @@ +<script> + let { ok = true } = $props(); + + function throwError() { + throw new Error(); + } + + function throwErrorOnUpdate() { + if (ok) { + return "OK"; + } else { + throwError(); + } + } +</script> + +<svelte:boundary> + <div>{throwError()}</div> + + {#snippet failed()} + <p>BOOM</p> + {/snippet} +</svelte:boundary> + +<svelte:boundary> + {@const result = throwError()} + <div>{result}</div> + + {#snippet failed()} + <p>BOOM</p> + {/snippet} +</svelte:boundary> + +<svelte:boundary> + <div>{throwErrorOnUpdate()}</div> + + {#snippet failed()} + <p>BOOM</p> + {/snippet} +</svelte:boundary> + +<svelte:boundary> + {@const result = throwErrorOnUpdate()} + <div>{result}</div> + + {#snippet failed()} + <p>BOOM</p> + {/snippet} +</svelte:boundary> From e4987d2a9f36b33f3934725723f9c4cc998ef278 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Feb 2025 09:58:56 +0000 Subject: [PATCH 0214/1429] Version Packages (#15383) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/fast-pants-decide.md | 5 ----- .changeset/lemon-cougars-buy.md | 6 ------ .changeset/sixty-cats-search.md | 5 ----- .changeset/spotty-drinks-tan.md | 5 ----- .changeset/two-dragons-camp.md | 5 ----- packages/svelte/CHANGELOG.md | 15 +++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 8 files changed, 17 insertions(+), 28 deletions(-) delete mode 100644 .changeset/fast-pants-decide.md delete mode 100644 .changeset/lemon-cougars-buy.md delete mode 100644 .changeset/sixty-cats-search.md delete mode 100644 .changeset/spotty-drinks-tan.md delete mode 100644 .changeset/two-dragons-camp.md diff --git a/.changeset/fast-pants-decide.md b/.changeset/fast-pants-decide.md deleted file mode 100644 index 396b80c0e0..0000000000 --- a/.changeset/fast-pants-decide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: allow double hyphen css selector names diff --git a/.changeset/lemon-cougars-buy.md b/.changeset/lemon-cougars-buy.md deleted file mode 100644 index 382b3cdeb2..0000000000 --- a/.changeset/lemon-cougars-buy.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'svelte': patch ---- - -fix: class:directive not working with $$restProps #15386 -fix: spread add an useless cssHash on non-scoped element diff --git a/.changeset/sixty-cats-search.md b/.changeset/sixty-cats-search.md deleted file mode 100644 index 6ffd007494..0000000000 --- a/.changeset/sixty-cats-search.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: catch error on @const tag in svelte:boundary in DEV mode diff --git a/.changeset/spotty-drinks-tan.md b/.changeset/spotty-drinks-tan.md deleted file mode 100644 index 2150c8cffd..0000000000 --- a/.changeset/spotty-drinks-tan.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: allow for duplicate `var` declarations diff --git a/.changeset/two-dragons-camp.md b/.changeset/two-dragons-camp.md deleted file mode 100644 index cb0635186f..0000000000 --- a/.changeset/two-dragons-camp.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix : bug "$0 is not defined" on svelte:element with a function call on class diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 907c5a3534..cb33bc0d39 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,20 @@ # svelte +## 5.20.5 + +### Patch Changes + +- fix: allow double hyphen css selector names ([#15384](https://github.com/sveltejs/svelte/pull/15384)) + +- fix: class:directive not working with $restProps #15386 ([#15389](https://github.com/sveltejs/svelte/pull/15389)) + fix: spread add an useless cssHash on non-scoped element + +- fix: catch error on @const tag in svelte:boundary in DEV mode ([#15369](https://github.com/sveltejs/svelte/pull/15369)) + +- fix: allow for duplicate `var` declarations ([#15382](https://github.com/sveltejs/svelte/pull/15382)) + +- fix : bug "$0 is not defined" on svelte:element with a function call on class ([#15396](https://github.com/sveltejs/svelte/pull/15396)) + ## 5.20.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index dd472e4413..bff6b5adc0 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.20.4", + "version": "5.20.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index e893def326..b77e0ea49e 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.20.4'; +export const VERSION = '5.20.5'; export const PUBLIC_VERSION = '5'; From 474c588067fa75f6785d9cfe5ac15ff8bbc829fe Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Thu, 27 Feb 2025 17:42:36 +0100 Subject: [PATCH 0215/1429] fix: disallow `bind:group` to snippet parameters (#15401) --- .changeset/hip-oranges-hang.md | 5 +++++ .../docs/98-reference/.generated/compile-errors.md | 6 ++++++ .../svelte/messages/compile-errors/template.md | 4 ++++ packages/svelte/src/compiler/errors.js | 9 +++++++++ .../phases/2-analyze/visitors/BindDirective.js | 4 ++++ .../bind-group-snippet-parameter/errors.json | 14 ++++++++++++++ .../bind-group-snippet-parameter/input.svelte | 3 +++ 7 files changed, 45 insertions(+) create mode 100644 .changeset/hip-oranges-hang.md create mode 100644 packages/svelte/tests/validator/samples/bind-group-snippet-parameter/errors.json create mode 100644 packages/svelte/tests/validator/samples/bind-group-snippet-parameter/input.svelte diff --git a/.changeset/hip-oranges-hang.md b/.changeset/hip-oranges-hang.md new file mode 100644 index 0000000000..addbeafa9c --- /dev/null +++ b/.changeset/hip-oranges-hang.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: disallow `bind:group` to snippet parameters diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index a4ecbb31d5..ea116014e7 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -84,6 +84,12 @@ Attribute values containing `{...}` must be enclosed in quote marks, unless the `bind:group` can only bind to an Identifier or MemberExpression ``` +### bind_group_invalid_snippet_parameter + +``` +Cannot `bind:group` to a snippet parameter +``` + ### bind_invalid_expression ``` diff --git a/packages/svelte/messages/compile-errors/template.md b/packages/svelte/messages/compile-errors/template.md index d061416dbd..0569f63ad3 100644 --- a/packages/svelte/messages/compile-errors/template.md +++ b/packages/svelte/messages/compile-errors/template.md @@ -54,6 +54,10 @@ > `bind:group` can only bind to an Identifier or MemberExpression +## bind_group_invalid_snippet_parameter + +> Cannot `bind:group` to a snippet parameter + ## bind_invalid_expression > Can only bind to an Identifier or MemberExpression or a `{get, set}` pair diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index 93eeee539c..677b99fcff 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -752,6 +752,15 @@ export function bind_group_invalid_expression(node) { e(node, 'bind_group_invalid_expression', `\`bind:group\` can only bind to an Identifier or MemberExpression\nhttps://svelte.dev/e/bind_group_invalid_expression`); } +/** + * Cannot `bind:group` to a snippet parameter + * @param {null | number | NodeLike} node + * @returns {never} + */ +export function bind_group_invalid_snippet_parameter(node) { + e(node, 'bind_group_invalid_snippet_parameter', `Cannot \`bind:group\` to a snippet parameter\nhttps://svelte.dev/e/bind_group_invalid_snippet_parameter`); +} + /** * Can only bind to an Identifier or MemberExpression or a `{get, set}` pair * @param {null | number | NodeLike} node diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js index 509fecf301..18ea79262b 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/BindDirective.js @@ -191,6 +191,10 @@ export function BindDirective(node, context) { throw new Error('Cannot find declaration for bind:group'); } + if (binding.kind === 'snippet') { + e.bind_group_invalid_snippet_parameter(node); + } + // Traverse the path upwards and find all EachBlocks who are (indirectly) contributing to bind:group, // i.e. one of their declarations is referenced in the binding. This allows group bindings to work // correctly when referencing a variable declared in an EachBlock by using the index of the each block diff --git a/packages/svelte/tests/validator/samples/bind-group-snippet-parameter/errors.json b/packages/svelte/tests/validator/samples/bind-group-snippet-parameter/errors.json new file mode 100644 index 0000000000..15e762419f --- /dev/null +++ b/packages/svelte/tests/validator/samples/bind-group-snippet-parameter/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "bind_group_invalid_snippet_parameter", + "end": { + "column": 44, + "line": 2 + }, + "message": "Cannot `bind:group` to a snippet parameter", + "start": { + "column": 21, + "line": 2 + } + } +] diff --git a/packages/svelte/tests/validator/samples/bind-group-snippet-parameter/input.svelte b/packages/svelte/tests/validator/samples/bind-group-snippet-parameter/input.svelte new file mode 100644 index 0000000000..368484788a --- /dev/null +++ b/packages/svelte/tests/validator/samples/bind-group-snippet-parameter/input.svelte @@ -0,0 +1,3 @@ +{#snippet test(group)} + <input type="radio" bind:group={group.name} /> +{/snippet} \ No newline at end of file From 2032049e47b97098c874195d837a16acea5bb42e Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Mon, 3 Mar 2025 17:38:41 +0100 Subject: [PATCH 0216/1429] chore: Reduce hydration comment for {:else if} (#15250) * rewrite else/if hydration * fix: bad index * reduce args on if_block() * restore the block * don't use isNan() * changeset --- .changeset/tough-steaks-travel.md | 5 +++ .../3-transform/client/visitors/Fragment.js | 4 +- .../3-transform/client/visitors/IfBlock.js | 25 +++++------ .../3-transform/server/visitors/IfBlock.js | 30 +++++++++----- .../src/internal/client/dom/blocks/if.js | 41 ++++++++++++++----- 5 files changed, 71 insertions(+), 34 deletions(-) create mode 100644 .changeset/tough-steaks-travel.md diff --git a/.changeset/tough-steaks-travel.md b/.changeset/tough-steaks-travel.md new file mode 100644 index 0000000000..6a9468b609 --- /dev/null +++ b/.changeset/tough-steaks-travel.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +chore: Reduce hydration comment for {:else if} diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js index e9cfd9c506..389a694741 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js @@ -48,7 +48,9 @@ export function Fragment(node, context) { const is_single_element = trimmed.length === 1 && trimmed[0].type === 'RegularElement'; const is_single_child_not_needing_template = trimmed.length === 1 && - (trimmed[0].type === 'SvelteFragment' || trimmed[0].type === 'TitleElement'); + (trimmed[0].type === 'SvelteFragment' || + trimmed[0].type === 'TitleElement' || + (trimmed[0].type === 'IfBlock' && trimmed[0].elseif)); const template_name = context.state.scope.root.unique('root'); // TODO infer name from parent diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js index d658f9eaf8..0876fa30b6 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js @@ -1,4 +1,4 @@ -/** @import { BlockStatement, Expression } from 'estree' */ +/** @import { BlockStatement, Expression, Identifier } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ import * as b from '../../../../utils/builders.js'; @@ -19,14 +19,21 @@ export function IfBlock(node, context) { let alternate_id; if (node.alternate) { - const alternate = /** @type {BlockStatement} */ (context.visit(node.alternate)); alternate_id = context.state.scope.generate('alternate'); - statements.push(b.var(b.id(alternate_id), b.arrow([b.id('$$anchor')], alternate))); + const alternate = /** @type {BlockStatement} */ (context.visit(node.alternate)); + const nodes = node.alternate.nodes; + + let alternate_args = [b.id('$$anchor')]; + if (nodes.length === 1 && nodes[0].type === 'IfBlock' && nodes[0].elseif) { + alternate_args.push(b.id('$$elseif')); + } + + statements.push(b.var(b.id(alternate_id), b.arrow(alternate_args, alternate))); } /** @type {Expression[]} */ const args = [ - context.state.node, + node.elseif ? b.id('$$anchor') : context.state.node, b.arrow( [b.id('$$render')], b.block([ @@ -34,13 +41,7 @@ export function IfBlock(node, context) { /** @type {Expression} */ (context.visit(node.test)), b.stmt(b.call(b.id('$$render'), b.id(consequent_id))), alternate_id - ? b.stmt( - b.call( - b.id('$$render'), - b.id(alternate_id), - node.alternate ? b.literal(false) : undefined - ) - ) + ? b.stmt(b.call(b.id('$$render'), b.id(alternate_id), b.literal(false))) : undefined ) ]) @@ -69,7 +70,7 @@ export function IfBlock(node, context) { // ...even though they're logically equivalent. In the first case, the // transition will only play when `y` changes, but in the second it // should play when `x` or `y` change — both are considered 'local' - args.push(b.literal(true)); + args.push(b.id('$$elseif')); } statements.push(b.stmt(b.call('$.if', ...args))); diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js index 4df09aa8b9..cbdd2cd8cc 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js @@ -1,4 +1,4 @@ -/** @import { BlockStatement, Expression } from 'estree' */ +/** @import { BlockStatement, Expression, IfStatement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ import { BLOCK_OPEN_ELSE } from '../../../../../internal/server/hydration.js'; @@ -10,19 +10,29 @@ import { block_close, block_open } from './shared/utils.js'; * @param {ComponentContext} context */ export function IfBlock(node, context) { - const test = /** @type {Expression} */ (context.visit(node.test)); - const consequent = /** @type {BlockStatement} */ (context.visit(node.consequent)); + consequent.body.unshift(b.stmt(b.assignment('+=', b.id('$$payload.out'), block_open))); + let if_statement = b.if(/** @type {Expression} */ (context.visit(node.test)), consequent); - const alternate = node.alternate - ? /** @type {BlockStatement} */ (context.visit(node.alternate)) - : b.block([]); + context.state.template.push(if_statement, block_close); - consequent.body.unshift(b.stmt(b.assignment('+=', b.id('$$payload.out'), block_open))); + let index = 1; + let alt = node.alternate; + while (alt && alt.nodes.length === 1 && alt.nodes[0].type === 'IfBlock' && alt.nodes[0].elseif) { + const elseif = alt.nodes[0]; + const alternate = /** @type {BlockStatement} */ (context.visit(elseif.consequent)); + alternate.body.unshift( + b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(`<!--[${index++}-->`))) + ); + if_statement = if_statement.alternate = b.if( + /** @type {Expression} */ (context.visit(elseif.test)), + alternate + ); + alt = elseif.alternate; + } - alternate.body.unshift( + if_statement.alternate = alt ? /** @type {BlockStatement} */ (context.visit(alt)) : b.block([]); + if_statement.alternate.body.unshift( b.stmt(b.assignment('+=', b.id('$$payload.out'), b.literal(BLOCK_OPEN_ELSE))) ); - - context.state.template.push(b.if(test, consequent, alternate), block_close); } diff --git a/packages/svelte/src/internal/client/dom/blocks/if.js b/packages/svelte/src/internal/client/dom/blocks/if.js index 36790c05c1..423c436fe4 100644 --- a/packages/svelte/src/internal/client/dom/blocks/if.js +++ b/packages/svelte/src/internal/client/dom/blocks/if.js @@ -9,16 +9,16 @@ import { set_hydrating } from '../hydration.js'; import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js'; -import { HYDRATION_START_ELSE, UNINITIALIZED } from '../../../../constants.js'; +import { HYDRATION_START, HYDRATION_START_ELSE, UNINITIALIZED } from '../../../../constants.js'; /** * @param {TemplateNode} node - * @param {(branch: (fn: (anchor: Node) => void, flag?: boolean) => void) => void} fn - * @param {boolean} [elseif] True if this is an `{:else if ...}` block rather than an `{#if ...}`, as that affects which transitions are considered 'local' + * @param {(branch: (fn: (anchor: Node, elseif?: [number,number]) => void, flag?: boolean) => void) => void} fn + * @param {[number,number]} [elseif] * @returns {void} */ -export function if_block(node, fn, elseif = false) { - if (hydrating) { +export function if_block(node, fn, [root_index, hydrate_index] = [0, 0]) { + if (hydrating && root_index === 0) { hydrate_next(); } @@ -33,26 +33,44 @@ export function if_block(node, fn, elseif = false) { /** @type {UNINITIALIZED | boolean | null} */ var condition = UNINITIALIZED; - var flags = elseif ? EFFECT_TRANSPARENT : 0; + var flags = root_index > 0 ? EFFECT_TRANSPARENT : 0; var has_branch = false; - const set_branch = (/** @type {(anchor: Node) => void} */ fn, flag = true) => { + const set_branch = ( + /** @type {(anchor: Node, elseif?: [number,number]) => void} */ fn, + flag = true + ) => { has_branch = true; update_branch(flag, fn); }; const update_branch = ( /** @type {boolean | null} */ new_condition, - /** @type {null | ((anchor: Node) => void)} */ fn + /** @type {null | ((anchor: Node, elseif?: [number,number]) => void)} */ fn ) => { if (condition === (condition = new_condition)) return; /** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */ let mismatch = false; - if (hydrating) { - const is_else = /** @type {Comment} */ (anchor).data === HYDRATION_START_ELSE; + if (hydrating && hydrate_index !== -1) { + if (root_index === 0) { + const data = /** @type {Comment} */ (anchor).data; + if (data === HYDRATION_START) { + hydrate_index = 0; + } else if (data === HYDRATION_START_ELSE) { + hydrate_index = Infinity; + } else { + hydrate_index = parseInt(data.substring(1)); + if (hydrate_index !== hydrate_index) { + // if hydrate_index is NaN + // we set an invalid index to force mismatch + hydrate_index = condition ? Infinity : -1; + } + } + } + const is_else = hydrate_index > root_index; if (!!condition === is_else) { // Hydration mismatch: remove everything inside the anchor and start fresh. @@ -62,6 +80,7 @@ export function if_block(node, fn, elseif = false) { set_hydrate_node(anchor); set_hydrating(false); mismatch = true; + hydrate_index = -1; // ignore hydration in next else if } } @@ -81,7 +100,7 @@ export function if_block(node, fn, elseif = false) { if (alternate_effect) { resume_effect(alternate_effect); } else if (fn) { - alternate_effect = branch(() => fn(anchor)); + alternate_effect = branch(() => fn(anchor, [root_index + 1, hydrate_index])); } if (consequent_effect) { From 7ce2dfc62297a41b8e403803cd54a86caf177418 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 16:40:08 +0000 Subject: [PATCH 0217/1429] Version Packages (#15402) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/hip-oranges-hang.md | 5 ----- .changeset/tough-steaks-travel.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 .changeset/hip-oranges-hang.md delete mode 100644 .changeset/tough-steaks-travel.md diff --git a/.changeset/hip-oranges-hang.md b/.changeset/hip-oranges-hang.md deleted file mode 100644 index addbeafa9c..0000000000 --- a/.changeset/hip-oranges-hang.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: disallow `bind:group` to snippet parameters diff --git a/.changeset/tough-steaks-travel.md b/.changeset/tough-steaks-travel.md deleted file mode 100644 index 6a9468b609..0000000000 --- a/.changeset/tough-steaks-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -chore: Reduce hydration comment for {:else if} diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index cb33bc0d39..f32f872ee0 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.21.0 + +### Minor Changes + +- chore: Reduce hydration comment for {:else if} ([#15250](https://github.com/sveltejs/svelte/pull/15250)) + +### Patch Changes + +- fix: disallow `bind:group` to snippet parameters ([#15401](https://github.com/sveltejs/svelte/pull/15401)) + ## 5.20.5 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index bff6b5adc0..f32466ce85 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.20.5", + "version": "5.21.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b77e0ea49e..46fbec674c 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.20.5'; +export const VERSION = '5.21.0'; export const PUBLIC_VERSION = '5'; From b82692af2f032407e34137e4fe17aea76b8bf7f1 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Tue, 4 Mar 2025 05:49:31 -0500 Subject: [PATCH 0218/1429] feat: add `idPrefix` to `render` (#15428) This ensures someone like Astro can render multiple islands and the unique ids produced in them are able to be deduplicated --------- Co-authored-by: Hugos68 <hugokorteapple@gmail.com> Co-authored-by: paoloricciuti <ricciutipaolo@gmail.com> Co-authored-by: Simon Holthausen <simon.holthausen@vercel.com> --- .changeset/wise-grapes-enjoy.md | 5 ++ packages/svelte/src/index.d.ts | 1 + .../src/internal/client/dom/template.js | 14 ++--- .../svelte/src/internal/disclose-version.js | 7 ++- packages/svelte/src/internal/server/index.js | 13 ++-- packages/svelte/src/server/index.d.ts | 12 +++- packages/svelte/tests/hydration/test.ts | 11 ++-- .../svelte/tests/runtime-browser/assert.js | 1 + .../tests/runtime-browser/driver-ssr.js | 2 +- .../svelte/tests/runtime-browser/test-ssr.ts | 2 +- .../svelte/tests/runtime-legacy/shared.ts | 10 +++- .../samples/props-id-prefix/Child.svelte | 5 ++ .../samples/props-id-prefix/_config.js | 60 +++++++++++++++++++ .../samples/props-id-prefix/main.svelte | 19 ++++++ .../tests/server-side-rendering/test.ts | 3 +- packages/svelte/types/index.d.ts | 13 +++- 16 files changed, 149 insertions(+), 29 deletions(-) create mode 100644 .changeset/wise-grapes-enjoy.md create mode 100644 packages/svelte/tests/runtime-runes/samples/props-id-prefix/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/props-id-prefix/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/props-id-prefix/main.svelte diff --git a/.changeset/wise-grapes-enjoy.md b/.changeset/wise-grapes-enjoy.md new file mode 100644 index 0000000000..05996232db --- /dev/null +++ b/.changeset/wise-grapes-enjoy.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: Add `idPrefix` option to `render` diff --git a/packages/svelte/src/index.d.ts b/packages/svelte/src/index.d.ts index 554510542e..38e6086689 100644 --- a/packages/svelte/src/index.d.ts +++ b/packages/svelte/src/index.d.ts @@ -19,6 +19,7 @@ export interface ComponentConstructorOptions< intro?: boolean; recover?: boolean; sync?: boolean; + idPrefix?: string; $$inline?: boolean; } diff --git a/packages/svelte/src/internal/client/dom/template.js b/packages/svelte/src/internal/client/dom/template.js index 575bf55cf6..de2df62c92 100644 --- a/packages/svelte/src/internal/client/dom/template.js +++ b/packages/svelte/src/internal/client/dom/template.js @@ -250,12 +250,6 @@ export function append(anchor, dom) { anchor.before(/** @type {Node} */ (dom)); } -let uid = 1; - -export function reset_props_id() { - uid = 1; -} - /** * Create (or hydrate) an unique UID for the component instance. */ @@ -264,12 +258,16 @@ export function props_id() { hydrating && hydrate_node && hydrate_node.nodeType === 8 && - hydrate_node.textContent?.startsWith('#s') + hydrate_node.textContent?.startsWith(`#`) ) { const id = hydrate_node.textContent.substring(1); hydrate_next(); return id; } - return 'c' + uid++; + // @ts-expect-error This way we ensure the id is unique even across Svelte runtimes + (window.__svelte ??= {}).uid ??= 1; + + // @ts-expect-error + return `c${window.__svelte.uid++}`; } diff --git a/packages/svelte/src/internal/disclose-version.js b/packages/svelte/src/internal/disclose-version.js index 5e72f1e216..86c3482b5d 100644 --- a/packages/svelte/src/internal/disclose-version.js +++ b/packages/svelte/src/internal/disclose-version.js @@ -1,5 +1,6 @@ import { PUBLIC_VERSION } from '../version.js'; -if (typeof window !== 'undefined') - // @ts-ignore - (window.__svelte ||= { v: new Set() }).v.add(PUBLIC_VERSION); +if (typeof window !== 'undefined') { + // @ts-expect-error + ((window.__svelte ??= {}).v ??= new Set()).add(PUBLIC_VERSION); +} diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 160a1faa65..2591dbe4ea 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -86,9 +86,14 @@ export function element(payload, tag, attributes_fn = noop, children_fn = noop) */ export let on_destroy = []; -function props_id_generator() { +/** + * Creates an ID generator + * @param {string} prefix + * @returns {() => string} + */ +function props_id_generator(prefix) { let uid = 1; - return () => 's' + uid++; + return () => `${prefix}s${uid++}`; } /** @@ -96,11 +101,11 @@ function props_id_generator() { * Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app. * @template {Record<string, any>} Props * @param {import('svelte').Component<Props> | ComponentType<SvelteComponent<Props>>} component - * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> }} [options] + * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string }} [options] * @returns {RenderOutput} */ export function render(component, options = {}) { - const uid = props_id_generator(); + const uid = props_id_generator(options.idPrefix ? options.idPrefix + '-' : ''); /** @type {Payload} */ const payload = { out: '', diff --git a/packages/svelte/src/server/index.d.ts b/packages/svelte/src/server/index.d.ts index b65ce5bdaa..d5a3b813e6 100644 --- a/packages/svelte/src/server/index.d.ts +++ b/packages/svelte/src/server/index.d.ts @@ -12,10 +12,18 @@ export function render< ...args: {} extends Props ? [ component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp, - options?: { props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> } + options?: { + props?: Omit<Props, '$$slots' | '$$events'>; + context?: Map<any, any>; + idPrefix?: string; + } ] : [ component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp, - options: { props: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> } + options: { + props: Omit<Props, '$$slots' | '$$events'>; + context?: Map<any, any>; + idPrefix?: string; + } ] ): RenderOutput; diff --git a/packages/svelte/tests/hydration/test.ts b/packages/svelte/tests/hydration/test.ts index 3bf2dd286c..266ac07bff 100644 --- a/packages/svelte/tests/hydration/test.ts +++ b/packages/svelte/tests/hydration/test.ts @@ -2,9 +2,9 @@ import * as fs from 'node:fs'; import { assert } from 'vitest'; -import { compile_directory, should_update_expected } from '../helpers.js'; +import { compile_directory } from '../helpers.js'; import { assert_html_equal } from '../html_equal.js'; -import { suite, assert_ok, type BaseTest } from '../suite.js'; +import { assert_ok, suite, type BaseTest } from '../suite.js'; import { createClassComponent } from 'svelte/legacy'; import { render } from 'svelte/server'; import type { CompileOptions } from '#compiler'; @@ -13,6 +13,7 @@ import { flushSync } from 'svelte'; interface HydrationTest extends BaseTest { load_compiled?: boolean; server_props?: Record<string, any>; + id_prefix?: string; props?: Record<string, any>; compileOptions?: Partial<CompileOptions>; /** @@ -50,7 +51,8 @@ const { test, run } = suite<HydrationTest>(async (config, cwd) => { const head = window.document.head; const rendered = render((await import(`${cwd}/_output/server/main.svelte.js`)).default, { - props: config.server_props ?? config.props ?? {} + props: config.server_props ?? config.props ?? {}, + idPrefix: config?.id_prefix }); const override = read(`${cwd}/_override.html`); @@ -103,7 +105,8 @@ const { test, run } = suite<HydrationTest>(async (config, cwd) => { component: (await import(`${cwd}/_output/client/main.svelte.js`)).default, target, hydrate: true, - props: config.props + props: config.props, + idPrefix: config?.id_prefix }); console.warn = warn; diff --git a/packages/svelte/tests/runtime-browser/assert.js b/packages/svelte/tests/runtime-browser/assert.js index 249d19f809..fb460c722a 100644 --- a/packages/svelte/tests/runtime-browser/assert.js +++ b/packages/svelte/tests/runtime-browser/assert.js @@ -119,6 +119,7 @@ function normalize_children(node) { * skip_mode?: Array<'server' | 'client' | 'hydrate'>; * html?: string; * ssrHtml?: string; + * id_prefix?: string; * props?: Props; * compileOptions?: Partial<CompileOptions>; * test?: (args: { diff --git a/packages/svelte/tests/runtime-browser/driver-ssr.js b/packages/svelte/tests/runtime-browser/driver-ssr.js index f5f15b6493..7067e48a1f 100644 --- a/packages/svelte/tests/runtime-browser/driver-ssr.js +++ b/packages/svelte/tests/runtime-browser/driver-ssr.js @@ -6,5 +6,5 @@ import config from '__CONFIG__'; import { render } from 'svelte/server'; export default function () { - return render(SvelteComponent, { props: config.props || {} }); + return render(SvelteComponent, { props: config.props || {}, idPrefix: config?.id_prefix }); } diff --git a/packages/svelte/tests/runtime-browser/test-ssr.ts b/packages/svelte/tests/runtime-browser/test-ssr.ts index 2ff1659f80..6987fac915 100644 --- a/packages/svelte/tests/runtime-browser/test-ssr.ts +++ b/packages/svelte/tests/runtime-browser/test-ssr.ts @@ -20,7 +20,7 @@ export async function run_ssr_test( await compile_directory(test_dir, 'server', config.compileOptions); const Component = (await import(`${test_dir}/_output/server/main.svelte.js`)).default; - const { body } = render(Component, { props: config.props || {} }); + const { body } = render(Component, { props: config.props || {}, idPrefix: config.id_prefix }); fs.writeFileSync(`${test_dir}/_output/rendered.html`, body); diff --git a/packages/svelte/tests/runtime-legacy/shared.ts b/packages/svelte/tests/runtime-legacy/shared.ts index 3ffb3092a4..fc748ce6b2 100644 --- a/packages/svelte/tests/runtime-legacy/shared.ts +++ b/packages/svelte/tests/runtime-legacy/shared.ts @@ -11,7 +11,6 @@ import { setup_html_equal } from '../html_equal.js'; import { raf } from '../animation-helpers.js'; import type { CompileOptions } from '#compiler'; import { suite_with_variants, type BaseTest } from '../suite.js'; -import { reset_props_id } from '../../src/internal/client/dom/template.js'; type Assert = typeof import('vitest').assert & { htmlEqual(a: string, b: string, description?: string): void; @@ -37,6 +36,7 @@ export interface RuntimeTest<Props extends Record<string, any> = Record<string, compileOptions?: Partial<CompileOptions>; props?: Props; server_props?: Props; + id_prefix?: string; before_test?: () => void; after_test?: () => void; test?: (args: { @@ -285,7 +285,8 @@ async function run_test_variant( // ssr into target const SsrSvelteComponent = (await import(`${cwd}/_output/server/main.svelte.js`)).default; const { html, head } = render(SsrSvelteComponent, { - props: config.server_props ?? config.props ?? {} + props: config.server_props ?? config.props ?? {}, + idPrefix: config.id_prefix }); fs.writeFileSync(`${cwd}/_output/rendered.html`, html); @@ -346,7 +347,10 @@ async function run_test_variant( if (runes) { props = proxy({ ...(config.props || {}) }); - reset_props_id(); + + // @ts-expect-error + globalThis.__svelte.uid = 1; + if (manual_hydrate) { hydrate_fn = () => { instance = hydrate(mod.default, { diff --git a/packages/svelte/tests/runtime-runes/samples/props-id-prefix/Child.svelte b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/Child.svelte new file mode 100644 index 0000000000..ad8bbd6f01 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/Child.svelte @@ -0,0 +1,5 @@ +<script> + let id = $props.id(); +</script> + +<p>{id}</p> diff --git a/packages/svelte/tests/runtime-runes/samples/props-id-prefix/_config.js b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/_config.js new file mode 100644 index 0000000000..6d4306c413 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/_config.js @@ -0,0 +1,60 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + id_prefix: 'myPrefix', + test({ assert, target, variant }) { + if (variant === 'dom') { + assert.htmlEqual( + target.innerHTML, + ` + <button>toggle</button> + <h1>c1</h1> + <p>c2</p> + <p>c3</p> + <p>c4</p> + ` + ); + } else { + assert.htmlEqual( + target.innerHTML, + ` + <button>toggle</button> + <h1>myPrefix-s1</h1> + <p>myPrefix-s2</p> + <p>myPrefix-s3</p> + <p>myPrefix-s4</p> + ` + ); + } + + let button = target.querySelector('button'); + flushSync(() => button?.click()); + + if (variant === 'dom') { + assert.htmlEqual( + target.innerHTML, + ` + <button>toggle</button> + <h1>c1</h1> + <p>c2</p> + <p>c3</p> + <p>c4</p> + <p>c5</p> + ` + ); + } else { + assert.htmlEqual( + target.innerHTML, + ` + <button>toggle</button> + <h1>myPrefix-s1</h1> + <p>myPrefix-s2</p> + <p>myPrefix-s3</p> + <p>myPrefix-s4</p> + <p>c1</p> + ` + ); + } + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/props-id-prefix/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/main.svelte new file mode 100644 index 0000000000..646bb2ebde --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-id-prefix/main.svelte @@ -0,0 +1,19 @@ +<script> + import Child from './Child.svelte'; + + let id = $props.id(); + + let show = $state(false); +</script> + +<button onclick={() => show = !show}>toggle</button> + +<h1>{id}</h1> + +<Child /> +<Child /> +<Child /> + +{#if show} + <Child /> +{/if} diff --git a/packages/svelte/tests/server-side-rendering/test.ts b/packages/svelte/tests/server-side-rendering/test.ts index f76c5b539f..3e57539427 100644 --- a/packages/svelte/tests/server-side-rendering/test.ts +++ b/packages/svelte/tests/server-side-rendering/test.ts @@ -15,6 +15,7 @@ import type { CompileOptions } from '#compiler'; interface SSRTest extends BaseTest { compileOptions?: Partial<CompileOptions>; props?: Record<string, any>; + id_prefix?: string; withoutNormalizeHtml?: boolean; errors?: string[]; } @@ -33,7 +34,7 @@ const { test, run } = suite<SSRTest>(async (config, test_dir) => { const Component = (await import(`${test_dir}/_output/server/main.svelte.js`)).default; const expected_html = try_read_file(`${test_dir}/_expected.html`); - const rendered = render(Component, { props: config.props || {} }); + const rendered = render(Component, { props: config.props || {}, idPrefix: config.id_prefix }); const { body, head } = rendered; fs.writeFileSync(`${test_dir}/_output/rendered.html`, body); diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 4c47661af8..c3dbdcac79 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -16,6 +16,7 @@ declare module 'svelte' { intro?: boolean; recover?: boolean; sync?: boolean; + idPrefix?: string; $$inline?: boolean; } @@ -2080,11 +2081,19 @@ declare module 'svelte/server' { ...args: {} extends Props ? [ component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp, - options?: { props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> } + options?: { + props?: Omit<Props, '$$slots' | '$$events'>; + context?: Map<any, any>; + idPrefix?: string; + } ] : [ component: Comp extends SvelteComponent<any> ? ComponentType<Comp> : Comp, - options: { props: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any> } + options: { + props: Omit<Props, '$$slots' | '$$events'>; + context?: Map<any, any>; + idPrefix?: string; + } ] ): RenderOutput; interface RenderOutput { From e3dc396e01469635e68ac25675739de7baaea2f7 Mon Sep 17 00:00:00 2001 From: Tom Adler <me@arty.name> Date: Tue, 4 Mar 2025 12:09:24 +0100 Subject: [PATCH 0219/1429] fix: make dialog element and role interactive (#15429) Fixes #8283 Also see https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/issues/932 --- .changeset/gentle-humans-call.md | 5 +++++ .../phases/2-analyze/visitors/shared/a11y.js | 2 +- .../input.svelte | 1 + .../warnings.json | 20 +++++++++---------- 4 files changed, 17 insertions(+), 11 deletions(-) create mode 100644 .changeset/gentle-humans-call.md diff --git a/.changeset/gentle-humans-call.md b/.changeset/gentle-humans-call.md new file mode 100644 index 0000000000..d3c6590e8a --- /dev/null +++ b/.changeset/gentle-humans-call.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: make dialog element and role interactive diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js index 24a8e5122d..1f58a28cad 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/a11y.js @@ -30,7 +30,7 @@ const non_interactive_roles = non_abstract_roles // 'generic' is meant to have no semantic meaning. // 'cell' is treated as CellRole by the AXObject which is interactive, so we treat 'cell' it as interactive as well. !['toolbar', 'tabpanel', 'generic', 'cell'].includes(name) && - !role?.superClass.some((classes) => classes.includes('widget')) + !role?.superClass.some((classes) => classes.includes('widget') || classes.includes('window')) ); }) .concat( diff --git a/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte b/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte index 1adea90ba3..759f969d82 100644 --- a/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte +++ b/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/input.svelte @@ -3,6 +3,7 @@ <div role="button" tabindex="-1" on:click={() => {}} on:keypress={() => {}}></div> <div role="listitem" aria-hidden="true" on:click={() => {}} on:keypress={() => {}}></div> <button on:click={() => {}}>click me</button> +<dialog on:click={() => {}}>alert</dialog> <h1 contenteditable="true" on:keydown={() => {}}>Heading</h1> <h1>Heading</h1> diff --git a/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/warnings.json b/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/warnings.json index 760866d136..e8bcd0cc1a 100644 --- a/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/warnings.json +++ b/packages/svelte/tests/validator/samples/a11y-no-noninteractive-element-interactions/warnings.json @@ -3,60 +3,60 @@ "code": "a11y_no_noninteractive_element_interactions", "end": { "column": 51, - "line": 10 + "line": 11 }, "message": "Non-interactive element `<div>` should not be assigned mouse or keyboard event listeners", "start": { "column": 0, - "line": 10 + "line": 11 } }, { "code": "a11y_no_noninteractive_element_interactions", "end": { "column": 58, - "line": 11 + "line": 12 }, "message": "Non-interactive element `<h1>` should not be assigned mouse or keyboard event listeners", "start": { "column": 0, - "line": 11 + "line": 12 } }, { "code": "a11y_no_noninteractive_element_interactions", "end": { "column": 50, - "line": 12 + "line": 13 }, "message": "Non-interactive element `<h1>` should not be assigned mouse or keyboard event listeners", "start": { "column": 0, - "line": 12 + "line": 13 } }, { "code": "a11y_no_noninteractive_element_interactions", "end": { "column": 30, - "line": 13 + "line": 14 }, "message": "Non-interactive element `<p>` should not be assigned mouse or keyboard event listeners", "start": { "column": 0, - "line": 13 + "line": 14 } }, { "code": "a11y_no_noninteractive_element_interactions", "end": { "column": 50, - "line": 14 + "line": 15 }, "message": "Non-interactive element `<div>` should not be assigned mouse or keyboard event listeners", "start": { "column": 0, - "line": 14 + "line": 15 } } ] From 0f2633736d4a6997456a368157115e1fa3781cca Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 13:26:13 +0100 Subject: [PATCH 0220/1429] Version Packages (#15431) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/gentle-humans-call.md | 5 ----- .changeset/wise-grapes-enjoy.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 .changeset/gentle-humans-call.md delete mode 100644 .changeset/wise-grapes-enjoy.md diff --git a/.changeset/gentle-humans-call.md b/.changeset/gentle-humans-call.md deleted file mode 100644 index d3c6590e8a..0000000000 --- a/.changeset/gentle-humans-call.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: make dialog element and role interactive diff --git a/.changeset/wise-grapes-enjoy.md b/.changeset/wise-grapes-enjoy.md deleted file mode 100644 index 05996232db..0000000000 --- a/.changeset/wise-grapes-enjoy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: Add `idPrefix` option to `render` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index f32f872ee0..6f6526c10b 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.22.0 + +### Minor Changes + +- feat: Add `idPrefix` option to `render` ([#15428](https://github.com/sveltejs/svelte/pull/15428)) + +### Patch Changes + +- fix: make dialog element and role interactive ([#15429](https://github.com/sveltejs/svelte/pull/15429)) + ## 5.21.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index f32466ce85..82d180d946 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.21.0", + "version": "5.22.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 46fbec674c..4b9540ad2a 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.21.0'; +export const VERSION = '5.22.0'; export const PUBLIC_VERSION = '5'; From ef7fd96ccf06aa6442db56a0871131163a7016ab Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 4 Mar 2025 14:36:44 +0100 Subject: [PATCH 0221/1429] fix: switch to our fork acorn typescript plugin (#15393) * fix: switch to our fork acorn typescript plugin * changeset * bump * bump * bump * adjust snapshots * this should be obsolete now * bump * bump --- .changeset/selfish-cougars-allow.md | 5 ++ packages/svelte/package.json | 2 +- .../src/compiler/phases/1-parse/acorn.js | 47 +------------------ .../src/compiler/phases/1-parse/ambient.d.ts | 3 -- .../samples/comment-before-script/output.json | 2 +- .../samples/snippets/output.json | 2 +- .../typescript-in-event-handler/output.json | 8 ++-- packages/svelte/tsconfig.json | 1 - pnpm-lock.yaml | 24 +++++----- 9 files changed, 26 insertions(+), 68 deletions(-) create mode 100644 .changeset/selfish-cougars-allow.md delete mode 100644 packages/svelte/src/compiler/phases/1-parse/ambient.d.ts diff --git a/.changeset/selfish-cougars-allow.md b/.changeset/selfish-cougars-allow.md new file mode 100644 index 0000000000..8cea0e52df --- /dev/null +++ b/.changeset/selfish-cougars-allow.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: switch acorn-typescript plugin diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 82d180d946..b756719956 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -150,7 +150,7 @@ "@jridgewell/sourcemap-codec": "^1.5.0", "@types/estree": "^1.0.5", "acorn": "^8.12.1", - "acorn-typescript": "^1.4.13", + "@sveltejs/acorn-typescript": "^1.0.5", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", diff --git a/packages/svelte/src/compiler/phases/1-parse/acorn.js b/packages/svelte/src/compiler/phases/1-parse/acorn.js index 6c106e0a84..36f7688c49 100644 --- a/packages/svelte/src/compiler/phases/1-parse/acorn.js +++ b/packages/svelte/src/compiler/phases/1-parse/acorn.js @@ -1,11 +1,9 @@ /** @import { Comment, Program } from 'estree' */ -/** @import { Node } from 'acorn' */ import * as acorn from 'acorn'; import { walk } from 'zimmerframe'; -import { tsPlugin } from 'acorn-typescript'; -import { locator } from '../../state.js'; +import { tsPlugin } from '@sveltejs/acorn-typescript'; -const ParserWithTS = acorn.Parser.extend(tsPlugin({ allowSatisfies: true })); +const ParserWithTS = acorn.Parser.extend(tsPlugin()); /** * @param {string} source @@ -48,7 +46,6 @@ export function parse(source, typescript, is_script) { } } - if (typescript) amend(source, ast); add_comments(ast); return /** @type {Program} */ (ast); @@ -71,7 +68,6 @@ export function parse_expression_at(source, typescript, index) { locations: true }); - if (typescript) amend(source, ast); add_comments(ast); return ast; @@ -173,42 +169,3 @@ function get_comment_handlers(source) { } }; } - -/** - * Tidy up some stuff left behind by acorn-typescript - * @param {string} source - * @param {Node} node - */ -function amend(source, node) { - return walk(node, null, { - _(node, context) { - // @ts-expect-error - delete node.loc.start.index; - // @ts-expect-error - delete node.loc.end.index; - - if (typeof node.loc?.end === 'number') { - const loc = locator(node.loc.end); - if (loc) { - node.loc.end = { - line: loc.line, - column: loc.column - }; - } - } - - if ( - /** @type {any} */ (node).typeAnnotation && - (node.end === undefined || node.end < node.start) - ) { - // i think there might be a bug in acorn-typescript that prevents - // `end` from being assigned when there's a type annotation - let end = /** @type {any} */ (node).typeAnnotation.start; - while (/\s/.test(source[end - 1])) end -= 1; - node.end = end; - } - - context.next(); - } - }); -} diff --git a/packages/svelte/src/compiler/phases/1-parse/ambient.d.ts b/packages/svelte/src/compiler/phases/1-parse/ambient.d.ts deleted file mode 100644 index 8243dd0a42..0000000000 --- a/packages/svelte/src/compiler/phases/1-parse/ambient.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Silence the acorn typescript errors through this ambient type definition + tsconfig.json path alias -// That way we can omit `"skipLibCheck": true` and catch other errors in our d.ts files -declare module 'acorn-typescript'; diff --git a/packages/svelte/tests/parser-modern/samples/comment-before-script/output.json b/packages/svelte/tests/parser-modern/samples/comment-before-script/output.json index 13c099a857..1aca0ce036 100644 --- a/packages/svelte/tests/parser-modern/samples/comment-before-script/output.json +++ b/packages/svelte/tests/parser-modern/samples/comment-before-script/output.json @@ -75,7 +75,7 @@ "id": { "type": "Identifier", "start": 52, - "end": 57, + "end": 65, "loc": { "start": { "line": 3, diff --git a/packages/svelte/tests/parser-modern/samples/snippets/output.json b/packages/svelte/tests/parser-modern/samples/snippets/output.json index 2cf2596b18..acf484d8ae 100644 --- a/packages/svelte/tests/parser-modern/samples/snippets/output.json +++ b/packages/svelte/tests/parser-modern/samples/snippets/output.json @@ -28,7 +28,7 @@ { "type": "Identifier", "start": 43, - "end": 46, + "end": 54, "loc": { "start": { "line": 3, diff --git a/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json b/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json index 5d72e01b0a..9c515ad905 100644 --- a/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json +++ b/packages/svelte/tests/parser-modern/samples/typescript-in-event-handler/output.json @@ -25,7 +25,6 @@ "end": 147, "type": "OnDirective", "name": "click", - "modifiers": [], "expression": { "type": "ArrowFunctionExpression", "start": 73, @@ -48,7 +47,7 @@ { "type": "Identifier", "start": 74, - "end": 75, + "end": 87, "loc": { "start": { "line": 6, @@ -155,7 +154,7 @@ "id": { "type": "Identifier", "start": 102, - "end": 106, + "end": 114, "loc": { "start": { "line": 7, @@ -316,7 +315,8 @@ } ] } - } + }, + "modifiers": [] } ], "fragment": { diff --git a/packages/svelte/tsconfig.json b/packages/svelte/tsconfig.json index c9f0fb3b2b..76005add13 100644 --- a/packages/svelte/tsconfig.json +++ b/packages/svelte/tsconfig.json @@ -15,7 +15,6 @@ "allowJs": true, "checkJs": true, "paths": { - "acorn-typescript": ["./src/compiler/phases/1-parse/ambient.d.ts"], "svelte": ["./src/index.d.ts"], "svelte/action": ["./src/action/public.d.ts"], "svelte/compiler": ["./src/compiler/public.d.ts"], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2457a94041..9ea9c9e929 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,15 +65,15 @@ importers: '@jridgewell/sourcemap-codec': specifier: ^1.5.0 version: 1.5.0 + '@sveltejs/acorn-typescript': + specifier: ^1.0.5 + version: 1.0.5(acorn@8.14.0) '@types/estree': specifier: ^1.0.5 version: 1.0.6 acorn: specifier: ^8.12.1 version: 8.14.0 - acorn-typescript: - specifier: ^1.4.13 - version: 1.4.13(acorn@8.14.0) aria-query: specifier: ^5.3.1 version: 5.3.1 @@ -626,6 +626,11 @@ packages: peerDependencies: eslint: '>=8.40.0' + '@sveltejs/acorn-typescript@1.0.5': + resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==} + peerDependencies: + acorn: ^8.9.0 + '@sveltejs/eslint-config@8.1.0': resolution: {integrity: sha512-cfgp4lPREYBjNd4ZzaP/jA85ufm7vfXiaV7h9vILXNogne80IbZRNhRCQ8XoOqTAOY/pChIzWTBuR8aDNMbAEA==} peerDependencies: @@ -779,11 +784,6 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn-typescript@1.4.13: - resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==} - peerDependencies: - acorn: '>=8.9.0' - acorn@8.14.0: resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} @@ -2720,6 +2720,10 @@ snapshots: eslint-visitor-keys: 3.4.3 espree: 9.6.1 + '@sveltejs/acorn-typescript@1.0.5(acorn@8.14.0)': + dependencies: + acorn: 8.14.0 + '@sveltejs/eslint-config@8.1.0(@stylistic/eslint-plugin-js@1.8.0(eslint@9.9.1))(eslint-config-prettier@9.1.0(eslint@9.9.1))(eslint-plugin-n@17.9.0(eslint@9.9.1))(eslint-plugin-svelte@2.38.0(eslint@9.9.1)(svelte@packages+svelte))(eslint@9.9.1)(typescript-eslint@8.2.0(eslint@9.9.1)(typescript@5.5.4))(typescript@5.5.4)': dependencies: '@stylistic/eslint-plugin-js': 1.8.0(eslint@9.9.1) @@ -2926,10 +2930,6 @@ snapshots: dependencies: acorn: 8.14.0 - acorn-typescript@1.4.13(acorn@8.14.0): - dependencies: - acorn: 8.14.0 - acorn@8.14.0: {} agent-base@7.1.1: From cd56c1d60eb5fb68af6618013be57f1a9fd9d55a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 09:30:57 -0500 Subject: [PATCH 0222/1429] Version Packages (#15433) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/selfish-cougars-allow.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/selfish-cougars-allow.md diff --git a/.changeset/selfish-cougars-allow.md b/.changeset/selfish-cougars-allow.md deleted file mode 100644 index 8cea0e52df..0000000000 --- a/.changeset/selfish-cougars-allow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: switch acorn-typescript plugin diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 6f6526c10b..5f031f8f99 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.22.1 + +### Patch Changes + +- chore: switch acorn-typescript plugin ([#15393](https://github.com/sveltejs/svelte/pull/15393)) + ## 5.22.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index b756719956..3acd327682 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.22.0", + "version": "5.22.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 4b9540ad2a..4cea5c4b7f 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.22.0'; +export const VERSION = '5.22.1'; export const PUBLIC_VERSION = '5'; From b28b01350367110521d3a3eeae825ffe29ec0e2a Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 4 Mar 2025 13:53:14 -0800 Subject: [PATCH 0223/1429] chore: speed up linting (#15424) --- package.json | 2 +- pnpm-lock.yaml | 208 ++++++++++++++++++++++++++----------------------- 2 files changed, 113 insertions(+), 97 deletions(-) diff --git a/package.json b/package.json index 2fe545b361..ad69bfc9ca 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "prettier-plugin-svelte": "^3.1.2", "svelte": "workspace:^", "typescript": "^5.5.4", - "typescript-eslint": "^8.2.0", + "typescript-eslint": "^8.24.0", "v8-natives": "^1.2.5", "vitest": "^2.1.9" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ea9c9e929..c687db12d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 2.27.8 '@sveltejs/eslint-config': specifier: ^8.1.0 - version: 8.1.0(@stylistic/eslint-plugin-js@1.8.0(eslint@9.9.1))(eslint-config-prettier@9.1.0(eslint@9.9.1))(eslint-plugin-n@17.9.0(eslint@9.9.1))(eslint-plugin-svelte@2.38.0(eslint@9.9.1)(svelte@packages+svelte))(eslint@9.9.1)(typescript-eslint@8.2.0(eslint@9.9.1)(typescript@5.5.4))(typescript@5.5.4) + version: 8.1.0(@stylistic/eslint-plugin-js@1.8.0(eslint@9.9.1))(eslint-config-prettier@9.1.0(eslint@9.9.1))(eslint-plugin-n@17.16.1(eslint@9.9.1)(typescript@5.5.4))(eslint-plugin-svelte@2.38.0(eslint@9.9.1)(svelte@packages+svelte))(eslint@9.9.1)(typescript-eslint@8.26.0(eslint@9.9.1)(typescript@5.5.4))(typescript@5.5.4) '@svitejs/changesets-changelog-github-compact': specifier: ^1.1.0 version: 1.1.0 @@ -48,8 +48,8 @@ importers: specifier: ^5.5.4 version: 5.5.4 typescript-eslint: - specifier: ^8.2.0 - version: 8.2.0(eslint@9.9.1)(typescript@5.5.4) + specifier: ^8.24.0 + version: 8.26.0(eslint@9.9.1)(typescript@5.5.4) v8-natives: specifier: ^1.2.5 version: 1.2.5 @@ -688,61 +688,51 @@ packages: '@types/semver@7.5.6': resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==} - '@typescript-eslint/eslint-plugin@8.2.0': - resolution: {integrity: sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==} + '@typescript-eslint/eslint-plugin@8.26.0': + resolution: {integrity: sha512-cLr1J6pe56zjKYajK6SSSre6nl1Gj6xDp1TY0trpgPzjVbgDwd09v2Ws37LABxzkicmUjhEeg/fAUjPJJB1v5Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@8.2.0': - resolution: {integrity: sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==} + '@typescript-eslint/parser@8.26.0': + resolution: {integrity: sha512-mNtXP9LTVBy14ZF3o7JG69gRPBK/2QWtQd0j0oH26HcY/foyJJau6pNUez7QrM5UHnSvwlQcJXKsk0I99B9pOA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/scope-manager@8.2.0': - resolution: {integrity: sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==} + '@typescript-eslint/scope-manager@8.26.0': + resolution: {integrity: sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.2.0': - resolution: {integrity: sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==} + '@typescript-eslint/type-utils@8.26.0': + resolution: {integrity: sha512-ruk0RNChLKz3zKGn2LwXuVoeBcUMh+jaqzN461uMMdxy5H9epZqIBtYj7UiPXRuOpaALXGbmRuZQhmwHhaS04Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/types@8.2.0': - resolution: {integrity: sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==} + '@typescript-eslint/types@8.26.0': + resolution: {integrity: sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.2.0': - resolution: {integrity: sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==} + '@typescript-eslint/typescript-estree@8.26.0': + resolution: {integrity: sha512-tiJ1Hvy/V/oMVRTbEOIeemA2XoylimlDQ03CgPPNaHYZbpsc78Hmngnt+WXZfJX1pjQ711V7g0H7cSJThGYfPQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@8.2.0': - resolution: {integrity: sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==} + '@typescript-eslint/utils@8.26.0': + resolution: {integrity: sha512-2L2tU3FVwhvU14LndnQCA2frYC8JnPDVKyQtWFPf8IYFMt/ykEN1bPolNhNbCVgOmdzTlWdusCTKA/9nKrf8Ig==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/visitor-keys@8.2.0': - resolution: {integrity: sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==} + '@typescript-eslint/visitor-keys@8.26.0': + resolution: {integrity: sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitest/coverage-v8@2.0.5': @@ -1070,8 +1060,8 @@ packages: eslint-plugin-lube@0.4.3: resolution: {integrity: sha512-BVO83tRo090d6a04cl45Gb761SD79cOT6wKxxWrpsH7Rv8I0SJvc79ijE11vvyxxCMiGUVq/w4NqqPJAHyYfSQ==} - eslint-plugin-n@17.9.0: - resolution: {integrity: sha512-CPSaXDXdrT4nsrOrO4mT4VB6FMUkoySRkHWuuJJHVqsIEjIeZgMY1H7AzSwPbDScikBmLN82KeM1u7ixV7PzGg==} + eslint-plugin-n@17.16.1: + resolution: {integrity: sha512-/7FVAwjUrix9P5lycnsYRIQRwFo/DZROD+ZXWLpE+/EZWLyuLvyFaRdAPYJSz+nlAdZIZp+LAzlBerQSVYUNFg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: '>=8.23.0' @@ -1098,8 +1088,8 @@ packages: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.0.0: - resolution: {integrity: sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==} + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint@9.9.1: @@ -1569,6 +1559,10 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + minimatch@10.0.1: + resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} + engines: {node: 20 || >=22} + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -2064,18 +2058,27 @@ packages: peerDependencies: typescript: '>=4.2.0' + ts-api-utils@2.0.1: + resolution: {integrity: sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-declaration-location@1.0.5: + resolution: {integrity: sha512-WqmlO9IoeYwCqJ2E9kHMcY9GZhhfLYItC3VnHDlPOrg6nNdUWS4wn4hhDZUPt60m1EvtjPIZyprTjpI992Bgzw==} + peerDependencies: + typescript: '>=4.0.0' + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typescript-eslint@8.2.0: - resolution: {integrity: sha512-DmnqaPcML0xYwUzgNbM1XaKXpEb7BShYf2P1tkUmmcl8hyeG7Pj08Er7R9bNy6AufabywzJcOybQAtnD/c9DGw==} + typescript-eslint@8.26.0: + resolution: {integrity: sha512-PtVz9nAnuNJuAVeUFvwztjuUgSnJInODAUx47VDwWPXzd5vismPOtPtt83tzNXyOjVQbPRp786D6WFW/M2koIA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' typescript@5.5.4: resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} @@ -2724,16 +2727,16 @@ snapshots: dependencies: acorn: 8.14.0 - '@sveltejs/eslint-config@8.1.0(@stylistic/eslint-plugin-js@1.8.0(eslint@9.9.1))(eslint-config-prettier@9.1.0(eslint@9.9.1))(eslint-plugin-n@17.9.0(eslint@9.9.1))(eslint-plugin-svelte@2.38.0(eslint@9.9.1)(svelte@packages+svelte))(eslint@9.9.1)(typescript-eslint@8.2.0(eslint@9.9.1)(typescript@5.5.4))(typescript@5.5.4)': + '@sveltejs/eslint-config@8.1.0(@stylistic/eslint-plugin-js@1.8.0(eslint@9.9.1))(eslint-config-prettier@9.1.0(eslint@9.9.1))(eslint-plugin-n@17.16.1(eslint@9.9.1)(typescript@5.5.4))(eslint-plugin-svelte@2.38.0(eslint@9.9.1)(svelte@packages+svelte))(eslint@9.9.1)(typescript-eslint@8.26.0(eslint@9.9.1)(typescript@5.5.4))(typescript@5.5.4)': dependencies: '@stylistic/eslint-plugin-js': 1.8.0(eslint@9.9.1) eslint: 9.9.1 eslint-config-prettier: 9.1.0(eslint@9.9.1) - eslint-plugin-n: 17.9.0(eslint@9.9.1) + eslint-plugin-n: 17.16.1(eslint@9.9.1)(typescript@5.5.4) eslint-plugin-svelte: 2.38.0(eslint@9.9.1)(svelte@packages+svelte) globals: 15.14.0 typescript: 5.5.4 - typescript-eslint: 8.2.0(eslint@9.9.1)(typescript@5.5.4) + typescript-eslint: 8.26.0(eslint@9.9.1)(typescript@5.5.4) '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: @@ -2787,86 +2790,82 @@ snapshots: '@types/semver@7.5.6': {} - '@typescript-eslint/eslint-plugin@8.2.0(@typescript-eslint/parser@8.2.0(eslint@9.9.1)(typescript@5.5.4))(eslint@9.9.1)(typescript@5.5.4)': + '@typescript-eslint/eslint-plugin@8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.9.1)(typescript@5.5.4))(eslint@9.9.1)(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.2.0(eslint@9.9.1)(typescript@5.5.4) - '@typescript-eslint/scope-manager': 8.2.0 - '@typescript-eslint/type-utils': 8.2.0(eslint@9.9.1)(typescript@5.5.4) - '@typescript-eslint/utils': 8.2.0(eslint@9.9.1)(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 8.2.0 + '@typescript-eslint/parser': 8.26.0(eslint@9.9.1)(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.26.0 + '@typescript-eslint/type-utils': 8.26.0(eslint@9.9.1)(typescript@5.5.4) + '@typescript-eslint/utils': 8.26.0(eslint@9.9.1)(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.26.0 eslint: 9.9.1 graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.5.4) - optionalDependencies: + ts-api-utils: 2.0.1(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.2.0(eslint@9.9.1)(typescript@5.5.4)': + '@typescript-eslint/parser@8.26.0(eslint@9.9.1)(typescript@5.5.4)': dependencies: - '@typescript-eslint/scope-manager': 8.2.0 - '@typescript-eslint/types': 8.2.0 - '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 8.2.0 + '@typescript-eslint/scope-manager': 8.26.0 + '@typescript-eslint/types': 8.26.0 + '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.26.0 debug: 4.4.0 eslint: 9.9.1 - optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.2.0': + '@typescript-eslint/scope-manager@8.26.0': dependencies: - '@typescript-eslint/types': 8.2.0 - '@typescript-eslint/visitor-keys': 8.2.0 + '@typescript-eslint/types': 8.26.0 + '@typescript-eslint/visitor-keys': 8.26.0 - '@typescript-eslint/type-utils@8.2.0(eslint@9.9.1)(typescript@5.5.4)': + '@typescript-eslint/type-utils@8.26.0(eslint@9.9.1)(typescript@5.5.4)': dependencies: - '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) - '@typescript-eslint/utils': 8.2.0(eslint@9.9.1)(typescript@5.5.4) + '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.5.4) + '@typescript-eslint/utils': 8.26.0(eslint@9.9.1)(typescript@5.5.4) debug: 4.4.0 - ts-api-utils: 1.3.0(typescript@5.5.4) - optionalDependencies: + eslint: 9.9.1 + ts-api-utils: 2.0.1(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - - eslint - supports-color - '@typescript-eslint/types@8.2.0': {} + '@typescript-eslint/types@8.26.0': {} - '@typescript-eslint/typescript-estree@8.2.0(typescript@5.5.4)': + '@typescript-eslint/typescript-estree@8.26.0(typescript@5.5.4)': dependencies: - '@typescript-eslint/types': 8.2.0 - '@typescript-eslint/visitor-keys': 8.2.0 + '@typescript-eslint/types': 8.26.0 + '@typescript-eslint/visitor-keys': 8.26.0 debug: 4.4.0 - globby: 11.1.0 + fast-glob: 3.3.2 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.1 - ts-api-utils: 1.3.0(typescript@5.5.4) - optionalDependencies: + ts-api-utils: 2.0.1(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.2.0(eslint@9.9.1)(typescript@5.5.4)': + '@typescript-eslint/utils@8.26.0(eslint@9.9.1)(typescript@5.5.4)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) - '@typescript-eslint/scope-manager': 8.2.0 - '@typescript-eslint/types': 8.2.0 - '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.26.0 + '@typescript-eslint/types': 8.26.0 + '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.5.4) eslint: 9.9.1 + typescript: 5.5.4 transitivePeerDependencies: - supports-color - - typescript - '@typescript-eslint/visitor-keys@8.2.0': + '@typescript-eslint/visitor-keys@8.26.0': dependencies: - '@typescript-eslint/types': 8.2.0 - eslint-visitor-keys: 3.4.3 + '@typescript-eslint/types': 8.26.0 + eslint-visitor-keys: 4.2.0 '@vitest/coverage-v8@2.0.5(vitest@2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: @@ -3200,9 +3199,10 @@ snapshots: eslint-plugin-lube@0.4.3: {} - eslint-plugin-n@17.9.0(eslint@9.9.1): + eslint-plugin-n@17.16.1(eslint@9.9.1)(typescript@5.5.4): dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) + '@typescript-eslint/utils': 8.26.0(eslint@9.9.1)(typescript@5.5.4) enhanced-resolve: 5.18.1 eslint: 9.9.1 eslint-plugin-es-x: 7.8.0(eslint@9.9.1) @@ -3211,6 +3211,10 @@ snapshots: ignore: 5.3.2 minimatch: 9.0.5 semver: 7.7.1 + ts-declaration-location: 1.0.5(typescript@5.5.4) + transitivePeerDependencies: + - supports-color + - typescript eslint-plugin-svelte@2.38.0(eslint@9.9.1)(svelte@packages+svelte): dependencies: @@ -3245,7 +3249,7 @@ snapshots: eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.0.0: {} + eslint-visitor-keys@4.2.0: {} eslint@9.9.1: dependencies: @@ -3263,7 +3267,7 @@ snapshots: debug: 4.4.0 escape-string-regexp: 4.0.0 eslint-scope: 8.0.2 - eslint-visitor-keys: 4.0.0 + eslint-visitor-keys: 4.2.0 espree: 10.1.0 esquery: 1.5.0 esutils: 2.0.3 @@ -3292,7 +3296,7 @@ snapshots: dependencies: acorn: 8.14.0 acorn-jsx: 5.3.2(acorn@8.14.0) - eslint-visitor-keys: 4.0.0 + eslint-visitor-keys: 4.2.0 espree@9.6.1: dependencies: @@ -3742,6 +3746,10 @@ snapshots: dependencies: mime-db: 1.52.0 + minimatch@10.0.1: + dependencies: + brace-expansion: 2.0.1 + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -4161,19 +4169,27 @@ snapshots: dependencies: typescript: 5.5.4 + ts-api-utils@2.0.1(typescript@5.5.4): + dependencies: + typescript: 5.5.4 + + ts-declaration-location@1.0.5(typescript@5.5.4): + dependencies: + minimatch: 10.0.1 + typescript: 5.5.4 + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - typescript-eslint@8.2.0(eslint@9.9.1)(typescript@5.5.4): + typescript-eslint@8.26.0(eslint@9.9.1)(typescript@5.5.4): dependencies: - '@typescript-eslint/eslint-plugin': 8.2.0(@typescript-eslint/parser@8.2.0(eslint@9.9.1)(typescript@5.5.4))(eslint@9.9.1)(typescript@5.5.4) - '@typescript-eslint/parser': 8.2.0(eslint@9.9.1)(typescript@5.5.4) - '@typescript-eslint/utils': 8.2.0(eslint@9.9.1)(typescript@5.5.4) - optionalDependencies: + '@typescript-eslint/eslint-plugin': 8.26.0(@typescript-eslint/parser@8.26.0(eslint@9.9.1)(typescript@5.5.4))(eslint@9.9.1)(typescript@5.5.4) + '@typescript-eslint/parser': 8.26.0(eslint@9.9.1)(typescript@5.5.4) + '@typescript-eslint/utils': 8.26.0(eslint@9.9.1)(typescript@5.5.4) + eslint: 9.9.1 typescript: 5.5.4 transitivePeerDependencies: - - eslint - supports-color typescript@5.5.4: {} From 181fb2ad496dcd7e1915032b553115c805034bbf Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Wed, 5 Mar 2025 00:36:26 +0100 Subject: [PATCH 0224/1429] fix: correctly set `is_updating` before flushing root effects (#15442) * fix: correctly set `is_updating` before flushing root effects * rename for consistency with update_effect * use var --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/proud-poems-brake.md | 5 +++ .../svelte/src/internal/client/runtime.js | 4 ++ packages/svelte/tests/signals/test.ts | 41 +++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 .changeset/proud-poems-brake.md diff --git a/.changeset/proud-poems-brake.md b/.changeset/proud-poems-brake.md new file mode 100644 index 0000000000..3f4e076e22 --- /dev/null +++ b/.changeset/proud-poems-brake.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly set `is_updating` before flushing root effects diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 486c819f36..9f721f9ec4 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -644,8 +644,11 @@ function infinite_loop_guard() { } function flush_queued_root_effects() { + var was_updating_effect = is_updating_effect; + try { var flush_count = 0; + is_updating_effect = true; while (queued_root_effects.length > 0) { if (flush_count++ > 1000) { @@ -670,6 +673,7 @@ function flush_queued_root_effects() { } } finally { is_flushing = false; + is_updating_effect = was_updating_effect; last_scheduled_effect = null; if (DEV) { diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index 046f833e0e..ef4cf16d3b 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -1135,4 +1135,45 @@ describe('signals', () => { destroy(); }; }); + + test('unowned deriveds correctly update', () => { + const log: any[] = []; + + return () => { + const a = state(0); + const b = state(0); + const c = derived(() => { + return $.get(a); + }); + const d = derived(() => { + return $.get(b); + }); + + const destroy = effect_root(() => { + const e = derived(() => { + return $.get(c) === 1 && $.get(d) === 1; + }); + render_effect(() => { + log.push($.get(e)); + }); + }); + + assert.deepEqual(log, [false]); + + set(a, 1); + set(b, 1); + + flushSync(); + + assert.deepEqual(log, [false, true]); + + set(b, 9); + + flushSync(); + + assert.deepEqual(log, [false, true, false]); + + destroy(); + }; + }); }); From 016421533d2fca452597e9b955dccc53880599a5 Mon Sep 17 00:00:00 2001 From: Matthew Carroll <53098729+matthewCmatt@users.noreply.github.com> Date: Tue, 4 Mar 2025 21:53:46 -0500 Subject: [PATCH 0225/1429] Fix Rune phonetic spelling (#15445) --- documentation/docs/02-runes/01-what-are-runes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/02-runes/01-what-are-runes.md b/documentation/docs/02-runes/01-what-are-runes.md index dc163ebdf1..59c371eb49 100644 --- a/documentation/docs/02-runes/01-what-are-runes.md +++ b/documentation/docs/02-runes/01-what-are-runes.md @@ -2,7 +2,7 @@ title: What are runes? --- -> [!NOTE] **rune** /ro͞on/ _noun_ +> [!NOTE] **rune** /ruːn/ _noun_ > > A letter or mark used as a mystical or magic symbol. From 3b465a717283a416a368071f07984285b8ea3f38 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 4 Mar 2025 21:55:34 -0500 Subject: [PATCH 0226/1429] Version Packages (#15444) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/proud-poems-brake.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/proud-poems-brake.md diff --git a/.changeset/proud-poems-brake.md b/.changeset/proud-poems-brake.md deleted file mode 100644 index 3f4e076e22..0000000000 --- a/.changeset/proud-poems-brake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly set `is_updating` before flushing root effects diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 5f031f8f99..8327013969 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.22.2 + +### Patch Changes + +- fix: correctly set `is_updating` before flushing root effects ([#15442](https://github.com/sveltejs/svelte/pull/15442)) + ## 5.22.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 3acd327682..478668a602 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.22.1", + "version": "5.22.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 4cea5c4b7f..d00562ea4e 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.22.1'; +export const VERSION = '5.22.2'; export const PUBLIC_VERSION = '5'; From e591e872aa413282abb51995b97c7c5161693077 Mon Sep 17 00:00:00 2001 From: Pavel Nedrigailov <1223112+shadow-identity@users.noreply.github.com> Date: Wed, 5 Mar 2025 03:56:28 +0100 Subject: [PATCH 0227/1429] Update 01-basic-markup.md with new svelte-ignore syntax (#15394) --- documentation/docs/03-template-syntax/01-basic-markup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/03-template-syntax/01-basic-markup.md b/documentation/docs/03-template-syntax/01-basic-markup.md index b41dc187c3..5e8b4342d3 100644 --- a/documentation/docs/03-template-syntax/01-basic-markup.md +++ b/documentation/docs/03-template-syntax/01-basic-markup.md @@ -185,7 +185,7 @@ You can use HTML comments inside components. Comments beginning with `svelte-ignore` disable warnings for the next block of markup. Usually, these are accessibility warnings; make sure that you're disabling them for a good reason. ```svelte -<!-- svelte-ignore a11y-autofocus --> +<!-- svelte-ignore a11y_autofocus --> <input bind:value={name} autofocus /> ``` From 3fc2007836d25b2e13d960ffa8ea26b5b197508e Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 5 Mar 2025 04:19:08 -0500 Subject: [PATCH 0228/1429] fix: run effect roots in tree order (#15446) * process effect roots in tree order * bring test over * add test * changeset * tidy --- .changeset/shy-falcons-occur.md | 5 ++++ .../src/internal/client/reactivity/effects.js | 24 ++++++++++++------- .../svelte/src/internal/client/runtime.js | 14 ++++------- .../samples/effect-root-5/_config.js | 17 +++++++++++++ .../samples/effect-root-5/main.svelte | 23 ++++++++++++++++++ .../samples/toStore-teardown/_config.js | 13 ++++++++++ .../samples/toStore-teardown/child.svelte | 11 +++++++++ .../samples/toStore-teardown/main.svelte | 15 ++++++++++++ 8 files changed, 104 insertions(+), 18 deletions(-) create mode 100644 .changeset/shy-falcons-occur.md create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-root-5/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/effect-root-5/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/toStore-teardown/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/toStore-teardown/child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/toStore-teardown/main.svelte diff --git a/.changeset/shy-falcons-occur.md b/.changeset/shy-falcons-occur.md new file mode 100644 index 0000000000..b16e28549e --- /dev/null +++ b/.changeset/shy-falcons-occur.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: run effect roots in tree order diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 28589ce94d..468bb94ab4 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -82,13 +82,12 @@ function push_effect(effect, parent_effect) { * @returns {Effect} */ function create_effect(type, fn, sync, push = true) { - var is_root = (type & ROOT_EFFECT) !== 0; - var parent_effect = active_effect; + var parent = active_effect; if (DEV) { // Ensure the parent is never an inspect effect - while (parent_effect !== null && (parent_effect.f & INSPECT_EFFECT) !== 0) { - parent_effect = parent_effect.parent; + while (parent !== null && (parent.f & INSPECT_EFFECT) !== 0) { + parent = parent.parent; } } @@ -103,7 +102,7 @@ function create_effect(type, fn, sync, push = true) { fn, last: null, next: null, - parent: is_root ? null : parent_effect, + parent, prev: null, teardown: null, transitions: null, @@ -136,9 +135,9 @@ function create_effect(type, fn, sync, push = true) { effect.teardown === null && (effect.f & (EFFECT_HAS_DERIVED | BOUNDARY_EFFECT)) === 0; - if (!inert && !is_root && push) { - if (parent_effect !== null) { - push_effect(effect, parent_effect); + if (!inert && push) { + if (parent !== null) { + push_effect(effect, parent); } // if we're in a derived, add the effect there too @@ -391,7 +390,14 @@ export function destroy_effect_children(signal, remove_dom = false) { while (effect !== null) { var next = effect.next; - destroy_effect(effect, remove_dom); + + if ((effect.f & ROOT_EFFECT) !== 0) { + // this is now an independent root + effect.parent = null; + } else { + destroy_effect(effect, remove_dom); + } + effect = next; } } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 9f721f9ec4..bbe4dc3d9b 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -661,13 +661,7 @@ function flush_queued_root_effects() { queued_root_effects = []; for (var i = 0; i < length; i++) { - var root = root_effects[i]; - - if ((root.f & CLEAN) === 0) { - root.f ^= CLEAN; - } - - var collected_effects = process_effects(root); + var collected_effects = process_effects(root_effects[i]); flush_queued_effects(collected_effects); } } @@ -759,11 +753,12 @@ function process_effects(root) { /** @type {Effect[]} */ var effects = []; - var effect = root.first; + /** @type {Effect | null} */ + var effect = root; while (effect !== null) { var flags = effect.f; - var is_branch = (flags & BRANCH_EFFECT) !== 0; + var is_branch = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) !== 0; var is_skippable_branch = is_branch && (flags & CLEAN) !== 0; if (!is_skippable_branch && (flags & INERT) === 0) { @@ -788,6 +783,7 @@ function process_effects(root) { } } + /** @type {Effect | null} */ var child = effect.first; if (child !== null) { diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-5/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-root-5/_config.js new file mode 100644 index 0000000000..260c757e3d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-root-5/_config.js @@ -0,0 +1,17 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target, logs }) { + const [b1, b2] = target.querySelectorAll('button'); + + flushSync(() => b1.click()); + assert.deepEqual(logs, [0, 1]); + + flushSync(() => b1.click()); + assert.deepEqual(logs, [0, 1, 2]); + + flushSync(() => b2.click()); + assert.deepEqual(logs, [0, 1, 2]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/effect-root-5/main.svelte b/packages/svelte/tests/runtime-runes/samples/effect-root-5/main.svelte new file mode 100644 index 0000000000..06655a5362 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/effect-root-5/main.svelte @@ -0,0 +1,23 @@ +<script> + let obj = $state({ count: 0 }); + + $effect.root(() => { + let teardown; + + $effect.pre(() => { + if (obj) { + teardown ??= $effect.root(() => { + $effect.pre(() => { + console.log(obj.count); + }); + }); + } else { + teardown?.(); + teardown = null; + } + }); + }); +</script> + +<button onclick={() => ((obj ??= { count: 0 }).count += 1)}>+1</button> +<button onclick={() => (obj = null)}>null</button> diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-teardown/_config.js b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/_config.js new file mode 100644 index 0000000000..95904f011f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/_config.js @@ -0,0 +1,13 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + let [, btn2] = target.querySelectorAll('button'); + + btn2.click(); + flushSync(); + + assert.htmlEqual(target.innerHTML, `<button>Set data</button><button>Clear data</button>`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-teardown/child.svelte b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/child.svelte new file mode 100644 index 0000000000..f1b1b7b497 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/child.svelte @@ -0,0 +1,11 @@ +<script> + import { toStore } from 'svelte/store' + + let { data } = $props() + const currentValue = toStore(() => data.value) +</script> + +<p> + Current value: + <span>{$currentValue}</span> +</p> diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-teardown/main.svelte b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/main.svelte new file mode 100644 index 0000000000..7d36dd95cb --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/toStore-teardown/main.svelte @@ -0,0 +1,15 @@ +<script> + import Child from './child.svelte' + + let data = $state({ value: 'hello' }); + + const setData = () => (data = { value: 'hello' }) + const clearData = () => (data = undefined) +</script> + +<button onclick={setData}>Set data</button> +<button onclick={clearData}>Clear data</button> + +{#if data} + <Child {data} /> +{/if} From 0abd7f2a7f08d400c622d3e0a6da12cd041bd9f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 10:59:54 +0000 Subject: [PATCH 0229/1429] Version Packages (#15447) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/shy-falcons-occur.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/shy-falcons-occur.md diff --git a/.changeset/shy-falcons-occur.md b/.changeset/shy-falcons-occur.md deleted file mode 100644 index b16e28549e..0000000000 --- a/.changeset/shy-falcons-occur.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: run effect roots in tree order diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 8327013969..e1bc27b51d 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.22.3 + +### Patch Changes + +- fix: run effect roots in tree order ([#15446](https://github.com/sveltejs/svelte/pull/15446)) + ## 5.22.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 478668a602..1657b59577 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.22.2", + "version": "5.22.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index d00562ea4e..29b56b5b2e 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.22.2'; +export const VERSION = '5.22.3'; export const PUBLIC_VERSION = '5'; From 76f5ecfdab2b65db9234d7fc2b59621a7b7c2dc5 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 5 Mar 2025 11:26:34 -0500 Subject: [PATCH 0230/1429] fix: never deduplicate expressions in templates (#15451) --- .changeset/five-wolves-swim.md | 5 ++ .../client/visitors/shared/utils.js | 46 ------------------- .../runtime-runes/samples/random/_config.js | 8 ++++ .../runtime-runes/samples/random/main.svelte | 6 +++ .../_expected/client/main.svelte.js | 6 +-- 5 files changed, 22 insertions(+), 49 deletions(-) create mode 100644 .changeset/five-wolves-swim.md create mode 100644 packages/svelte/tests/runtime-runes/samples/random/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/random/main.svelte diff --git a/.changeset/five-wolves-swim.md b/.changeset/five-wolves-swim.md new file mode 100644 index 0000000000..92178bed90 --- /dev/null +++ b/.changeset/five-wolves-swim.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: never deduplicate expressions in templates diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index c25ef3ab50..df6308d631 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -26,55 +26,9 @@ export function memoize_expression(state, value) { * @param {Expression} value */ export function get_expression_id(state, value) { - for (let i = 0; i < state.expressions.length; i += 1) { - if (compare_expressions(state.expressions[i], value)) { - return b.id(`$${i}`); - } - } - return b.id(`$${state.expressions.push(value) - 1}`); } -/** - * Returns true of two expressions have an identical AST shape - * @param {Expression} a - * @param {Expression} b - */ -function compare_expressions(a, b) { - if (a.type !== b.type) { - return false; - } - - for (const key in a) { - if (key === 'type' || key === 'metadata' || key === 'loc' || key === 'start' || key === 'end') { - continue; - } - - const va = /** @type {any} */ (a)[key]; - const vb = /** @type {any} */ (b)[key]; - - if ((typeof va === 'object') !== (typeof vb === 'object')) { - return false; - } - - if (typeof va !== 'object' || va === null || vb === null) { - if (va !== vb) return false; - } else if (Array.isArray(va)) { - if (va.length !== vb.length) { - return false; - } - - if (va.some((v, i) => !compare_expressions(v, vb[i]))) { - return false; - } - } else if (!compare_expressions(va, vb)) { - return false; - } - } - - return true; -} - /** * @param {Array<AST.Text | AST.ExpressionTag>} values * @param {(node: AST.SvelteNode, state: any) => any} visit diff --git a/packages/svelte/tests/runtime-runes/samples/random/_config.js b/packages/svelte/tests/runtime-runes/samples/random/_config.js new file mode 100644 index 0000000000..368dd20c6c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/random/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + test({ assert, target }) { + const [p1, p2] = target.querySelectorAll('p'); + assert.notEqual(p1.textContent, p2.textContent); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/random/main.svelte b/packages/svelte/tests/runtime-runes/samples/random/main.svelte new file mode 100644 index 0000000000..e1ec0b5649 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/random/main.svelte @@ -0,0 +1,6 @@ +<script> + let { m = 1 } = $props(); +</script> + +<p>{(Math.random() * m).toFixed(10)}</p> +<p>{(Math.random() * m).toFixed(10)}</p> diff --git a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js index d97a58bf40..219db6ffd5 100644 --- a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js +++ b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js @@ -21,13 +21,13 @@ export default function Main($$anchor) { $.template_effect(() => $.set_custom_element_data(custom_element_1, 'fooBar', y())); $.template_effect( - ($0) => { + ($0, $1) => { $.set_attribute(div, 'foobar', x); $.set_attribute(svg, 'viewBox', x); $.set_attribute(div_1, 'foobar', $0); - $.set_attribute(svg_1, 'viewBox', $0); + $.set_attribute(svg_1, 'viewBox', $1); }, - [y] + [y, y] ); $.append($$anchor, fragment); From 43ff3047ac525742c020565781b48dcd6bd06c39 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 11:37:21 -0500 Subject: [PATCH 0231/1429] Version Packages (#15452) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/five-wolves-swim.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/five-wolves-swim.md diff --git a/.changeset/five-wolves-swim.md b/.changeset/five-wolves-swim.md deleted file mode 100644 index 92178bed90..0000000000 --- a/.changeset/five-wolves-swim.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: never deduplicate expressions in templates diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index e1bc27b51d..0ff6b62fe0 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.22.4 + +### Patch Changes + +- fix: never deduplicate expressions in templates ([#15451](https://github.com/sveltejs/svelte/pull/15451)) + ## 5.22.3 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 1657b59577..1f95811cd5 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.22.3", + "version": "5.22.4", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 29b56b5b2e..845375314f 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.22.3'; +export const VERSION = '5.22.4'; export const PUBLIC_VERSION = '5'; From 2d3818463a6ef4b888d6ca387725eb45e2143059 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 5 Mar 2025 11:47:59 -0500 Subject: [PATCH 0232/1429] chore: check namespace inside set attributes (#15443) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * set_attributes for <svelte:element> * changeset * Update .changeset/wise-hats-wonder.md Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> * add changeset * remove `chore` changeset — no need for non-user-facing changes to appear in changelog * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js * determine element traits inside set_attributes * unused * stash lookup --------- Co-authored-by: adiguba <frederic.martini@gmail.com> Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- .changeset/quiet-baboons-listen.md | 5 ++ .../client/visitors/RegularElement.js | 11 +---- .../client/visitors/SvelteElement.js | 4 +- .../client/visitors/shared/element.js | 8 +-- packages/svelte/src/constants.js | 1 + .../client/dom/elements/attributes.js | 49 +++++++++++-------- 6 files changed, 37 insertions(+), 41 deletions(-) create mode 100644 .changeset/quiet-baboons-listen.md diff --git a/.changeset/quiet-baboons-listen.md b/.changeset/quiet-baboons-listen.md new file mode 100644 index 0000000000..eb5b4cc699 --- /dev/null +++ b/.changeset/quiet-baboons-listen.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +fix: respect `svelte-ignore hydration_attribute_changed` on elements with spread attributes diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 434b49caa1..3dd3039213 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -221,16 +221,7 @@ export function RegularElement(node, context) { if (has_spread) { const attributes_id = b.id(context.state.scope.generate('attributes')); - build_set_attributes( - attributes, - class_directives, - context, - node, - node_id, - attributes_id, - (node.metadata.svg || node.metadata.mathml || is_custom_element_node(node)) && b.true, - is_custom_element_node(node) && b.true - ); + build_set_attributes(attributes, class_directives, context, node, node_id, attributes_id); // If value binding exists, that one takes care of calling $.init_select if (node.name === 'select' && !bindings.has('value')) { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js index ac284c818d..3250c24392 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js @@ -114,9 +114,7 @@ export function SvelteElement(node, context) { inner_context, node, element_id, - attributes_id, - b.binary('===', b.member(element_id, 'namespaceURI'), b.id('$.NAMESPACE_SVG')), - b.call(b.member(b.member(element_id, 'nodeName'), 'includes'), b.literal('-')) + attributes_id ); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index 81a4b45288..db8f2e4aa0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -17,8 +17,6 @@ import { build_template_chunk, get_expression_id } from './utils.js'; * @param {AST.RegularElement | AST.SvelteElement} element * @param {Identifier} element_id * @param {Identifier} attributes_id - * @param {false | Expression} preserve_attribute_case - * @param {false | Expression} is_custom_element */ export function build_set_attributes( attributes, @@ -26,9 +24,7 @@ export function build_set_attributes( context, element, element_id, - attributes_id, - preserve_attribute_case, - is_custom_element + attributes_id ) { let is_dynamic = false; @@ -91,8 +87,6 @@ export function build_set_attributes( element.metadata.scoped && context.state.analysis.css.hash !== '' && b.literal(context.state.analysis.css.hash), - preserve_attribute_case, - is_custom_element, is_ignored(element, 'hydration_attribute_changed') && b.true ); diff --git a/packages/svelte/src/constants.js b/packages/svelte/src/constants.js index 03fddc5ebd..8861e440fc 100644 --- a/packages/svelte/src/constants.js +++ b/packages/svelte/src/constants.js @@ -33,6 +33,7 @@ export const UNINITIALIZED = Symbol(); export const FILENAME = Symbol('filename'); export const HMR = Symbol('hmr'); +export const NAMESPACE_HTML = 'http://www.w3.org/1999/xhtml'; export const NAMESPACE_SVG = 'http://www.w3.org/2000/svg'; export const NAMESPACE_MATHML = 'http://www.w3.org/1998/Math/MathML'; diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index dd408dcf87..44e67155fc 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -15,10 +15,14 @@ import { } from '../../runtime.js'; import { clsx } from '../../../shared/attributes.js'; import { set_class } from './class.js'; +import { NAMESPACE_HTML } from '../../../../constants.js'; export const CLASS = Symbol('class'); export const STYLE = Symbol('style'); +const IS_CUSTOM_ELEMENT = Symbol('is custom element'); +const IS_HTML = Symbol('is html'); + /** * The value/checked attribute in the template actually corresponds to the defaultValue property, so we need * to remove it upon hydration to avoid a bug when someone resets the form value. @@ -63,8 +67,7 @@ export function remove_input_defaults(input) { * @param {any} value */ export function set_value(element, value) { - // @ts-expect-error - var attributes = (element.__attributes ??= {}); + var attributes = get_attributes(element); if ( attributes.value === @@ -87,8 +90,7 @@ export function set_value(element, value) { * @param {boolean} checked */ export function set_checked(element, checked) { - // @ts-expect-error - var attributes = (element.__attributes ??= {}); + var attributes = get_attributes(element); if ( attributes.checked === @@ -151,8 +153,7 @@ export function set_default_value(element, value) { * @param {boolean} [skip_warning] */ export function set_attribute(element, attribute, value, skip_warning) { - // @ts-expect-error - var attributes = (element.__attributes ??= {}); + var attributes = get_attributes(element); if (hydrating) { attributes[attribute] = element.getAttribute(attribute); @@ -261,20 +262,15 @@ export function set_custom_element_data(node, prop, value) { * @param {Record<string | symbol, any> | undefined} prev * @param {Record<string | symbol, any>} next New attributes - this function mutates this object * @param {string} [css_hash] - * @param {boolean} [preserve_attribute_case] - * @param {boolean} [is_custom_element] * @param {boolean} [skip_warning] * @returns {Record<string, any>} */ -export function set_attributes( - element, - prev, - next, - css_hash, - preserve_attribute_case = false, - is_custom_element = false, - skip_warning = false -) { +export function set_attributes(element, prev, next, css_hash, skip_warning = false) { + var attributes = get_attributes(element); + + var is_custom_element = attributes[IS_CUSTOM_ELEMENT]; + var preserve_attribute_case = !attributes[IS_HTML]; + // If we're hydrating but the custom element is from Svelte, and it already scaffolded, // then it might run block logic in hydration mode, which we have to prevent. let is_hydrating_custom_element = hydrating && is_custom_element; @@ -299,9 +295,6 @@ export function set_attributes( var setters = get_setters(element); - // @ts-expect-error - var attributes = /** @type {Record<string, unknown>} **/ (element.__attributes ??= {}); - // since key is captured we use const for (const key in next) { // let instead of var because referenced in a closure @@ -432,7 +425,7 @@ export function set_attributes( // @ts-ignore element[name] = value; } else if (typeof value !== 'function') { - set_attribute(element, name, value); + set_attribute(element, name, value, skip_warning); } } if (key === 'style' && '__styles' in element) { @@ -448,6 +441,20 @@ export function set_attributes( return current; } +/** + * + * @param {Element} element + */ +function get_attributes(element) { + return /** @type {Record<string | symbol, unknown>} **/ ( + // @ts-expect-error + element.__attributes ??= { + [IS_CUSTOM_ELEMENT]: element.nodeName.includes('-'), + [IS_HTML]: element.namespaceURI === NAMESPACE_HTML + } + ); +} + /** @type {Map<string, string[]>} */ var setters_cache = new Map(); From 1efad3f6e23442f296aa9910762512a42e450b87 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 5 Mar 2025 12:26:18 -0500 Subject: [PATCH 0233/1429] chore: add monitoring to github actions (#15436) * chore: add monitoring to github actions * try this --- .github/workflows/ci.yml | 3 +++ .github/workflows/ecosystem-ci-trigger.yml | 1 + .github/workflows/pkg.pr.new-comment.yml | 1 + .github/workflows/pkg.pr.new.yml | 2 ++ .github/workflows/release.yml | 1 + 5 files changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2bcb08848..cf73a1f6cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,7 @@ env: jobs: Tests: + permissions: {} runs-on: ${{ matrix.os }} timeout-minutes: 15 strategy: @@ -41,6 +42,7 @@ jobs: env: CI: true Lint: + permissions: {} runs-on: ubuntu-latest timeout-minutes: 5 steps: @@ -61,6 +63,7 @@ jobs: if: (${{ success() }} || ${{ failure() }}) # ensures this step runs even if previous steps fail run: pnpm build && { [ "`git status --porcelain=v1`" == "" ] || (echo "Generated types have changed — please regenerate types locally with `cd packages/svelte && pnpm generate:types` and commit the changes after you have reviewed them"; git diff; exit 1); } Benchmarks: + permissions: {} runs-on: ubuntu-latest timeout-minutes: 15 steps: diff --git a/.github/workflows/ecosystem-ci-trigger.yml b/.github/workflows/ecosystem-ci-trigger.yml index ce7bf04136..71df3242e8 100644 --- a/.github/workflows/ecosystem-ci-trigger.yml +++ b/.github/workflows/ecosystem-ci-trigger.yml @@ -9,6 +9,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'sveltejs/svelte' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/ecosystem-ci run') steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - uses: actions/github-script@v6 with: script: | diff --git a/.github/workflows/pkg.pr.new-comment.yml b/.github/workflows/pkg.pr.new-comment.yml index 1698a456d3..b1fba0b04b 100644 --- a/.github/workflows/pkg.pr.new-comment.yml +++ b/.github/workflows/pkg.pr.new-comment.yml @@ -11,6 +11,7 @@ jobs: name: 'Update comment' runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - name: Download artifact uses: actions/download-artifact@v4 with: diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml index 99f8153517..90d219faae 100644 --- a/.github/workflows/pkg.pr.new.yml +++ b/.github/workflows/pkg.pr.new.yml @@ -3,6 +3,8 @@ on: [push, pull_request] jobs: build: + permissions: {} + runs-on: ubuntu-latest steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1daef0b89c..6debe5662a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,6 +17,7 @@ jobs: name: Release runs-on: ubuntu-latest steps: + - uses: GitHubSecurityLab/actions-permissions/monitor@v1 - name: Checkout Repo uses: actions/checkout@v4 with: From 2f685c1dbadcf7e158282109f746400834f5eaf5 Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Wed, 5 Mar 2025 20:20:56 +0100 Subject: [PATCH 0234/1429] fix: spreading style is not consistent with attribute (#15323) * style must be set via set_attribute * test * changeset * add empty string and null in test * explanatory comment * this is now redundant, set_attribute takes care of it * drive-by * tweak changeset --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/real-cameras-attack.md | 5 ++ .../client/dom/elements/attributes.js | 17 ++--- .../samples/style-update/_config.js | 64 +++++++++++++++++++ .../samples/style-update/main.svelte | 9 +++ 4 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 .changeset/real-cameras-attack.md create mode 100644 packages/svelte/tests/runtime-runes/samples/style-update/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/style-update/main.svelte diff --git a/.changeset/real-cameras-attack.md b/.changeset/real-cameras-attack.md new file mode 100644 index 0000000000..35e2764785 --- /dev/null +++ b/.changeset/real-cameras-attack.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: always use `setAttribute` when setting `style` diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 44e67155fc..cebc9173ba 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -218,6 +218,7 @@ export function set_custom_element_data(node, prop, value) { // or effect var previous_reaction = active_reaction; var previous_effect = active_effect; + // If we're hydrating but the custom element is from Svelte, and it already scaffolded, // then it might run block logic in hydration mode, which we have to prevent. let was_hydrating = hydrating; @@ -227,17 +228,20 @@ export function set_custom_element_data(node, prop, value) { set_active_reaction(null); set_active_effect(null); + try { if ( + // `style` should use `set_attribute` rather than the setter + prop !== 'style' && // Don't compute setters for custom elements while they aren't registered yet, // because during their upgrade/instantiation they might add more setters. // Instead, fall back to a simple "an object, then set as property" heuristic. - setters_cache.has(node.nodeName) || + (setters_cache.has(node.nodeName) || // customElements may not be available in browser extension contexts !customElements || customElements.get(node.tagName.toLowerCase()) ? get_setters(node).includes(prop) - : value && typeof value === 'object' + : value && typeof value === 'object') ) { // @ts-expect-error node[prop] = value; @@ -378,8 +382,9 @@ export function set_attributes(element, prev, next, css_hash, skip_warning = fal // @ts-ignore element[`__${event_name}`] = undefined; } - } else if (key === 'style' && value != null) { - element.style.cssText = value + ''; + } else if (key === 'style') { + // avoid using the setter + set_attribute(element, key, value); } else if (key === 'autofocus') { autofocus(/** @type {HTMLElement} */ (element), Boolean(value)); } else if (!is_custom_element && (key === '__value' || (key === 'value' && value != null))) { @@ -428,10 +433,6 @@ export function set_attributes(element, prev, next, css_hash, skip_warning = fal set_attribute(element, name, value, skip_warning); } } - if (key === 'style' && '__styles' in element) { - // reset styles to force style: directive to update - element.__styles = {}; - } } if (is_hydrating_custom_element) { diff --git a/packages/svelte/tests/runtime-runes/samples/style-update/_config.js b/packages/svelte/tests/runtime-runes/samples/style-update/_config.js new file mode 100644 index 0000000000..f0b7f2648e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/style-update/_config.js @@ -0,0 +1,64 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +const style_1 = 'invalid-key:0; margin:4px;;color: green ;color:blue '; +const style_2 = ' other-key : 0 ; padding:2px; COLOR:green; color: blue'; + +// https://github.com/sveltejs/svelte/issues/15309 +export default test({ + props: { + style: style_1 + }, + + html: ` + <div style="${style_1}"></div> + <div style="${style_1}"></div> + + <custom-element style="${style_1}"></custom-element> + <custom-element style="${style_1}"></custom-element> + `, + + async test({ assert, target, component }) { + component.style = style_2; + flushSync(); + + assert.htmlEqual( + target.innerHTML, + ` + <div style="${style_2}"></div> + <div style="${style_2}"></div> + + <custom-element style="${style_2}"></custom-element> + <custom-element style="${style_2}"></custom-element> + ` + ); + + component.style = ''; + flushSync(); + + assert.htmlEqual( + target.innerHTML, + ` + <div style=""></div> + <div style=""></div> + + <custom-element style=""></custom-element> + <custom-element style=""></custom-element> + ` + ); + + component.style = null; + flushSync(); + + assert.htmlEqual( + target.innerHTML, + ` + <div></div> + <div></div> + + <custom-element></custom-element> + <custom-element></custom-element> + ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/style-update/main.svelte b/packages/svelte/tests/runtime-runes/samples/style-update/main.svelte new file mode 100644 index 0000000000..d29590d670 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/style-update/main.svelte @@ -0,0 +1,9 @@ +<script> + let { style } = $props(); +</script> + +<div {style}></div> +<div {...{style}}></div> + +<custom-element {style}></custom-element> +<custom-element {...{style}}></custom-element> From 30562b87802c7c814229c2774d2b264a89fda0d4 Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Thu, 6 Mar 2025 01:34:10 +0100 Subject: [PATCH 0235/1429] chore: rewrite set_style() to handle directives (#15418) * add style attribute when needed * set_style() * to_style() * remove `style=""` * use cssTest for perfs * base test * test * changeset * revert dom.style.cssText * format name * use style.cssText + adapt test * Apply suggestions from code review suggestions from dummdidumm Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> * fix priority * lint * yawn * update test * we can simplify some stuff now * simplify * more * simplify some more * more * more * more * more * more * remove continue * tweak * tweak * tweak * skip hash argument where possible * tweak * tweak * tweak * tweak --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/strange-planes-shout.md | 5 + .../src/compiler/phases/2-analyze/index.js | 26 +- .../client/visitors/RegularElement.js | 230 ++++++++---------- .../client/visitors/SvelteElement.js | 34 +-- .../client/visitors/shared/element.js | 136 +++++------ .../server/visitors/shared/element.js | 185 ++++++-------- .../client/dom/elements/attributes.js | 17 +- .../src/internal/client/dom/elements/style.js | 63 +++-- .../src/internal/client/dom/operations.js | 2 +- packages/svelte/src/internal/server/index.js | 46 ++-- .../svelte/src/internal/shared/attributes.js | 137 ++++++++++- .../_config.js | 2 +- .../_config.js | 2 +- .../main.svelte | 2 +- .../samples/dynamic-style-attr/_config.js | 4 +- .../samples/dynamic-style-attr/main.svelte | 2 +- .../style-directive-mutations/_config.js | 95 ++++++++ .../style-directive-mutations/main.svelte | 54 ++++ .../samples/style-update/_config.js | 11 +- 19 files changed, 654 insertions(+), 399 deletions(-) create mode 100644 .changeset/strange-planes-shout.md create mode 100644 packages/svelte/tests/runtime-runes/samples/style-directive-mutations/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/style-directive-mutations/main.svelte diff --git a/.changeset/strange-planes-shout.md b/.changeset/strange-planes-shout.md new file mode 100644 index 0000000000..58ef252740 --- /dev/null +++ b/.changeset/strange-planes-shout.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: make `style:` directive and CSS handling more robust diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 322293bf6b..1f636c32df 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -769,17 +769,24 @@ export function analyze_component(root, source, options) { } let has_class = false; + let has_style = false; let has_spread = false; let has_class_directive = false; + let has_style_directive = false; for (const attribute of node.attributes) { // The spread method appends the hash to the end of the class attribute on its own if (attribute.type === 'SpreadAttribute') { has_spread = true; break; + } else if (attribute.type === 'Attribute') { + has_class ||= attribute.name.toLowerCase() === 'class'; + has_style ||= attribute.name.toLowerCase() === 'style'; + } else if (attribute.type === 'ClassDirective') { + has_class_directive = true; + } else if (attribute.type === 'StyleDirective') { + has_style_directive = true; } - has_class_directive ||= attribute.type === 'ClassDirective'; - has_class ||= attribute.type === 'Attribute' && attribute.name.toLowerCase() === 'class'; } // We need an empty class to generate the set_class() or class="" correctly @@ -796,6 +803,21 @@ export function analyze_component(root, source, options) { ]) ); } + + // We need an empty style to generate the set_style() correctly + if (!has_spread && !has_style && has_style_directive) { + node.attributes.push( + create_attribute('style', -1, -1, [ + { + type: 'Text', + data: '', + raw: '', + start: -1, + end: -1 + } + ]) + ); + } } // TODO diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 3dd3039213..6122dc4e0e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -1,4 +1,4 @@ -/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */ +/** @import { ArrayExpression, Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { SourceLocation } from '#shared' */ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */ @@ -20,9 +20,9 @@ import { build_getter } from '../utils.js'; import { get_attribute_name, build_attribute_value, - build_style_directives, build_set_attributes, - build_set_class + build_set_class, + build_set_style } from './shared/element.js'; import { process_children } from './shared/fragment.js'; import { @@ -215,13 +215,18 @@ export function RegularElement(node, context) { const node_id = context.state.node; - // Then do attributes - let is_attributes_reactive = has_spread; - if (has_spread) { const attributes_id = b.id(context.state.scope.generate('attributes')); - build_set_attributes(attributes, class_directives, context, node, node_id, attributes_id); + build_set_attributes( + attributes, + class_directives, + style_directives, + context, + node, + node_id, + attributes_id + ); // If value binding exists, that one takes care of calling $.init_select if (node.name === 'select' && !bindings.has('value')) { @@ -262,11 +267,13 @@ export function RegularElement(node, context) { } const name = get_attribute_name(node, attribute); + if ( !is_custom_element && !cannot_be_set_statically(attribute.name) && (attribute.value === true || is_text_attribute(attribute)) && - (name !== 'class' || class_directives.length === 0) + (name !== 'class' || class_directives.length === 0) && + (name !== 'style' || style_directives.length === 0) ) { let value = is_text_attribute(attribute) ? attribute.value[0].data : true; @@ -287,27 +294,30 @@ export function RegularElement(node, context) { }` ); } - continue; - } + } else if (name === 'autofocus') { + let { value } = build_attribute_value(attribute.value, context); + context.state.init.push(b.stmt(b.call('$.autofocus', node_id, value))); + } else if (name === 'class') { + const is_html = context.state.metadata.namespace === 'html' && node.name !== 'svg'; + build_set_class(node, node_id, attribute, class_directives, context, is_html); + } else if (name === 'style') { + build_set_style(node_id, attribute, style_directives, context); + } else if (is_custom_element) { + build_custom_element_attribute_update_assignment(node_id, attribute, context); + } else { + const { value, has_state } = build_attribute_value( + attribute.value, + context, + (value, metadata) => (metadata.has_call ? get_expression_id(context.state, value) : value) + ); + + const update = build_element_attribute_update(node, node_id, name, value, attributes); - const is = - is_custom_element && name !== 'class' - ? build_custom_element_attribute_update_assignment(node_id, attribute, context) - : build_element_attribute_update_assignment( - node, - node_id, - attribute, - attributes, - class_directives, - context - ); - if (is) is_attributes_reactive = true; + (has_state ? context.state.update : context.state.init).push(b.stmt(update)); + } } } - // style directives must be applied last since they could override class/style attributes - build_style_directives(style_directives, node_id, context, is_attributes_reactive); - if ( is_load_error_element(node.name) && (has_spread || has_use || lookup.has('onload') || lookup.has('onerror')) @@ -519,6 +529,36 @@ export function build_class_directives_object(class_directives, context) { return b.object(properties); } +/** + * @param {AST.StyleDirective[]} style_directives + * @param {ComponentContext} context + * @return {ObjectExpression | ArrayExpression}} + */ +export function build_style_directives_object(style_directives, context) { + let normal_properties = []; + let important_properties = []; + + for (const directive of style_directives) { + const expression = + directive.value === true + ? build_getter({ name: directive.name, type: 'Identifier' }, context.state) + : build_attribute_value(directive.value, context, (value, metadata) => + metadata.has_call ? get_expression_id(context.state, value) : value + ).value; + const property = b.init(directive.name, expression); + + if (directive.modifiers.includes('important')) { + important_properties.push(property); + } else { + normal_properties.push(property); + } + } + + return important_properties.length + ? b.array([b.object(normal_properties), b.object(important_properties)]) + : b.object(normal_properties); +} + /** * Serializes an assignment to an element property by adding relevant statements to either only * the init or the the init and update arrays, depending on whether or not the value is dynamic. @@ -543,73 +583,29 @@ export function build_class_directives_object(class_directives, context) { * Returns true if attribute is deemed reactive, false otherwise. * @param {AST.RegularElement} element * @param {Identifier} node_id - * @param {AST.Attribute} attribute + * @param {string} name + * @param {Expression} value * @param {Array<AST.Attribute | AST.SpreadAttribute>} attributes - * @param {AST.ClassDirective[]} class_directives - * @param {ComponentContext} context - * @returns {boolean} */ -function build_element_attribute_update_assignment( - element, - node_id, - attribute, - attributes, - class_directives, - context -) { - const state = context.state; - const name = get_attribute_name(element, attribute); - const is_svg = context.state.metadata.namespace === 'svg' || element.name === 'svg'; - const is_mathml = context.state.metadata.namespace === 'mathml'; - - const is_autofocus = name === 'autofocus'; - - let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => - metadata.has_call - ? // if it's autofocus we will not add this to a template effect so we don't want to get the expression id - // but separately memoize the expression - is_autofocus - ? memoize_expression(state, value) - : get_expression_id(state, value) - : value - ); +function build_element_attribute_update(element, node_id, name, value, attributes) { + if (name === 'muted') { + // Special case for Firefox who needs it set as a property in order to work + return b.assignment('=', b.member(node_id, b.id('muted')), value); + } - if (is_autofocus) { - state.init.push(b.stmt(b.call('$.autofocus', node_id, value))); - return false; + if (name === 'value') { + return b.call('$.set_value', node_id, value); } - // Special case for Firefox who needs it set as a property in order to work - if (name === 'muted') { - if (!has_state) { - state.init.push(b.stmt(b.assignment('=', b.member(node_id, b.id('muted')), value))); - return false; - } - state.update.push(b.stmt(b.assignment('=', b.member(node_id, b.id('muted')), value))); - return false; + if (name === 'checked') { + return b.call('$.set_checked', node_id, value); } - /** @type {Statement} */ - let update; + if (name === 'selected') { + return b.call('$.set_selected', node_id, value); + } - if (name === 'class') { - return build_set_class( - element, - node_id, - attribute, - value, - has_state, - class_directives, - context, - !is_svg && !is_mathml - ); - } else if (name === 'value') { - update = b.stmt(b.call('$.set_value', node_id, value)); - } else if (name === 'checked') { - update = b.stmt(b.call('$.set_checked', node_id, value)); - } else if (name === 'selected') { - update = b.stmt(b.call('$.set_selected', node_id, value)); - } else if ( + if ( // If we would just set the defaultValue property, it would override the value property, // because it is set in the template which implicitly means it's also setting the default value, // and if one updates the default value while the input is pristine it will also update the @@ -620,62 +616,49 @@ function build_element_attribute_update_assignment( ) || (element.name === 'textarea' && element.fragment.nodes.length > 0)) ) { - update = b.stmt(b.call('$.set_default_value', node_id, value)); - } else if ( + return b.call('$.set_default_value', node_id, value); + } + + if ( // See defaultValue comment name === 'defaultChecked' && attributes.some( (attr) => attr.type === 'Attribute' && attr.name === 'checked' && attr.value === true ) ) { - update = b.stmt(b.call('$.set_default_checked', node_id, value)); - } else if (is_dom_property(name)) { - update = b.stmt(b.assignment('=', b.member(node_id, name), value)); - } else { - const callee = name.startsWith('xlink') ? '$.set_xlink_attribute' : '$.set_attribute'; - update = b.stmt( - b.call( - callee, - node_id, - b.literal(name), - value, - is_ignored(element, 'hydration_attribute_changed') && b.true - ) - ); + return b.call('$.set_default_checked', node_id, value); } - if (has_state) { - state.update.push(update); - return true; - } else { - state.init.push(update); - return false; + if (is_dom_property(name)) { + return b.assignment('=', b.member(node_id, name), value); } + + return b.call( + name.startsWith('xlink') ? '$.set_xlink_attribute' : '$.set_attribute', + node_id, + b.literal(name), + value, + is_ignored(element, 'hydration_attribute_changed') && b.true + ); } /** - * Like `build_element_attribute_update_assignment` but without any special attribute treatment. + * Like `build_element_attribute_update` but without any special attribute treatment. * @param {Identifier} node_id * @param {AST.Attribute} attribute * @param {ComponentContext} context - * @returns {boolean} */ function build_custom_element_attribute_update_assignment(node_id, attribute, context) { - const state = context.state; - const name = attribute.name; // don't lowercase, as we set the element's property, which might be case sensitive - let { value, has_state } = build_attribute_value(attribute.value, context); + const { value, has_state } = build_attribute_value(attribute.value, context); - const update = b.stmt(b.call('$.set_custom_element_data', node_id, b.literal(name), value)); + // don't lowercase name, as we set the element's property, which might be case sensitive + const call = b.call('$.set_custom_element_data', node_id, b.literal(attribute.name), value); - if (has_state) { - // this is different from other updates — it doesn't get grouped, - // because set_custom_element_data may not be idempotent - state.init.push(b.stmt(b.call('$.template_effect', b.thunk(update.expression)))); - return true; - } else { - state.init.push(update); - return false; - } + // this is different from other updates — it doesn't get grouped, + // because set_custom_element_data may not be idempotent + const update = has_state ? b.call('$.template_effect', b.thunk(call)) : call; + + context.state.init.push(b.stmt(update)); } /** @@ -686,7 +669,6 @@ function build_custom_element_attribute_update_assignment(node_id, attribute, co * @param {Identifier} node_id * @param {AST.Attribute} attribute * @param {ComponentContext} context - * @returns {boolean} */ function build_element_special_value_attribute(element, node_id, attribute, context) { const state = context.state; @@ -699,7 +681,7 @@ function build_element_special_value_attribute(element, node_id, attribute, cont metadata.has_call ? // if is a select with value we will also invoke `init_select` which need a reference before the template effect so we memoize separately is_select_with_value - ? memoize_expression(context.state, value) + ? memoize_expression(state, value) : get_expression_id(state, value) : value ); @@ -743,9 +725,7 @@ function build_element_special_value_attribute(element, node_id, attribute, cont value, update ); - return true; } else { state.init.push(update); - return false; } } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js index 3250c24392..115eb6ccc1 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js @@ -5,12 +5,7 @@ import { dev, locator } from '../../../../state.js'; import { is_text_attribute } from '../../../../utils/ast.js'; import * as b from '../../../../utils/builders.js'; import { determine_namespace_for_children } from '../../utils.js'; -import { - build_attribute_value, - build_set_attributes, - build_set_class, - build_style_directives -} from './shared/element.js'; +import { build_attribute_value, build_set_attributes, build_set_class } from './shared/element.js'; import { build_render_statement, get_expression_id } from './shared/utils.js'; /** @@ -77,40 +72,22 @@ export function SvelteElement(node, context) { // Let bindings first, they can be used on attributes context.state.init.push(...lets); // create computeds in the outer context; the dynamic element is the single child of this slot - // Then do attributes - let is_attributes_reactive = false; - if ( attributes.length === 1 && attributes[0].type === 'Attribute' && attributes[0].name.toLowerCase() === 'class' && is_text_attribute(attributes[0]) ) { - // special case when there only a class attribute, without call expression - let { value, has_state } = build_attribute_value( - attributes[0].value, - context, - (value, metadata) => (metadata.has_call ? get_expression_id(context.state, value) : value) - ); - - is_attributes_reactive = build_set_class( - node, - element_id, - attributes[0], - value, - has_state, - class_directives, - inner_context, - false - ); + build_set_class(node, element_id, attributes[0], class_directives, inner_context, false); } else if (attributes.length) { const attributes_id = b.id(context.state.scope.generate('attributes')); // Always use spread because we don't know whether the element is a custom element or not, // therefore we need to do the "how to set an attribute" logic at runtime. - is_attributes_reactive = build_set_attributes( + build_set_attributes( attributes, class_directives, + style_directives, inner_context, node, element_id, @@ -118,9 +95,6 @@ export function SvelteElement(node, context) { ); } - // style directives must be applied last since they could override class/style attributes - build_style_directives(style_directives, element_id, inner_context, is_attributes_reactive); - const get_tag = b.thunk(/** @type {Expression} */ (context.visit(node.tag))); if (dev) { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index db8f2e4aa0..e0eb04d823 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -1,4 +1,4 @@ -/** @import { Expression, Identifier, ObjectExpression } from 'estree' */ +/** @import { ArrayExpression, Expression, Identifier, ObjectExpression } from 'estree' */ /** @import { AST, ExpressionMetadata } from '#compiler' */ /** @import { ComponentClientTransformState, ComponentContext } from '../../types' */ import { escape_html } from '../../../../../../escaping.js'; @@ -6,13 +6,13 @@ import { normalize_attribute } from '../../../../../../utils.js'; import { is_ignored } from '../../../../../state.js'; import { is_event_attribute } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; -import { build_getter } from '../../utils.js'; -import { build_class_directives_object } from '../RegularElement.js'; +import { build_class_directives_object, build_style_directives_object } from '../RegularElement.js'; import { build_template_chunk, get_expression_id } from './utils.js'; /** * @param {Array<AST.Attribute | AST.SpreadAttribute>} attributes * @param {AST.ClassDirective[]} class_directives + * @param {AST.StyleDirective[]} style_directives * @param {ComponentContext} context * @param {AST.RegularElement | AST.SvelteElement} element * @param {Identifier} element_id @@ -21,6 +21,7 @@ import { build_template_chunk, get_expression_id } from './utils.js'; export function build_set_attributes( attributes, class_directives, + style_directives, context, element, element_id, @@ -79,6 +80,18 @@ export function build_set_attributes( class_directives.find((directive) => directive.metadata.expression.has_state) !== null; } + if (style_directives.length) { + values.push( + b.prop( + 'init', + b.array([b.id('$.STYLE')]), + build_style_directives_object(style_directives, context) + ) + ); + + is_dynamic ||= style_directives.some((directive) => directive.metadata.expression.has_state); + } + const call = b.call( '$.set_attributes', element_id, @@ -94,54 +107,8 @@ export function build_set_attributes( context.state.init.push(b.let(attributes_id)); const update = b.stmt(b.assignment('=', attributes_id, call)); context.state.update.push(update); - return true; - } - - context.state.init.push(b.stmt(call)); - return false; -} - -/** - * Serializes each style directive into something like `$.set_style(element, style_property, value)` - * and adds it either to init or update, depending on whether or not the value or the attributes are dynamic. - * @param {AST.StyleDirective[]} style_directives - * @param {Identifier} element_id - * @param {ComponentContext} context - * @param {boolean} is_attributes_reactive - */ -export function build_style_directives( - style_directives, - element_id, - context, - is_attributes_reactive -) { - const state = context.state; - - for (const directive of style_directives) { - const { has_state } = directive.metadata.expression; - - let value = - directive.value === true - ? build_getter({ name: directive.name, type: 'Identifier' }, context.state) - : build_attribute_value(directive.value, context, (value, metadata) => - metadata.has_call ? get_expression_id(context.state, value) : value - ).value; - - const update = b.stmt( - b.call( - '$.set_style', - element_id, - b.literal(directive.name), - value, - /** @type {Expression} */ (directive.modifiers.includes('important') ? b.true : undefined) - ) - ); - - if (has_state || is_attributes_reactive) { - state.update.push(update); - } else { - state.init.push(update); - } + } else { + context.state.init.push(b.stmt(call)); } } @@ -189,24 +156,16 @@ export function get_attribute_name(element, attribute) { /** * @param {AST.RegularElement | AST.SvelteElement} element * @param {Identifier} node_id - * @param {AST.Attribute | null} attribute - * @param {Expression} value - * @param {boolean} has_state + * @param {AST.Attribute} attribute * @param {AST.ClassDirective[]} class_directives * @param {ComponentContext} context * @param {boolean} is_html - * @returns {boolean} */ -export function build_set_class( - element, - node_id, - attribute, - value, - has_state, - class_directives, - context, - is_html -) { +export function build_set_class(element, node_id, attribute, class_directives, context, is_html) { + let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => + metadata.has_call ? get_expression_id(context.state, value) : value + ); + if (attribute && attribute.metadata.needs_clsx) { value = b.call('$.clsx', value); } @@ -265,13 +224,48 @@ export function build_set_class( set_class = b.assignment('=', previous_id, set_class); } - const update = b.stmt(set_class); + (has_state ? context.state.update : context.state.init).push(b.stmt(set_class)); +} - if (has_state) { - context.state.update.push(update); - return true; +/** + * @param {Identifier} node_id + * @param {AST.Attribute} attribute + * @param {AST.StyleDirective[]} style_directives + * @param {ComponentContext} context + */ +export function build_set_style(node_id, attribute, style_directives, context) { + let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => + metadata.has_call ? get_expression_id(context.state, value) : value + ); + + /** @type {Identifier | undefined} */ + let previous_id; + + /** @type {ObjectExpression | Identifier | undefined} */ + let prev; + + /** @type {ArrayExpression | ObjectExpression | undefined} */ + let next; + + if (style_directives.length) { + next = build_style_directives_object(style_directives, context); + has_state ||= style_directives.some((d) => d.metadata.expression.has_state); + + if (has_state) { + previous_id = b.id(context.state.scope.generate('styles')); + context.state.init.push(b.declaration('let', [b.declarator(previous_id)])); + prev = previous_id; + } else { + prev = b.object([]); + } + } + + /** @type {Expression} */ + let set_style = b.call('$.set_style', node_id, value, prev, next); + + if (previous_id) { + set_style = b.assignment('=', previous_id, set_style); } - context.state.init.push(update); - return false; + (has_state ? context.state.update : context.state.init).push(b.stmt(set_style)); } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js index 281d8f0617..4a5becfb2f 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js @@ -1,4 +1,4 @@ -/** @import { Expression, Literal, ObjectExpression } from 'estree' */ +/** @import { ArrayExpression, Expression, Literal, ObjectExpression } from 'estree' */ /** @import { AST, Namespace } from '#compiler' */ /** @import { ComponentContext, ComponentServerTransformState } from '../../types.js' */ import { @@ -48,9 +48,6 @@ export function build_element_attributes(node, context) { let content = null; let has_spread = false; - // Use the index to keep the attributes order which is important for spreading - let class_index = -1; - let style_index = -1; let events_to_capture = new Set(); for (const attribute of node.attributes) { @@ -86,7 +83,6 @@ export function build_element_attributes(node, context) { // the defaultValue/defaultChecked properties don't exist as attributes } else if (attribute.name !== 'defaultValue' && attribute.name !== 'defaultChecked') { if (attribute.name === 'class') { - class_index = attributes.length; if (attribute.metadata.needs_clsx) { attributes.push({ ...attribute, @@ -102,10 +98,6 @@ export function build_element_attributes(node, context) { attributes.push(attribute); } } else { - if (attribute.name === 'style') { - style_index = attributes.length; - } - attributes.push(attribute); } } @@ -212,41 +204,30 @@ export function build_element_attributes(node, context) { } } - if ((node.metadata.scoped || class_directives.length) && !has_spread) { - const class_attribute = build_to_class( - node.metadata.scoped ? context.state.analysis.css.hash : null, - class_directives, - /** @type {AST.Attribute | null} */ (attributes[class_index] ?? null) - ); - if (class_index === -1) { - attributes.push(class_attribute); - } - } - - if (style_directives.length > 0 && !has_spread) { - build_style_directives( - style_directives, - /** @type {AST.Attribute | null} */ (attributes[style_index] ?? null), - context - ); - if (style_index > -1) { - attributes.splice(style_index, 1); - } - } - if (has_spread) { build_element_spread_attributes(node, attributes, style_directives, class_directives, context); } else { + const css_hash = node.metadata.scoped ? context.state.analysis.css.hash : null; + for (const attribute of /** @type {AST.Attribute[]} */ (attributes)) { - if (attribute.value === true || is_text_attribute(attribute)) { - const name = get_attribute_name(node, attribute); - const literal_value = /** @type {Literal} */ ( + const name = get_attribute_name(node, attribute); + const can_use_literal = + (name !== 'class' || class_directives.length === 0) && + (name !== 'style' || style_directives.length === 0); + + if (can_use_literal && (attribute.value === true || is_text_attribute(attribute))) { + let literal_value = /** @type {Literal} */ ( build_attribute_value( attribute.value, context, WHITESPACE_INSENSITIVE_ATTRIBUTES.includes(name) ) ).value; + + if (name === 'class' && css_hash) { + literal_value = (String(literal_value) + ' ' + css_hash).trim(); + } + if (name !== 'class' || literal_value) { context.state.template.push( b.literal( @@ -258,10 +239,10 @@ export function build_element_attributes(node, context) { ) ); } + continue; } - const name = get_attribute_name(node, attribute); const value = build_attribute_value( attribute.value, context, @@ -269,8 +250,15 @@ export function build_element_attributes(node, context) { ); // pre-escape and inline literal attributes : - if (value.type === 'Literal' && typeof value.value === 'string') { + if (can_use_literal && value.type === 'Literal' && typeof value.value === 'string') { + if (name === 'class' && css_hash) { + value.value = (value.value + ' ' + css_hash).trim(); + } context.state.template.push(b.literal(` ${name}="${escape_html(value.value, true)}"`)); + } else if (name === 'class') { + context.state.template.push(build_attr_class(class_directives, value, context, css_hash)); + } else if (name === 'style') { + context.state.template.push(build_attr_style(style_directives, value, context)); } else { context.state.template.push( b.call('$.attr', b.literal(name), value, is_boolean_attribute(name) && b.true) @@ -379,100 +367,79 @@ function build_element_spread_attributes( /** * - * @param {string | null} hash * @param {AST.ClassDirective[]} class_directives - * @param {AST.Attribute | null} class_attribute - * @returns + * @param {Expression} expression + * @param {ComponentContext} context + * @param {string | null} hash */ -function build_to_class(hash, class_directives, class_attribute) { - if (class_attribute === null) { - class_attribute = create_attribute('class', -1, -1, []); - } - +function build_attr_class(class_directives, expression, context, hash) { /** @type {ObjectExpression | undefined} */ - let classes; + let directives; if (class_directives.length) { - classes = b.object( + directives = b.object( class_directives.map((directive) => - b.prop('init', b.literal(directive.name), directive.expression) + b.prop( + 'init', + b.literal(directive.name), + /** @type {Expression} */ (context.visit(directive.expression, context.state)) + ) ) ); } - /** @type {Expression} */ - let class_name; - - if (class_attribute.value === true) { - class_name = b.literal(''); - } else if (Array.isArray(class_attribute.value)) { - if (class_attribute.value.length === 0) { - class_name = b.null; - } else { - class_name = class_attribute.value - .map((val) => (val.type === 'Text' ? b.literal(val.data) : val.expression)) - .reduce((left, right) => b.binary('+', left, right)); - } - } else { - class_name = class_attribute.value.expression; - } + let css_hash; - /** @type {Expression} */ - let expression; - - if ( - hash && - !classes && - class_name.type === 'Literal' && - (class_name.value === null || class_name.value === '' || typeof class_name.value === 'string') - ) { - if (class_name.value === null || class_name.value === '') { - expression = b.literal(hash); + if (hash) { + if (expression.type === 'Literal' && typeof expression.value === 'string') { + expression.value = (expression.value + ' ' + hash).trim(); } else { - expression = b.literal(escape_html(class_name.value, true) + ' ' + hash); + css_hash = b.literal(hash); } - } else { - expression = b.call('$.to_class', class_name, b.literal(hash), classes); } - class_attribute.value = { - type: 'ExpressionTag', - start: -1, - end: -1, - expression: expression, - metadata: { - expression: create_expression_metadata() - } - }; - - return class_attribute; + return b.call('$.attr_class', expression, css_hash, directives); } /** + * * @param {AST.StyleDirective[]} style_directives - * @param {AST.Attribute | null} style_attribute + * @param {Expression} expression * @param {ComponentContext} context */ -function build_style_directives(style_directives, style_attribute, context) { - const styles = style_directives.map((directive) => { - let value = - directive.value === true - ? b.id(directive.name) - : build_attribute_value(directive.value, context, true); - if (directive.modifiers.includes('important')) { - value = b.binary('+', value, b.literal(' !important')); +function build_attr_style(style_directives, expression, context) { + /** @type {ArrayExpression | ObjectExpression | undefined} */ + let directives; + + if (style_directives.length) { + let normal_properties = []; + let important_properties = []; + + for (const directive of style_directives) { + const expression = + directive.value === true + ? b.id(directive.name) + : build_attribute_value(directive.value, context, true); + + let name = directive.name; + if (name[0] !== '-' || name[1] !== '-') { + name = name.toLowerCase(); + } + + const property = b.init(directive.name, expression); + if (directive.modifiers.includes('important')) { + important_properties.push(property); + } else { + normal_properties.push(property); + } } - return b.init(directive.name, value); - }); - - const arg = - style_attribute === null - ? b.object(styles) - : b.call( - '$.merge_styles', - build_attribute_value(style_attribute.value, context, true), - b.object(styles) - ); - context.state.template.push(b.call('$.add_styles', arg)); + if (important_properties.length) { + directives = b.array([b.object(normal_properties), b.object(important_properties)]); + } else { + directives = b.object(normal_properties); + } + } + + return b.call('$.attr_style', expression, directives); } diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index cebc9173ba..5a5d5d7c9b 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -15,6 +15,7 @@ import { } from '../../runtime.js'; import { clsx } from '../../../shared/attributes.js'; import { set_class } from './class.js'; +import { set_style } from './style.js'; import { NAMESPACE_HTML } from '../../../../constants.js'; export const CLASS = Symbol('class'); @@ -177,11 +178,6 @@ export function set_attribute(element, attribute, value, skip_warning) { if (attributes[attribute] === (attributes[attribute] = value)) return; - if (attribute === 'style' && '__styles' in element) { - // reset styles to force style: directive to update - element.__styles = {}; - } - if (attribute === 'loading') { // @ts-expect-error element[LOADING_ATTR_SYMBOL] = value; @@ -297,6 +293,10 @@ export function set_attributes(element, prev, next, css_hash, skip_warning = fal next.class = null; /* force call to set_class() */ } + if (next[STYLE]) { + next.style ??= null; /* force call to set_style() */ + } + var setters = get_setters(element); // since key is captured we use const @@ -331,6 +331,13 @@ export function set_attributes(element, prev, next, css_hash, skip_warning = fal continue; } + if (key === 'style') { + set_style(element, value, prev?.[STYLE], next[STYLE]); + current[key] = value; + current[STYLE] = next[STYLE]; + continue; + } + var prev_value = current[key]; if (value === prev_value) continue; diff --git a/packages/svelte/src/internal/client/dom/elements/style.js b/packages/svelte/src/internal/client/dom/elements/style.js index 34531029c9..3e05eec30e 100644 --- a/packages/svelte/src/internal/client/dom/elements/style.js +++ b/packages/svelte/src/internal/client/dom/elements/style.js @@ -1,22 +1,57 @@ +import { to_style } from '../../../shared/attributes.js'; +import { hydrating } from '../hydration.js'; + /** - * @param {HTMLElement} dom - * @param {string} key - * @param {string} value - * @param {boolean} [important] + * @param {Element & ElementCSSInlineStyle} dom + * @param {Record<string, any>} prev + * @param {Record<string, any>} next + * @param {string} [priority] */ -export function set_style(dom, key, value, important) { - // @ts-expect-error - var styles = (dom.__styles ??= {}); +function update_styles(dom, prev = {}, next, priority) { + for (var key in next) { + var value = next[key]; - if (styles[key] === value) { - return; + if (prev[key] !== value) { + if (next[key] == null) { + dom.style.removeProperty(key); + } else { + dom.style.setProperty(key, value, priority); + } + } } +} - styles[key] = value; +/** + * @param {Element & ElementCSSInlineStyle} dom + * @param {string | null} value + * @param {Record<string, any> | [Record<string, any>, Record<string, any>]} [prev_styles] + * @param {Record<string, any> | [Record<string, any>, Record<string, any>]} [next_styles] + */ +export function set_style(dom, value, prev_styles, next_styles) { + // @ts-expect-error + var prev = dom.__style; + + if (hydrating || prev !== value) { + var next_style_attr = to_style(value, next_styles); - if (value == null) { - dom.style.removeProperty(key); - } else { - dom.style.setProperty(key, value, important ? 'important' : ''); + if (!hydrating || next_style_attr !== dom.getAttribute('style')) { + if (next_style_attr == null) { + dom.removeAttribute('style'); + } else { + dom.style.cssText = next_style_attr; + } + } + + // @ts-expect-error + dom.__style = value; + } else if (next_styles) { + if (Array.isArray(next_styles)) { + update_styles(dom, prev_styles?.[0], next_styles[0]); + update_styles(dom, prev_styles?.[1], next_styles[1], 'important'); + } else { + update_styles(dom, prev_styles, next_styles); + } } + + return next_styles; } diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index f6ac92456e..0ad9045b20 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -48,7 +48,7 @@ export function init_operations() { // @ts-expect-error element_prototype.__attributes = null; // @ts-expect-error - element_prototype.__styles = null; + element_prototype.__style = undefined; // @ts-expect-error element_prototype.__e = undefined; diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 2591dbe4ea..6098b496c5 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -2,7 +2,7 @@ /** @import { Component, Payload, RenderOutput } from '#server' */ /** @import { Store } from '#shared' */ export { FILENAME, HMR } from '../../constants.js'; -import { attr, clsx, to_class } from '../shared/attributes.js'; +import { attr, clsx, to_class, to_style } from '../shared/attributes.js'; import { is_promise, noop } from '../shared/utils.js'; import { subscribe_to_store } from '../../store/utils.js'; import { @@ -210,9 +210,7 @@ export function css_props(payload, is_html, props, component, dynamic = false) { */ export function spread_attributes(attrs, css_hash, classes, styles, flags = 0) { if (styles) { - attrs.style = attrs.style - ? style_object_to_string(merge_styles(/** @type {string} */ (attrs.style), styles)) - : style_object_to_string(styles); + attrs.style = to_style(attrs.style, styles); } if (attrs.class) { @@ -286,35 +284,23 @@ function style_object_to_string(style_object) { .join(' '); } -/** @param {Record<string, string>} style_object */ -export function add_styles(style_object) { - const styles = style_object_to_string(style_object); - return styles ? ` style="${styles}"` : ''; +/** + * @param {any} value + * @param {string | undefined} [hash] + * @param {Record<string, boolean>} [directives] + */ +export function attr_class(value, hash, directives) { + var result = to_class(value, hash, directives); + return result ? ` class="${escape_html(result, true)}"` : ''; } /** - * @param {string} attribute - * @param {Record<string, string>} styles + * @param {any} value + * @param {Record<string,any>|[Record<string,any>,Record<string,any>]} [directives] */ -export function merge_styles(attribute, styles) { - /** @type {Record<string, string>} */ - var merged = {}; - - if (attribute) { - for (var declaration of attribute.split(';')) { - var i = declaration.indexOf(':'); - var name = declaration.slice(0, i).trim(); - var value = declaration.slice(i + 1).trim(); - - if (name !== '') merged[name] = value; - } - } - - for (name in styles) { - merged[name] = styles[name]; - } - - return merged; +export function attr_style(value, directives) { + var result = to_style(value, directives); + return result ? ` style="${escape_html(result, true)}"` : ''; } /** @@ -549,7 +535,7 @@ export function props_id(payload) { return uid; } -export { attr, clsx, to_class }; +export { attr, clsx }; export { html } from './blocks/html.js'; diff --git a/packages/svelte/src/internal/shared/attributes.js b/packages/svelte/src/internal/shared/attributes.js index 89cc17e51b..c8758c1d4d 100644 --- a/packages/svelte/src/internal/shared/attributes.js +++ b/packages/svelte/src/internal/shared/attributes.js @@ -22,7 +22,7 @@ const replacements = { * @returns {string} */ export function attr(name, value, is_boolean = false) { - if (value == null || (!value && is_boolean) || (value === '' && name === 'class')) return ''; + if (value == null || (!value && is_boolean)) return ''; const normalized = (name in replacements && replacements[name].get(value)) || value; const assignment = is_boolean ? '' : `="${escape_html(normalized, true)}"`; return ` ${name}${assignment}`; @@ -82,3 +82,138 @@ export function to_class(value, hash, directives) { return classname === '' ? null : classname; } + +/** + * + * @param {Record<string,any>} styles + * @param {boolean} important + */ +function append_styles(styles, important = false) { + var separator = important ? ' !important;' : ';'; + var css = ''; + + for (var key in styles) { + var value = styles[key]; + if (value != null && value !== '') { + css += ' ' + key + ': ' + value + separator; + } + } + + return css; +} + +/** + * @param {string} name + * @returns {string} + */ +function to_css_name(name) { + if (name[0] !== '-' || name[1] !== '-') { + return name.toLowerCase(); + } + return name; +} + +/** + * @param {any} value + * @param {Record<string, any> | [Record<string, any>, Record<string, any>]} [styles] + * @returns {string | null} + */ +export function to_style(value, styles) { + if (styles) { + var new_style = ''; + + /** @type {Record<string,any> | undefined} */ + var normal_styles; + + /** @type {Record<string,any> | undefined} */ + var important_styles; + + if (Array.isArray(styles)) { + normal_styles = styles[0]; + important_styles = styles[1]; + } else { + normal_styles = styles; + } + + if (value) { + value = String(value) + .replaceAll(/\s*\/\*.*?\*\/\s*/g, '') + .trim(); + + /** @type {boolean | '"' | "'"} */ + var in_str = false; + var in_apo = 0; + var in_comment = false; + + var reserved_names = []; + + if (normal_styles) { + reserved_names.push(...Object.keys(normal_styles).map(to_css_name)); + } + if (important_styles) { + reserved_names.push(...Object.keys(important_styles).map(to_css_name)); + } + + var start_index = 0; + var name_index = -1; + + const len = value.length; + for (var i = 0; i < len; i++) { + var c = value[i]; + + if (in_comment) { + if (c === '/' && value[i - 1] === '*') { + in_comment = false; + } + } else if (in_str) { + if (in_str === c) { + in_str = false; + } + } else if (c === '/' && value[i + 1] === '*') { + in_comment = true; + } else if (c === '"' || c === "'") { + in_str = c; + } else if (c === '(') { + in_apo++; + } else if (c === ')') { + in_apo--; + } + + if (!in_comment && in_str === false && in_apo === 0) { + if (c === ':' && name_index === -1) { + name_index = i; + } else if (c === ';' || i === len - 1) { + if (name_index !== -1) { + var name = to_css_name(value.substring(start_index, name_index).trim()); + + if (!reserved_names.includes(name)) { + if (c !== ';') { + i++; + } + + var property = value.substring(start_index, i).trim(); + new_style += ' ' + property + ';'; + } + } + + start_index = i + 1; + name_index = -1; + } + } + } + } + + if (normal_styles) { + new_style += append_styles(normal_styles); + } + + if (important_styles) { + new_style += append_styles(important_styles, true); + } + + new_style = new_style.trim(); + return new_style === '' ? null : new_style; + } + + return value == null ? null : String(value); +} diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-style-directive-spread-and-attr-empty/_config.js b/packages/svelte/tests/runtime-legacy/samples/inline-style-directive-spread-and-attr-empty/_config.js index 9ff0007c37..04c9868ac3 100644 --- a/packages/svelte/tests/runtime-legacy/samples/inline-style-directive-spread-and-attr-empty/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/inline-style-directive-spread-and-attr-empty/_config.js @@ -2,6 +2,6 @@ import { test } from '../../test'; export default test({ html: ` - <p style=""></p> + <p></p> ` }); diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/_config.js b/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/_config.js index adcdc4706d..e9965b2b1e 100644 --- a/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/_config.js @@ -2,7 +2,7 @@ import { ok, test } from '../../test'; export default test({ html: ` - <p style="opacity: 0.5; color: red">color: red</p> + <p style="opacity: 0.5; color: red;">color: red;</p> `, test({ assert, component, target, window }) { diff --git a/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/main.svelte b/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/main.svelte index 35b768547e..e07adaa1c9 100644 --- a/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/main.svelte +++ b/packages/svelte/tests/runtime-legacy/samples/inline-style-optimisation-bailout/main.svelte @@ -1,5 +1,5 @@ <script> - export let styles = `color: red`; + export let styles = `color: red;`; </script> <p style="opacity: 0.5; {styles}">{styles}</p> \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-style-attr/_config.js b/packages/svelte/tests/runtime-runes/samples/dynamic-style-attr/_config.js index f682972179..20092ddadf 100644 --- a/packages/svelte/tests/runtime-runes/samples/dynamic-style-attr/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/dynamic-style-attr/_config.js @@ -2,7 +2,7 @@ import { test } from '../../test'; import { flushSync } from 'svelte'; export default test({ - html: `<div style="background-color: red">Hello world</div><button>Make blue</button`, + html: `<div style="background-color: red;">Hello world</div><button>Make blue</button`, async test({ assert, target, component }) { const [b1] = target.querySelectorAll('button'); @@ -11,7 +11,7 @@ export default test({ }); assert.htmlEqual( target.innerHTML, - `<div style="background-color: blue">Hello world</div><button>Make blue</button` + `<div style="background-color: blue;">Hello world</div><button>Make blue</button` ); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/dynamic-style-attr/main.svelte b/packages/svelte/tests/runtime-runes/samples/dynamic-style-attr/main.svelte index f39c691ba3..59c8b0c567 100644 --- a/packages/svelte/tests/runtime-runes/samples/dynamic-style-attr/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/dynamic-style-attr/main.svelte @@ -4,6 +4,6 @@ const getColor = () => color; </script> -<div style="background-color: {getColor()}">Hello world</div> +<div style="background-color: {getColor()};">Hello world</div> <button onclick={() => color = 'blue'}>Make blue</button> diff --git a/packages/svelte/tests/runtime-runes/samples/style-directive-mutations/_config.js b/packages/svelte/tests/runtime-runes/samples/style-directive-mutations/_config.js new file mode 100644 index 0000000000..bd76e4e6b9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/style-directive-mutations/_config.js @@ -0,0 +1,95 @@ +import { flushSync, tick } from 'svelte'; +import { test } from '../../test'; + +// This test counts mutations on hydration +// set_style() should not mutate style on hydration, except if mismatch +export default test({ + mode: ['server', 'hydrate'], + + server_props: { + browser: false + }, + + props: { + browser: true + }, + + ssrHtml: ` + <main id="main" style="color: black;"> + <div style="color: red; font-size: 18px !important;"></div> + <div style="border: 1px solid; color: red; font-size: 18px !important;"></div> + <div style="border: 1px solid; color: red; font-size: 18px !important;"></div> + <div style="border: 1px solid; color: red; font-size: 18px !important;"></div> + <div style="background:blue; background: linear-gradient(0, white 0%, red 100%); color: red; font-size: 18px !important;"></div> + <div style="border: 1px solid; color: red; font-size: 18px !important;"></div> + <div style="background: url(https://placehold.co/100x100?text=;&font=roboto); color: red; font-size: 18px !important;"></div> + <div style="background: url("https://placehold.co/100x100?text=;&font=roboto"); color: red; font-size: 18px !important;"></div> + <div style="background: url('https://placehold.co/100x100?text=;&font=roboto'); color: red; font-size: 18px !important;"></div> + </main> + `, + + html: ` + <main id="main" style="color: white;"> + <div style="color: red; font-size: 18px !important;"></div> + <div style="border: 1px solid; color: red; font-size: 18px !important;"></div> + <div style="border: 1px solid; color: red; font-size: 18px !important;"></div> + <div style="border: 1px solid; color: red; font-size: 18px !important;"></div> + <div style="background:blue; background: linear-gradient(0, white 0%, red 100%); color: red; font-size: 18px !important;"></div> + <div style="border: 1px solid; color: red; font-size: 18px !important;"></div> + <div style="background: url(https://placehold.co/100x100?text=;&font=roboto); color: red; font-size: 18px !important;"></div> + <div style="background: url("https://placehold.co/100x100?text=;&font=roboto"); color: red; font-size: 18px !important;"></div> + <div style="background: url('https://placehold.co/100x100?text=;&font=roboto'); color: red; font-size: 18px !important;"></div> + </main> + `, + + async test({ target, assert, component, instance }) { + flushSync(); + tick(); + assert.deepEqual(instance.get_and_clear_mutations(), ['MAIN']); + + let divs = target.querySelectorAll('div'); + + // Note : we cannot compare HTML because set_style() use dom.style.cssText + // which can alter the format of the attribute... + + divs.forEach((d) => assert.equal(d.style.margin, '')); + divs.forEach((d) => assert.equal(d.style.color, 'red')); + divs.forEach((d) => assert.equal(d.style.fontSize, '18px')); + + component.margin = '1px'; + flushSync(); + assert.deepEqual( + instance.get_and_clear_mutations(), + ['DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV'], + 'margin' + ); + divs.forEach((d) => assert.equal(d.style.margin, '1px')); + + component.color = 'yellow'; + flushSync(); + assert.deepEqual( + instance.get_and_clear_mutations(), + ['DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV'], + 'color' + ); + divs.forEach((d) => assert.equal(d.style.color, 'yellow')); + + component.fontSize = '10px'; + flushSync(); + assert.deepEqual( + instance.get_and_clear_mutations(), + ['DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV'], + 'fontSize' + ); + divs.forEach((d) => assert.equal(d.style.fontSize, '10px')); + + component.fontSize = null; + flushSync(); + assert.deepEqual( + instance.get_and_clear_mutations(), + ['DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV', 'DIV'], + 'fontSize' + ); + divs.forEach((d) => assert.equal(d.style.fontSize, '')); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/style-directive-mutations/main.svelte b/packages/svelte/tests/runtime-runes/samples/style-directive-mutations/main.svelte new file mode 100644 index 0000000000..ae4da8ae37 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/style-directive-mutations/main.svelte @@ -0,0 +1,54 @@ +<script> + let { + margin = null, + color = 'red', + fontSize = '18px', + style1 = 'border: 1px solid', + style2 = 'border: 1px solid; margin: 1em', + style3 = 'color:blue; border: 1px solid; color: green;', + style4 = 'background:blue; background: linear-gradient(0, white 0%, red 100%)', + style5 = 'border: 1px solid; /* width: 100px; height: 100%; color: green */', + style6 = 'background: url(https://placehold.co/100x100?text=;&font=roboto);', + style7 = 'background: url("https://placehold.co/100x100?text=;&font=roboto");', + style8 = "background: url('https://placehold.co/100x100?text=;&font=roboto');", + + browser + } = $props(); + + let mutations = []; + let observer; + + if (browser) { + observer = new MutationObserver(update_mutation_records); + observer.observe(document.querySelector('#main'), { attributes: true, subtree: true }); + + $effect(() => { + return () => observer.disconnect(); + }); + } + + function update_mutation_records(results) { + for (const r of results) { + mutations.push(r.target.nodeName); + } + } + + export function get_and_clear_mutations() { + update_mutation_records(observer.takeRecords()); + const result = mutations; + mutations = []; + return result; + } +</script> + +<main id="main" style:color={browser?'white':'black'}> + <div style:margin style:color style:font-size|important={fontSize}></div> + <div style={style1} style:margin style:color style:font-size|important={fontSize}></div> + <div style={style2} style:margin style:color style:font-size|important={fontSize}></div> + <div style={style3} style:margin style:color style:font-size|important={fontSize}></div> + <div style={style4} style:margin style:color style:font-size|important={fontSize}></div> + <div style={style5} style:margin style:color style:font-size|important={fontSize}></div> + <div style={style6} style:margin style:color style:font-size|important={fontSize}></div> + <div style={style7} style:margin style:color style:font-size|important={fontSize}></div> + <div style={style8} style:margin style:color style:font-size|important={fontSize}></div> +</main> diff --git a/packages/svelte/tests/runtime-runes/samples/style-update/_config.js b/packages/svelte/tests/runtime-runes/samples/style-update/_config.js index f0b7f2648e..52690a431a 100644 --- a/packages/svelte/tests/runtime-runes/samples/style-update/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/style-update/_config.js @@ -3,6 +3,7 @@ import { test } from '../../test'; const style_1 = 'invalid-key:0; margin:4px;;color: green ;color:blue '; const style_2 = ' other-key : 0 ; padding:2px; COLOR:green; color: blue'; +const style_2_normalized = 'padding: 2px; color: blue;'; // https://github.com/sveltejs/svelte/issues/15309 export default test({ @@ -10,7 +11,7 @@ export default test({ style: style_1 }, - html: ` + ssrHtml: ` <div style="${style_1}"></div> <div style="${style_1}"></div> @@ -25,11 +26,11 @@ export default test({ assert.htmlEqual( target.innerHTML, ` - <div style="${style_2}"></div> - <div style="${style_2}"></div> + <div style="${style_2_normalized}"></div> + <div style="${style_2_normalized}"></div> - <custom-element style="${style_2}"></custom-element> - <custom-element style="${style_2}"></custom-element> + <custom-element style="${style_2_normalized}"></custom-element> + <custom-element style="${style_2_normalized}"></custom-element> ` ); From ae615ae2acb0a5574547b9161390a2761f60d2d4 Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Thu, 6 Mar 2025 02:52:34 +0100 Subject: [PATCH 0236/1429] chore: memoize clsx() (alternative) (#15456) * memoize clsx + directives * changeset * unused * tweak * tweak changeset --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/flat-jars-search.md | 5 +++++ .../3-transform/client/visitors/RegularElement.js | 15 +++++++-------- .../3-transform/client/visitors/shared/element.js | 14 +++++++------- .../src/internal/client/dom/elements/class.js | 2 +- 4 files changed, 20 insertions(+), 16 deletions(-) create mode 100644 .changeset/flat-jars-search.md diff --git a/.changeset/flat-jars-search.md b/.changeset/flat-jars-search.md new file mode 100644 index 0000000000..fc0de76f95 --- /dev/null +++ b/.changeset/flat-jars-search.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: memoize `clsx` calls diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 6122dc4e0e..9b3ecc922d 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -511,22 +511,21 @@ function setup_select_synchronization(value_binding, context) { /** * @param {AST.ClassDirective[]} class_directives * @param {ComponentContext} context - * @return {ObjectExpression} + * @return {ObjectExpression | Identifier} */ export function build_class_directives_object(class_directives, context) { let properties = []; + let has_call_or_state = false; for (const d of class_directives) { - let expression = /** @type Expression */ (context.visit(d.expression)); - - if (d.metadata.expression.has_call) { - expression = get_expression_id(context.state, expression); - } - + const expression = /** @type Expression */ (context.visit(d.expression)); properties.push(b.init(d.name, expression)); + has_call_or_state ||= d.metadata.expression.has_call || d.metadata.expression.has_state; } - return b.object(properties); + const directives = b.object(properties); + + return has_call_or_state ? get_expression_id(context.state, directives) : directives; } /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index e0eb04d823..084c1e7c67 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -162,13 +162,13 @@ export function get_attribute_name(element, attribute) { * @param {boolean} is_html */ export function build_set_class(element, node_id, attribute, class_directives, context, is_html) { - let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => - metadata.has_call ? get_expression_id(context.state, value) : value - ); + let { value, has_state } = build_attribute_value(attribute.value, context, (value, metadata) => { + if (attribute.metadata.needs_clsx) { + value = b.call('$.clsx', value); + } - if (attribute && attribute.metadata.needs_clsx) { - value = b.call('$.clsx', value); - } + return metadata.has_call ? get_expression_id(context.state, value) : value; + }); /** @type {Identifier | undefined} */ let previous_id; @@ -176,7 +176,7 @@ export function build_set_class(element, node_id, attribute, class_directives, c /** @type {ObjectExpression | Identifier | undefined} */ let prev; - /** @type {ObjectExpression | undefined} */ + /** @type {ObjectExpression | Identifier | undefined} */ let next; if (class_directives.length) { diff --git a/packages/svelte/src/internal/client/dom/elements/class.js b/packages/svelte/src/internal/client/dom/elements/class.js index 7027c84f62..ecbfcbc010 100644 --- a/packages/svelte/src/internal/client/dom/elements/class.js +++ b/packages/svelte/src/internal/client/dom/elements/class.js @@ -33,7 +33,7 @@ export function set_class(dom, is_html, value, hash, prev_classes, next_classes) // @ts-expect-error need to add __className to patched prototype dom.__className = value; - } else if (next_classes) { + } else if (next_classes && prev_classes !== next_classes) { for (var key in next_classes) { var is_present = !!next_classes[key]; From d513304dd063f8d01d470b9a4a476cdb24d9d20c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 20:55:52 -0500 Subject: [PATCH 0237/1429] Version Packages (#15453) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/flat-jars-search.md | 5 ----- .changeset/quiet-baboons-listen.md | 5 ----- .changeset/real-cameras-attack.md | 5 ----- .changeset/strange-planes-shout.md | 5 ----- packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 7 files changed, 14 insertions(+), 22 deletions(-) delete mode 100644 .changeset/flat-jars-search.md delete mode 100644 .changeset/quiet-baboons-listen.md delete mode 100644 .changeset/real-cameras-attack.md delete mode 100644 .changeset/strange-planes-shout.md diff --git a/.changeset/flat-jars-search.md b/.changeset/flat-jars-search.md deleted file mode 100644 index fc0de76f95..0000000000 --- a/.changeset/flat-jars-search.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: memoize `clsx` calls diff --git a/.changeset/quiet-baboons-listen.md b/.changeset/quiet-baboons-listen.md deleted file mode 100644 index eb5b4cc699..0000000000 --- a/.changeset/quiet-baboons-listen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"svelte": patch ---- - -fix: respect `svelte-ignore hydration_attribute_changed` on elements with spread attributes diff --git a/.changeset/real-cameras-attack.md b/.changeset/real-cameras-attack.md deleted file mode 100644 index 35e2764785..0000000000 --- a/.changeset/real-cameras-attack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: always use `setAttribute` when setting `style` diff --git a/.changeset/strange-planes-shout.md b/.changeset/strange-planes-shout.md deleted file mode 100644 index 58ef252740..0000000000 --- a/.changeset/strange-planes-shout.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: make `style:` directive and CSS handling more robust diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 0ff6b62fe0..44f1f7cb79 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.22.5 + +### Patch Changes + +- fix: memoize `clsx` calls ([#15456](https://github.com/sveltejs/svelte/pull/15456)) + +- fix: respect `svelte-ignore hydration_attribute_changed` on elements with spread attributes ([#15443](https://github.com/sveltejs/svelte/pull/15443)) + +- fix: always use `setAttribute` when setting `style` ([#15323](https://github.com/sveltejs/svelte/pull/15323)) + +- fix: make `style:` directive and CSS handling more robust ([#15418](https://github.com/sveltejs/svelte/pull/15418)) + ## 5.22.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 1f95811cd5..fb20167a4e 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.22.4", + "version": "5.22.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 845375314f..e20a9683dd 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.22.4'; +export const VERSION = '5.22.5'; export const PUBLIC_VERSION = '5'; From 2c4d85bcec74f78a6291b07920c423908714aefc Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 5 Mar 2025 18:49:52 -0800 Subject: [PATCH 0238/1429] docs: address `$effect` feedback (#15107) * docs: address $effect feedback * also add a note to the migration guide * minor wording tweak * update onMount docs * Update documentation/docs/02-runes/05-$effect.md Co-authored-by: Rich Harris <rich.harris@vercel.com> * restore order * soften a bit * add back mention of updating template in response to effects * define parent effect * state that they don't run on the server * Update documentation/docs/02-runes/04-$effect.md Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> * format * Apply suggestions from code review Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> * update onMount docs * add 'Understanding lifecycle' section * note * tweak wording --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- documentation/docs/02-runes/04-$effect.md | 30 +++++++++++-------- .../docs/07-misc/07-v5-migration-guide.md | 2 ++ packages/svelte/src/index-client.js | 11 +++---- packages/svelte/types/index.d.ts | 11 +++---- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md index da24084d4d..e346bceba8 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -2,15 +2,11 @@ title: $effect --- -Effects are what make your application _do things_. When Svelte runs an effect function, it tracks which pieces of state (and derived state) are accessed (unless accessed inside [`untrack`](svelte#untrack)), and re-runs the function when that state later changes. +Effects are functions that run when state updates, and can be used for things like calling third-party libraries, drawing on `<canvas>` elements, or making network requests. They only run in the browser, not during server-side rendering. -Most of the effects in a Svelte app are created by Svelte itself — they're the bits that update the text in `<h1>hello {name}!</h1>` when `name` changes, for example. +Generally speaking, you should _not_ update state inside effects, as it will make code more convoluted and will often lead to never-ending update cycles. If you find yourself doing so, see [when not to use `$effect`](#When-not-to-use-$effect) to learn about alternative approaches. -But you can also create your own effects with the `$effect` rune, which is useful when you need to synchronize an external system (whether that's a library, or a `<canvas>` element, or something across a network) with state inside your Svelte app. - -> [!NOTE] Avoid overusing `$effect`! When you do too much work in effects, code often becomes difficult to understand and maintain. See [when not to use `$effect`](#When-not-to-use-$effect) to learn about alternative approaches. - -Your effects run after the component has been mounted to the DOM, and in a [microtask](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide) after state changes ([demo](/playground/untitled#H4sIAAAAAAAAE31S246bMBD9lZF3pSRSAqTVvrCAVPUP2sdSKY4ZwJJjkD0hSVH-vbINuWxXfQH5zMyZc2ZmZLVUaFn6a2R06ZGlHmBrpvnBvb71fWQHVOSwPbf4GS46TajJspRlVhjZU1HqkhQSWPkHIYdXS5xw-Zas3ueI6FRn7qHFS11_xSRZhIxbFtcDtw7SJb1iXaOg5XIFeQGjzyPRaevYNOGZIJ8qogbpe8CWiy_VzEpTXiQUcvPDkSVrSNZz1UlW1N5eLcqmpdXUvaQ4BmqlhZNUCgxuzFHDqUWNAxrYeUM76AzsnOsdiJbrBp_71lKpn3RRbii-4P3f-IMsRxS-wcDV_bL4PmSdBa2wl7pKnbp8DMgVvJm8ZNskKRkEM_OzyOKQFkgqOYBQ3Nq89Ns0nbIl81vMFN-jKoLMTOr-SOBOJS-Z8f5Y6D1wdcR8dFqvEBdetK-PHwj-z-cH8oHPY54wRJ8Ys7iSQ3Bg3VA9azQbmC9k35kKzYa6PoVtfwbbKVnBixBiGn7Pq0rqJoUtHiCZwAM3jdTPWCVtr_glhVrhecIa3vuksJ_b7TqFs4DPyriSjd5IwoNNQaAmNI-ESfR2p8zimzvN1swdCkvJHPH6-_oX8o1SgcIDAAA=)): +You can create an effect with the `$effect` rune ([demo](/playground/untitled#H4sIAAAAAAAAE31S246bMBD9lZF3pSRSAqTVvrCAVPUP2sdSKY4ZwJJjkD0hSVH-vbINuWxXfQH5zMyZc2ZmZLVUaFn6a2R06ZGlHmBrpvnBvb71fWQHVOSwPbf4GS46TajJspRlVhjZU1HqkhQSWPkHIYdXS5xw-Zas3ueI6FRn7qHFS11_xSRZhIxbFtcDtw7SJb1iXaOg5XIFeQGjzyPRaevYNOGZIJ8qogbpe8CWiy_VzEpTXiQUcvPDkSVrSNZz1UlW1N5eLcqmpdXUvaQ4BmqlhZNUCgxuzFHDqUWNAxrYeUM76AzsnOsdiJbrBp_71lKpn3RRbii-4P3f-IMsRxS-wcDV_bL4PmSdBa2wl7pKnbp8DMgVvJm8ZNskKRkEM_OzyOKQFkgqOYBQ3Nq89Ns0nbIl81vMFN-jKoLMTOr-SOBOJS-Z8f5Y6D1wdcR8dFqvEBdetK-PHwj-z-cH8oHPY54wRJ8Ys7iSQ3Bg3VA9azQbmC9k35kKzYa6PoVtfwbbKVnBixBiGn7Pq0rqJoUtHiCZwAM3jdTPWCVtr_glhVrhecIa3vuksJ_b7TqFs4DPyriSjd5IwoNNQaAmNI-ESfR2p8zimzvN1swdCkvJHPH6-_oX8o1SgcIDAAA=)): ```svelte <script> @@ -32,11 +28,19 @@ Your effects run after the component has been mounted to the DOM, and in a [micr <canvas bind:this={canvas} width="100" height="100" /> ``` -Re-runs are batched (i.e. changing `color` and `size` in the same moment won't cause two separate runs), and happen after any DOM updates have been applied. +When Svelte runs an effect function, it tracks which pieces of state (and derived state) are accessed (unless accessed inside [`untrack`](svelte#untrack)), and re-runs the function when that state later changes. + +> [!NOTE] If you're having difficulty understanding why your `$effect` is rerunning or is not running see [understanding dependencies](#Understanding-dependencies). Effects are triggered differently than the `$:` blocks you may be used to if coming from Svelte 4. + +### Understanding lifecycle -You can place `$effect` anywhere, not just at the top level of a component, as long as it is called during component initialization (or while a parent effect is active). It is then tied to the lifecycle of the component (or parent effect) and will therefore destroy itself when the component unmounts (or the parent effect is destroyed). +Your effects run after the component has been mounted to the DOM, and in a [microtask](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide) after state changes. Re-runs are batched (i.e. changing `color` and `size` in the same moment won't cause two separate runs), and happen after any DOM updates have been applied. -You can return a function from `$effect`, which will run immediately before the effect re-runs, and before it is destroyed ([demo](/playground/untitled#H4sIAAAAAAAAE42RQY-bMBCF_8rI2kPopiXpMQtIPfbeW6m0xjyKtWaM7CFphPjvFVB2k2oPe7LmzXzyezOjaqxDVKefo5JrD3VaBLVXrLu5-tb3X-IZTmat0hHv6cazgCWqk8qiCbaXouRSHISMH1gop4coWrA7JE9bp7PO2QjjuY5vA8fDYZ3hUh7QNDCy2yWUFzTOUilpSj9aG-linaMKFGACtKCmSwvGGYGeLQvCWbtnMq3m34grajxHoa1JOUXI93_V_Sfz7Oz7Mafj0ypN-zvHm8dSAmQITP_xaUq2IU1GO1dp80I2Uh_82dao92Rl9R8GvgF0QrbrUFstcFeq0PgAkha0LoICPoeB4w1SJUvsZcj4rvcMlvmvGlGCv6J-DeSgw2vabQnJlm55p7nM0rcTctYei3HZxZSl7XHVqkHEM3k2zpqXfFyj393zU05fpyI6f0HI0hUoPoamC9roKDeo2ivBH1EnCQOmX9NfYw2GHrgCAAA=)). +You can use `$effect` anywhere, not just at the top level of a component, as long as it is called while a parent effect is running. + +> [!NOTE] Svelte uses effects internally to represent logic and expressions in your template — this is how `<h1>hello {name}!</h1>` updates when `name` changes. + +An effect can return a _teardown function_ which will run immediately before the effect re-runs ([demo](/playground/untitled#H4sIAAAAAAAAE42SQVODMBCF_8pOxkPRKq3HCsx49K4n64xpskjGkDDJ0tph-O8uINo6HjxB3u7HvrehE07WKDbiyZEhi1osRWksRrF57gQdm6E2CKx_dd43zU3co6VB28mIf-nKO0JH_BmRRRVMQ8XWbXkAgfKtI8jhIpIkXKySu7lSG2tNRGZ1_GlYr1ZTD3ddYFmiosUigbyAbpC2lKbwWJkIB8ZhhxBQBWRSw6FCh3sM8GrYTthL-wqqku4N44TyqEgwF3lmRHr4Op0PGXoH31c5rO8mqV-eOZ49bikgtcHBL55tmhIkEMqg_cFB2TpFxjtg703we6NRL8HQFCS07oSUCZi6Rm04lz1yytIHBKoQpo1w6Gsm4gmyS8b8Y5PydeMdX8gwS2Ok4I-ov5NZtvQde95GMsccn_1wzNKfu3RZtS66cSl9lvL7qO1aIk7knbJGvefdtIOzi73M4bYvovUHDFk6AcX_0HRESxnpBOW_jfCDxIZCi_1L_wm4xGQ60wIAAA==)). ```svelte <script> @@ -50,7 +54,7 @@ You can return a function from `$effect`, which will run immediately before the }, milliseconds); return () => { - // if a callback is provided, it will run + // if a teardown function is provided, it will run // a) immediately before the effect re-runs // b) when the component is destroyed clearInterval(interval); @@ -64,9 +68,11 @@ You can return a function from `$effect`, which will run immediately before the <button onclick={() => (milliseconds /= 2)}>faster</button> ``` +Teardown functions also run when the effect is destroyed, which happens when its parent is destroyed (for example, a component is unmounted) or the parent effect re-runs. + ### Understanding dependencies -`$effect` automatically picks up any reactive values (`$state`, `$derived`, `$props`) that are _synchronously_ read inside its function body (including indirectly, via function calls) and registers them as dependencies. When those dependencies change, the `$effect` schedules a rerun. +`$effect` automatically picks up any reactive values (`$state`, `$derived`, `$props`) that are _synchronously_ read inside its function body (including indirectly, via function calls) and registers them as dependencies. When those dependencies change, the `$effect` schedules a re-run. Values that are read _asynchronously_ — after an `await` or inside a `setTimeout`, for example — will not be tracked. Here, the canvas will be repainted when `color` changes, but not when `size` changes ([demo](/playground/untitled#H4sIAAAAAAAAE31T246bMBD9lZF3pWSlBEirfaEQqdo_2PatVIpjBrDkGGQPJGnEv1e2IZfVal-wfHzmzJyZ4cIqqdCy9M-F0blDlnqArZjmB3f72XWRHVCRw_bc4me4aDWhJstSlllhZEfbQhekkMDKfwg5PFvihMvX5OXH_CJa1Zrb0-Kpqr5jkiwC48rieuDWQbqgZ6wqFLRcvkC-hYvnkWi1dWqa8ESQTxFRjfQWsOXiWzmr0sSLhEJu3p1YsoJkNUcdZUnN9dagrBu6FVRQHAM10sJRKgUG16bXcGxQ44AGdt7SDkTDdY02iqLHnJVU6hedlWuIp94JW6Tf8oBt_8GdTxlF0b4n0C35ZLBzXb3mmYn3ae6cOW74zj0YVzDNYXRHFt9mprNgHfZSl6mzml8CMoLvTV6wTZIUDEJv5us2iwMtiJRyAKG4tXnhl8O0yhbML0Wm-B7VNlSSSd31BG7z8oIZZ6dgIffAVY_5xdU9Qrz1Bnx8fCfwtZ7v8Qc9j3nB8PqgmMWlHIID6-bkVaPZwDySfWtKNGtquxQ23Qlsq2QJT0KIqb8dL0up6xQ2eIBkAg_c1FI_YqW0neLnFCqFpwmreedJYT7XX8FVOBfwWRhXstZrSXiwKQjUhOZeMIleb5JZfHWn2Yq5pWEpmR7Hv-N_wEqT8hEEAAA=)): diff --git a/documentation/docs/07-misc/07-v5-migration-guide.md b/documentation/docs/07-misc/07-v5-migration-guide.md index 94ade6e887..87ff40cf47 100644 --- a/documentation/docs/07-misc/07-v5-migration-guide.md +++ b/documentation/docs/07-misc/07-v5-migration-guide.md @@ -51,6 +51,8 @@ A `$:` statement could also be used to create side effects. In Svelte 5, this is </script> ``` +Note that [when `$effect` runs is different]($effect#Understanding-dependencies) than when `$:` runs. + > [!DETAILS] Why we did this > `$:` was a great shorthand and easy to get started with: you could slap a `$:` in front of most code and it would somehow work. This intuitiveness was also its drawback the more complicated your code became, because it wasn't as easy to reason about. Was the intent of the code to create a derivation, or a side effect? With `$derived` and `$effect`, you have a bit more up-front decision making to do (spoiler alert: 90% of the time you want `$derived`), but future-you and other developers on your team will have an easier time. > diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index efcf7b727b..fd8e999da7 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -45,13 +45,14 @@ if (DEV) { } /** - * The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. - * It must be called during the component's initialisation (but doesn't need to live *inside* the component; - * it can be called from an external module). + * `onMount`, like [`$effect`](https://svelte.dev/docs/svelte/$effect), schedules a function to run as soon as the component has been mounted to the DOM. + * Unlike `$effect`, the provided function only runs once. * - * If a function is returned _synchronously_ from `onMount`, it will be called when the component is unmounted. + * It must be called during the component's initialisation (but doesn't need to live _inside_ the component; + * it can be called from an external module). If a function is returned _synchronously_ from `onMount`, + * it will be called when the component is unmounted. * - * `onMount` does not run inside [server-side components](https://svelte.dev/docs/svelte/svelte-server#render). + * `onMount` functions do not run during [server-side rendering](https://svelte.dev/docs/svelte/svelte-server#render). * * @template T * @param {() => NotFunction<T> | Promise<NotFunction<T>> | (() => any)} fn diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index c3dbdcac79..c6000fc4b6 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -349,13 +349,14 @@ declare module 'svelte' { props: Props; }); /** - * The `onMount` function schedules a callback to run as soon as the component has been mounted to the DOM. - * It must be called during the component's initialisation (but doesn't need to live *inside* the component; - * it can be called from an external module). + * `onMount`, like [`$effect`](https://svelte.dev/docs/svelte/$effect), schedules a function to run as soon as the component has been mounted to the DOM. + * Unlike `$effect`, the provided function only runs once. * - * If a function is returned _synchronously_ from `onMount`, it will be called when the component is unmounted. + * It must be called during the component's initialisation (but doesn't need to live _inside_ the component; + * it can be called from an external module). If a function is returned _synchronously_ from `onMount`, + * it will be called when the component is unmounted. * - * `onMount` does not run inside [server-side components](https://svelte.dev/docs/svelte/svelte-server#render). + * `onMount` functions do not run during [server-side rendering](https://svelte.dev/docs/svelte/svelte-server#render). * * */ export function onMount<T>(fn: () => NotFunction<T> | Promise<NotFunction<T>> | (() => any)): void; From c5912aad71e77e75e4dcec8c27acb49e412f92f2 Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Fri, 7 Mar 2025 15:30:56 +0100 Subject: [PATCH 0239/1429] fix: null and warnings for local handlers (#15460) * fix null and warning for local handlers * test * changeset * treat `let handler = () => {...}` the same as `function handler() {...}` --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/shy-mirrors-remain.md | 5 ++ .../client/visitors/shared/events.js | 26 +++++++--- packages/svelte/src/compiler/phases/scope.js | 15 ++++++ .../internal/client/dom/elements/events.js | 11 ++--- .../event-handler-invalid-values/_config.js | 48 +++++++++++++++++++ .../event-handler-invalid-values/main.svelte | 10 ++++ 6 files changed, 102 insertions(+), 13 deletions(-) create mode 100644 .changeset/shy-mirrors-remain.md create mode 100644 packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/main.svelte diff --git a/.changeset/shy-mirrors-remain.md b/.changeset/shy-mirrors-remain.md new file mode 100644 index 0000000000..028f7beb68 --- /dev/null +++ b/.changeset/shy-mirrors-remain.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: null and warnings for local handlers diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/events.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/events.js index f23f7548ec..2667a96f6a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/events.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/events.js @@ -46,8 +46,12 @@ export function visit_event_attribute(node, context) { // When we hoist a function we assign an array with the function and all // hoisted closure params. - const args = [handler, ...hoisted_params]; - delegated_assignment = b.array(args); + if (hoisted_params) { + const args = [handler, ...hoisted_params]; + delegated_assignment = b.array(args); + } else { + delegated_assignment = handler; + } } else { delegated_assignment = handler; } @@ -123,11 +127,19 @@ export function build_event_handler(node, metadata, context) { } // function declared in the script - if ( - handler.type === 'Identifier' && - context.state.scope.get(handler.name)?.declaration_kind !== 'import' - ) { - return handler; + if (handler.type === 'Identifier') { + const binding = context.state.scope.get(handler.name); + + if (binding?.is_function()) { + return handler; + } + + // local variable can be assigned directly + // except in dev mode where when need $.apply() + // in order to handle warnings. + if (!dev && binding?.declaration_kind !== 'import') { + return handler; + } } if (metadata.has_call) { diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 7d9f90982a..a5227c1b51 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -79,6 +79,21 @@ export class Binding { get updated() { return this.mutated || this.reassigned; } + + is_function() { + if (this.reassigned) { + // even if it's reassigned to another function, + // we can't use it directly as e.g. an event handler + return false; + } + + if (this.declaration_kind === 'function') { + return true; + } + + const type = this.initial?.type; + return type === 'ArrowFunctionExpression' || type === 'FunctionExpression'; + } } export class Scope { diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js index 25ece5f569..0c1bb1dada 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -238,7 +238,7 @@ export function handle_event_propagation(event) { var delegated = current_target['__' + event_name]; if ( - delegated !== undefined && + delegated != null && (!(/** @type {any} */ (current_target).disabled) || // DOM could've been updated already by the time this is reached, so we check this as well // -> the target could not have been disabled because it emits the event in the first place @@ -311,13 +311,11 @@ export function apply( error = e; } - if (typeof handler === 'function') { - handler.apply(element, args); - } else if (has_side_effects || handler != null || error) { + if (typeof handler !== 'function' && (has_side_effects || handler != null || error)) { const filename = component?.[FILENAME]; const location = loc ? ` at ${filename}:${loc[0]}:${loc[1]}` : ` in ${filename}`; - - const event_name = args[0].type; + const phase = args[0]?.eventPhase < Event.BUBBLING_PHASE ? 'capture' : ''; + const event_name = args[0]?.type + phase; const description = `\`${event_name}\` handler${location}`; const suggestion = remove_parens ? 'remove the trailing `()`' : 'add a leading `() =>`'; @@ -327,4 +325,5 @@ export function apply( throw error; } } + handler?.apply(element, args); } diff --git a/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/_config.js b/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/_config.js new file mode 100644 index 0000000000..d53812d4c3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/_config.js @@ -0,0 +1,48 @@ +import { assertType } from 'vitest'; +import { test } from '../../test'; + +export default test({ + mode: ['client'], + + compileOptions: { + dev: true + }, + + test({ assert, target, warnings, logs }) { + /** @type {any} */ + let error = null; + + const handler = (/** @type {any} */ e) => { + error = e.error; + e.stopImmediatePropagation(); + }; + + window.addEventListener('error', handler, true); + + const [b1, b2, b3] = target.querySelectorAll('button'); + + b1.click(); + assert.deepEqual(logs, []); + assert.equal(error, null); + + error = null; + logs.length = 0; + + b2.click(); + assert.deepEqual(logs, ['clicked']); + assert.equal(error, null); + + error = null; + logs.length = 0; + + b3.click(); + assert.deepEqual(logs, []); + assert.deepEqual(warnings, [ + '`click` handler at main.svelte:10:17 should be a function. Did you mean to add a leading `() =>`?' + ]); + assert.isNotNull(error); + assert.match(error.message, /is not a function/); + + window.removeEventListener('error', handler, true); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/main.svelte b/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/main.svelte new file mode 100644 index 0000000000..f6e344ece8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/main.svelte @@ -0,0 +1,10 @@ +<script> + let ignore = null; + let handler = () => console.log("clicked"); + let bad = "invalid"; + +</script> + +<button onclick={ignore}>click</button> +<button onclick={handler}>click</button> +<button onclick={bad}>click</button> From 3326bd8ae77a43b6ae2b03970969066e716f6063 Mon Sep 17 00:00:00 2001 From: "Trevor N. Suarez" <Rican7@gmail.com> Date: Fri, 7 Mar 2025 08:02:02 -0700 Subject: [PATCH 0240/1429] feat: Add `closedby` to `HTMLDialogAttributes` (dialog element) (#15458) * Adding the `closedby` attribute to dialog element Spec: https://html.spec.whatwg.org/#attr-dialog-closedby * Adding changeset * Update .changeset/metal-spoons-scream.md --------- Co-authored-by: Rich Harris <hello@rich-harris.dev> --- .changeset/metal-spoons-scream.md | 5 +++++ packages/svelte/elements.d.ts | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/metal-spoons-scream.md diff --git a/.changeset/metal-spoons-scream.md b/.changeset/metal-spoons-scream.md new file mode 100644 index 0000000000..2eb7b7140c --- /dev/null +++ b/.changeset/metal-spoons-scream.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: Add `closedby` property to HTMLDialogAttributes type diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts index 6d256b5620..08687cafaf 100644 --- a/packages/svelte/elements.d.ts +++ b/packages/svelte/elements.d.ts @@ -957,6 +957,7 @@ export interface HTMLDelAttributes extends HTMLAttributes<HTMLModElement> { export interface HTMLDialogAttributes extends HTMLAttributes<HTMLDialogElement> { open?: boolean | undefined | null; + closedby?: 'any' | 'closerequest' | 'none' | undefined | null; } export interface HTMLEmbedAttributes extends HTMLAttributes<HTMLEmbedElement> { From eaf0087d7c5671794ee25521667860b1a8af1828 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 7 Mar 2025 10:02:58 -0500 Subject: [PATCH 0241/1429] fix: skip `log_if_contains_state` if only logging literals (#15468) --- .changeset/hungry-monkeys-fly.md | 5 +++++ .../phases/3-transform/client/visitors/CallExpression.js | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changeset/hungry-monkeys-fly.md diff --git a/.changeset/hungry-monkeys-fly.md b/.changeset/hungry-monkeys-fly.md new file mode 100644 index 0000000000..f52c8dad92 --- /dev/null +++ b/.changeset/hungry-monkeys-fly.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: skip `log_if_contains_state` if only logging literals diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js index 7a3057451a..fda43ad791 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js @@ -44,7 +44,8 @@ export function CallExpression(node, context) { node.callee.property.type === 'Identifier' && ['debug', 'dir', 'error', 'group', 'groupCollapsed', 'info', 'log', 'trace', 'warn'].includes( node.callee.property.name - ) + ) && + node.arguments.some((arg) => arg.type !== 'Literal') // TODO more cases? ) { return b.call( node.callee, From e2bbc560e434df055402eb018789754a41afc456 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 7 Mar 2025 10:07:50 -0500 Subject: [PATCH 0242/1429] Version Packages (#15466) Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> --- .changeset/hungry-monkeys-fly.md | 5 ----- .changeset/metal-spoons-scream.md | 5 ----- .changeset/shy-mirrors-remain.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 .changeset/hungry-monkeys-fly.md delete mode 100644 .changeset/metal-spoons-scream.md delete mode 100644 .changeset/shy-mirrors-remain.md diff --git a/.changeset/hungry-monkeys-fly.md b/.changeset/hungry-monkeys-fly.md deleted file mode 100644 index f52c8dad92..0000000000 --- a/.changeset/hungry-monkeys-fly.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: skip `log_if_contains_state` if only logging literals diff --git a/.changeset/metal-spoons-scream.md b/.changeset/metal-spoons-scream.md deleted file mode 100644 index 2eb7b7140c..0000000000 --- a/.changeset/metal-spoons-scream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: Add `closedby` property to HTMLDialogAttributes type diff --git a/.changeset/shy-mirrors-remain.md b/.changeset/shy-mirrors-remain.md deleted file mode 100644 index 028f7beb68..0000000000 --- a/.changeset/shy-mirrors-remain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: null and warnings for local handlers diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 44f1f7cb79..a05938dacc 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.22.6 + +### Patch Changes + +- fix: skip `log_if_contains_state` if only logging literals ([#15468](https://github.com/sveltejs/svelte/pull/15468)) + +- fix: Add `closedby` property to HTMLDialogAttributes type ([#15458](https://github.com/sveltejs/svelte/pull/15458)) + +- fix: null and warnings for local handlers ([#15460](https://github.com/sveltejs/svelte/pull/15460)) + ## 5.22.5 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index fb20167a4e..53f03e3543 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.22.5", + "version": "5.22.6", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index e20a9683dd..01e1b390a3 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.22.5'; +export const VERSION = '5.22.6'; export const PUBLIC_VERSION = '5'; From 1c0e24013fff52e106870dfc7e4b38a817c25610 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Mon, 10 Mar 2025 11:22:21 -0400 Subject: [PATCH 0243/1429] chore: reuse is_function helper (#15467) --- .../phases/2-analyze/visitors/Attribute.js | 12 ++---------- packages/svelte/src/compiler/phases/scope.js | 18 +++++++++++------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js index 561a004526..9124a8822f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -162,16 +162,8 @@ function get_delegated_event(event_name, handler, context) { return unhoisted; } - if (binding !== null && binding.initial !== null && !binding.updated) { - const binding_type = binding.initial.type; - - if ( - binding_type === 'ArrowFunctionExpression' || - binding_type === 'FunctionDeclaration' || - binding_type === 'FunctionExpression' - ) { - target_function = binding.initial; - } + if (binding?.is_function()) { + target_function = binding.initial; } } diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index a5227c1b51..b6063c3234 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -1,4 +1,4 @@ -/** @import { ClassDeclaration, Expression, FunctionDeclaration, Identifier, ImportDeclaration, MemberExpression, Node, Pattern, VariableDeclarator } from 'estree' */ +/** @import { ArrowFunctionExpression, ClassDeclaration, Expression, FunctionDeclaration, FunctionExpression, Identifier, ImportDeclaration, MemberExpression, Node, Pattern, VariableDeclarator } from 'estree' */ /** @import { Context, Visitor } from 'zimmerframe' */ /** @import { AST, BindingKind, DeclarationKind } from '#compiler' */ import is_reference from 'is-reference'; @@ -80,19 +80,23 @@ export class Binding { return this.mutated || this.reassigned; } + /** + * @returns {this is Binding & { initial: ArrowFunctionExpression | FunctionDeclaration | FunctionExpression }} + */ is_function() { - if (this.reassigned) { + if (this.updated) { // even if it's reassigned to another function, // we can't use it directly as e.g. an event handler return false; } - if (this.declaration_kind === 'function') { - return true; - } - const type = this.initial?.type; - return type === 'ArrowFunctionExpression' || type === 'FunctionExpression'; + + return ( + type === 'ArrowFunctionExpression' || + type === 'FunctionExpression' || + type === 'FunctionDeclaration' + ); } } From 81480c40a02678bd02d89e2e20d50a6ddd3ec383 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Mon, 10 Mar 2025 12:48:50 -0700 Subject: [PATCH 0244/1429] chore: add missing permissions for `pkg.pr.new-comment` (#15489) --- .github/workflows/pkg.pr.new-comment.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/pkg.pr.new-comment.yml b/.github/workflows/pkg.pr.new-comment.yml index b1fba0b04b..3f1fca5a0b 100644 --- a/.github/workflows/pkg.pr.new-comment.yml +++ b/.github/workflows/pkg.pr.new-comment.yml @@ -6,6 +6,9 @@ on: types: - completed +permissions: + pull-requests: write + jobs: build: name: 'Update comment' From dbd4617ac42a4bf5e97af05f2d5bc6a0517e24b2 Mon Sep 17 00:00:00 2001 From: Maple <52185471+fuuki12@users.noreply.github.com> Date: Tue, 11 Mar 2025 06:52:22 +0900 Subject: [PATCH 0245/1429] docs: correct toggle function in lifecycle hooks example (#15486) --- documentation/docs/06-runtime/03-lifecycle-hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/06-runtime/03-lifecycle-hooks.md b/documentation/docs/06-runtime/03-lifecycle-hooks.md index 2b97ca796f..f051c46d73 100644 --- a/documentation/docs/06-runtime/03-lifecycle-hooks.md +++ b/documentation/docs/06-runtime/03-lifecycle-hooks.md @@ -147,7 +147,7 @@ With runes, we can use `$effect.pre`, which behaves the same as `$effect` but ru } function toggle() { - toggleValue = !toggleValue; + theme = theme === 'dark' ? 'light' : 'dark'; } </script> From 1cc5bcdc999716673d15844bd1190758f882ba05 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 11 Mar 2025 18:57:58 +0100 Subject: [PATCH 0246/1429] chore: clarify fuzzyset adaption (#15491) it was BSD in 2016 but has undergone some license changes since then - clarify in the comment that the adaption was from the 2016 BSD version --- .../svelte/src/compiler/phases/1-parse/utils/fuzzymatch.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/fuzzymatch.js b/packages/svelte/src/compiler/phases/1-parse/utils/fuzzymatch.js index cd72d73005..28b314cdd5 100644 --- a/packages/svelte/src/compiler/phases/1-parse/utils/fuzzymatch.js +++ b/packages/svelte/src/compiler/phases/1-parse/utils/fuzzymatch.js @@ -12,8 +12,8 @@ export default function fuzzymatch(name, names) { return matches && matches[0][0] > 0.7 ? matches[0][1] : null; } -// adapted from https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js -// BSD Licensed +// adapted from https://github.com/Glench/fuzzyset.js/blob/master/lib/fuzzyset.js in 2016 +// BSD Licensed (see https://github.com/Glench/fuzzyset.js/issues/10) const GRAM_SIZE_LOWER = 2; const GRAM_SIZE_UPPER = 3; From 110d42062fb1a98698d2a68a39919cc44962613d Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Tue, 11 Mar 2025 18:48:55 +0000 Subject: [PATCH 0247/1429] fix: on teardown, use the last known value for the signal before the set (#15469) * fix: on teardown, use the last known value for the signal before the se * fix: on teardown, use the last known value for the signal before the se * fix: on teardown, use the last known value for the signal before the se * fix: on teardown, use the last known value for the signal before the se * fix: on teardown, use the last known value for the signal before the se * Update packages/svelte/src/internal/client/reactivity/props.js Co-authored-by: Rich Harris <rich.harris@vercel.com> * Update packages/svelte/src/internal/client/reactivity/props.js Co-authored-by: Rich Harris <rich.harris@vercel.com> * Update packages/svelte/src/internal/client/reactivity/props.js Co-authored-by: Rich Harris <rich.harris@vercel.com> * lint * lint * lint * Update .changeset/sharp-elephants-invite.md --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/sharp-elephants-invite.md | 5 ++ .../2-analyze/visitors/CallExpression.js | 3 + .../svelte/src/internal/client/context.js | 11 ++- .../src/internal/client/reactivity/props.js | 43 +++++++----- .../src/internal/client/reactivity/sources.js | 11 ++- .../svelte/src/internal/client/runtime.js | 7 +- .../svelte/src/internal/client/types.d.ts | 2 + .../ondestroy-prop-access-2/Component.svelte | 11 +++ .../ondestroy-prop-access-2/_config.js | 14 ++++ .../ondestroy-prop-access-2/main.svelte | 15 ++++ .../ondestroy-prop-access-3/Component.svelte | 5 ++ .../ondestroy-prop-access-3/_config.js | 11 +++ .../ondestroy-prop-access-3/main.svelte | 16 +++++ .../ondestroy-prop-access/Component.svelte | 12 ++++ .../samples/ondestroy-prop-access/_config.js | 68 +++++++++++++++++++ .../samples/ondestroy-prop-access/main.svelte | 41 +++++++++++ .../samples/nested-effect-conflict/_config.js | 10 +-- 17 files changed, 254 insertions(+), 31 deletions(-) create mode 100644 .changeset/sharp-elephants-invite.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/Component.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/main.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/Component.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/main.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/Component.svelte create mode 100644 packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/main.svelte diff --git a/.changeset/sharp-elephants-invite.md b/.changeset/sharp-elephants-invite.md new file mode 100644 index 0000000000..3a106cd450 --- /dev/null +++ b/.changeset/sharp-elephants-invite.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +fix: make values consistent between effects and their cleanup functions diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index 4d09d9293f..6c21717852 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -42,6 +42,9 @@ export function CallExpression(node, context) { e.bindable_invalid_location(node); } + // We need context in case the bound prop is stale + context.state.analysis.needs_context = true; + break; case '$host': diff --git a/packages/svelte/src/internal/client/context.js b/packages/svelte/src/internal/client/context.js index bd94d5ad8a..bfca9d5e6a 100644 --- a/packages/svelte/src/internal/client/context.js +++ b/packages/svelte/src/internal/client/context.js @@ -11,7 +11,7 @@ import { set_active_reaction, untrack } from './runtime.js'; -import { effect } from './reactivity/effects.js'; +import { effect, teardown } from './reactivity/effects.js'; import { legacy_mode_flag } from '../flags/index.js'; /** @type {ComponentContext | null} */ @@ -112,15 +112,16 @@ export function getAllContexts() { * @returns {void} */ export function push(props, runes = false, fn) { - component_context = { + var ctx = (component_context = { p: component_context, c: null, + d: false, e: null, m: false, s: props, x: null, l: null - }; + }); if (legacy_mode_flag && !runes) { component_context.l = { @@ -131,6 +132,10 @@ export function push(props, runes = false, fn) { }; } + teardown(() => { + /** @type {ComponentContext} */ (ctx).d = true; + }); + if (DEV) { // component function component_context.function = fn; diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index 5a3b30281f..bd85b14df0 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -1,4 +1,4 @@ -/** @import { Source } from './types.js' */ +/** @import { Derived, Source } from './types.js' */ import { DEV } from 'esm-env'; import { PROPS_IS_BINDABLE, @@ -10,24 +10,10 @@ import { import { get_descriptor, is_function } from '../../shared/utils.js'; import { mutable_source, set, source, update } from './sources.js'; import { derived, derived_safe_equal } from './deriveds.js'; -import { - active_effect, - get, - captured_signals, - set_active_effect, - untrack, - active_reaction, - set_active_reaction -} from '../runtime.js'; +import { get, captured_signals, untrack } from '../runtime.js'; import { safe_equals } from './equality.js'; import * as e from '../errors.js'; -import { - BRANCH_EFFECT, - LEGACY_DERIVED_PROP, - LEGACY_PROPS, - ROOT_EFFECT, - STATE_SYMBOL -} from '../constants.js'; +import { LEGACY_DERIVED_PROP, LEGACY_PROPS, STATE_SYMBOL } from '../constants.js'; import { proxy } from '../proxy.js'; import { capture_store_binding } from './store.js'; import { legacy_mode_flag } from '../../flags/index.js'; @@ -249,6 +235,14 @@ export function spread_props(...props) { return new Proxy({ props }, spread_props_handler); } +/** + * @param {Derived} current_value + * @returns {boolean} + */ +function has_destroyed_component_ctx(current_value) { + return current_value.ctx?.d ?? false; +} + /** * This function is responsible for synchronizing a possibly bound prop with the inner component state. * It is used whenever the compiler sees that the component writes to the prop, or when it has a default prop_value. @@ -382,6 +376,11 @@ export function prop(props, key, flags, fallback) { return (inner_current_value.v = parent_value); }); + // Ensure we eagerly capture the initial value if it's bindable + if (bindable) { + get(current_value); + } + if (!immutable) current_value.equals = safe_equals; return function (/** @type {any} */ value, /** @type {boolean} */ mutation) { @@ -408,11 +407,21 @@ export function prop(props, key, flags, fallback) { if (fallback_used && fallback_value !== undefined) { fallback_value = new_value; } + + if (has_destroyed_component_ctx(current_value)) { + return value; + } + untrack(() => get(current_value)); // force a synchronisation immediately } return value; } + + if (has_destroyed_component_ctx(current_value)) { + return current_value.v; + } + return get(current_value); }; } diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index f6a3fd7e33..49584e8626 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -14,7 +14,8 @@ import { derived_sources, set_derived_sources, check_dirtiness, - untracking + untracking, + is_destroying_effect } from '../runtime.js'; import { equals, safe_equals } from './equality.js'; import { @@ -34,6 +35,7 @@ import { get_stack } from '../dev/tracing.js'; import { component_context, is_runes } from '../context.js'; export let inspect_effects = new Set(); +export const old_values = new Map(); /** * @param {Set<any>} v @@ -168,6 +170,13 @@ export function set(source, value) { export function internal_set(source, value) { if (!source.equals(value)) { var old_value = source.v; + + if (is_destroying_effect) { + old_values.set(source, value); + } else { + old_values.set(source, old_value); + } + source.v = value; source.wv = increment_write_version(); diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index bbe4dc3d9b..aa0a41e71f 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -25,7 +25,7 @@ import { BOUNDARY_EFFECT } from './constants.js'; import { flush_tasks } from './dom/task.js'; -import { internal_set } from './reactivity/sources.js'; +import { internal_set, old_values } from './reactivity/sources.js'; import { destroy_derived_effects, update_derived } from './reactivity/deriveds.js'; import * as e from './errors.js'; import { FILENAME } from '../../constants.js'; @@ -673,6 +673,7 @@ function flush_queued_root_effects() { if (DEV) { dev_effect_stack = []; } + old_values.clear(); } } @@ -923,6 +924,10 @@ export function get(signal) { } } + if (is_destroying_effect && old_values.has(signal)) { + return old_values.get(signal); + } + return signal.v; } diff --git a/packages/svelte/src/internal/client/types.d.ts b/packages/svelte/src/internal/client/types.d.ts index 7208ed7783..0c260a0a9f 100644 --- a/packages/svelte/src/internal/client/types.d.ts +++ b/packages/svelte/src/internal/client/types.d.ts @@ -14,6 +14,8 @@ export type ComponentContext = { p: null | ComponentContext; /** context */ c: null | Map<unknown, unknown>; + /** destroyed */ + d: boolean; /** deferred effects */ e: null | Array<{ fn: () => void | (() => void); diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/Component.svelte b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/Component.svelte new file mode 100644 index 0000000000..73347c4d7f --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/Component.svelte @@ -0,0 +1,11 @@ +<script> + import { onDestroy } from 'svelte'; + + export let my_prop; + + onDestroy(() => { + console.log(my_prop.foo); + }); +</script> + +{my_prop.foo} diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/_config.js b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/_config.js new file mode 100644 index 0000000000..81005cf737 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/_config.js @@ -0,0 +1,14 @@ +import { test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + async test({ assert, target, logs }) { + const [btn1] = target.querySelectorAll('button'); + + flushSync(() => { + btn1.click(); + }); + + assert.deepEqual(logs, ['bar']); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/main.svelte b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/main.svelte new file mode 100644 index 0000000000..f38b37fb7f --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-2/main.svelte @@ -0,0 +1,15 @@ +<script> + import Component from './Component.svelte'; + + let value = { foo: 'bar' }; +</script> + +<button + onclick={() => { + value = undefined; + }}>Reset value</button +> + +{#if value !== undefined} + <Component my_prop={value} /> +{/if} diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/Component.svelte b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/Component.svelte new file mode 100644 index 0000000000..5bfb777128 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/Component.svelte @@ -0,0 +1,5 @@ +<script lang="ts"> + export let ref; +</script> + +<input bind:this={ref} /> diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/_config.js b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/_config.js new file mode 100644 index 0000000000..0eb68310cb --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + async test({ target }) { + const [btn1] = target.querySelectorAll('button'); + + btn1.click(); + flushSync(); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/main.svelte b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/main.svelte new file mode 100644 index 0000000000..9c72d2c48a --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access-3/main.svelte @@ -0,0 +1,16 @@ +<script> + import Component from './Component.svelte'; + let state = { title: 'foo' }; +</script> + +{#if state} + {@const attributes = { title: state.title }} + <Component {...attributes} /> +{/if} +<button + onclick={() => { + state = undefined; + }} +> + Del +</button> diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/Component.svelte b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/Component.svelte new file mode 100644 index 0000000000..761f303c2e --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/Component.svelte @@ -0,0 +1,12 @@ +<script> + import { onDestroy } from "svelte"; + export let checked; + export let count; + onDestroy(() => { + console.log(count, checked); + }); +</script> + +<p>{count}</p> + +<button onclick={()=> count-- }></button> diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/_config.js b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/_config.js new file mode 100644 index 0000000000..2ffb7e653f --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/_config.js @@ -0,0 +1,68 @@ +import { test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + async test({ assert, target, logs }) { + const [btn1, btn2, btn3] = target.querySelectorAll('button'); + let ps = [...target.querySelectorAll('p')]; + + for (const p of ps) { + assert.equal(p.innerHTML, '0'); + } + + flushSync(() => { + btn1.click(); + }); + + // prop update normally if we are not unmounting + for (const p of ps) { + assert.equal(p.innerHTML, '1'); + } + + flushSync(() => { + btn3.click(); + }); + + // binding still works and update the value correctly + for (const p of ps) { + assert.equal(p.innerHTML, '0'); + } + + flushSync(() => { + btn1.click(); + }); + + flushSync(() => { + btn1.click(); + }); + + console.warn(logs); + + // the five components guarded by `count < 2` unmount and log + assert.deepEqual(logs, [1, true, 1, true, 1, true, 1, true, 1, true]); + + flushSync(() => { + btn2.click(); + }); + + // the three components guarded by `show` unmount and log + assert.deepEqual(logs, [ + 1, + true, + 1, + true, + 1, + true, + 1, + true, + 1, + true, + 2, + true, + 2, + true, + 2, + true + ]); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/main.svelte b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/main.svelte new file mode 100644 index 0000000000..73a7501e9d --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/main.svelte @@ -0,0 +1,41 @@ +<script> + import Component from "./Component.svelte"; + let show = true; + let count = 0; + $: spread = { checked: show, count }; +</script> + +<button onclick={()=> count++ }></button> +<button onclick={()=> show = !show }></button> + +<!-- count with bind --> +{#if count < 2} + <Component bind:count bind:checked={show} /> +{/if} + +<!-- spread syntax --> +{#if count < 2} + <Component {...spread} /> +{/if} + +<!-- normal prop --> +{#if count < 2} + <Component {count} checked={show} /> +{/if} + +<!-- prop only accessed in destroy --> +{#if show} + <Component {count} checked={show} /> +{/if} + +<!-- dynamic component --> +<svelte:component this={count < 2 ? Component : undefined} {count} checked={show} /> + +<!-- dynamic component spread --> +<svelte:component this={count < 2 ? Component : undefined} {...spread} /> + +<!-- dynamic component with prop only accessed on destroy --> +<svelte:component this={show ? Component : undefined} {count} checked={show} /> + +<!-- dynamic component with prop only accessed on destroy spread --> +<svelte:component this={show ? Component : undefined} {...spread} /> diff --git a/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/_config.js b/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/_config.js index a8c16b7008..eb631bc9f4 100644 --- a/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/nested-effect-conflict/_config.js @@ -10,14 +10,6 @@ export default test({ }); await Promise.resolve(); - assert.deepEqual(logs, [ - 'top level', - 'inner', - 0, - 'destroy inner', - undefined, - 'destroy outer', - undefined - ]); + assert.deepEqual(logs, ['top level', 'inner', 0, 'destroy inner', 0, 'destroy outer', 0]); } }); From a1257c17f5ba93bdbf7c470a5720ff9a69a224dc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 11 Mar 2025 14:51:01 -0400 Subject: [PATCH 0248/1429] Version Packages (#15493) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/sharp-elephants-invite.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/sharp-elephants-invite.md diff --git a/.changeset/sharp-elephants-invite.md b/.changeset/sharp-elephants-invite.md deleted file mode 100644 index 3a106cd450..0000000000 --- a/.changeset/sharp-elephants-invite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -fix: make values consistent between effects and their cleanup functions diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index a05938dacc..65b3edd1fd 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.23.0 + +### Minor Changes + +- fix: make values consistent between effects and their cleanup functions ([#15469](https://github.com/sveltejs/svelte/pull/15469)) + ## 5.22.6 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 53f03e3543..b3ac0a5b51 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.22.6", + "version": "5.23.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 01e1b390a3..5f06fd0753 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.22.6'; +export const VERSION = '5.23.0'; export const PUBLIC_VERSION = '5'; From b27ca425c792d53d58346d76850add7db1cef3ad Mon Sep 17 00:00:00 2001 From: "D.V. Colomban" <17250935+dvcol@users.noreply.github.com> Date: Wed, 12 Mar 2025 23:58:40 +0100 Subject: [PATCH 0249/1429] fix: add files and group properties to HTMLInputAttributes (#15492) Fixes #14579 Although this isn't 100% correct because there's no `group` attribute, there's no better way to make people have components' props just be `HTMLInputAttributes` and allow them to reference `group` --- .changeset/plenty-bats-lay.md | 5 +++++ packages/svelte/elements.d.ts | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 .changeset/plenty-bats-lay.md diff --git a/.changeset/plenty-bats-lay.md b/.changeset/plenty-bats-lay.md new file mode 100644 index 0000000000..cd5ce66e42 --- /dev/null +++ b/.changeset/plenty-bats-lay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: add `files` and `group` to HTMLInputAttributes in elements.d.ts diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts index 08687cafaf..99d87b4c09 100644 --- a/packages/svelte/elements.d.ts +++ b/packages/svelte/elements.d.ts @@ -1076,6 +1076,7 @@ export interface HTMLInputAttributes extends HTMLAttributes<HTMLInputElement> { checked?: boolean | undefined | null; dirname?: string | undefined | null; disabled?: boolean | undefined | null; + files?: FileList | undefined | null; form?: string | undefined | null; formaction?: string | undefined | null; formenctype?: @@ -1087,6 +1088,7 @@ export interface HTMLInputAttributes extends HTMLAttributes<HTMLInputElement> { formmethod?: 'dialog' | 'get' | 'post' | 'DIALOG' | 'GET' | 'POST' | undefined | null; formnovalidate?: boolean | undefined | null; formtarget?: string | undefined | null; + group?: any | undefined | null; height?: number | string | undefined | null; indeterminate?: boolean | undefined | null; list?: string | undefined | null; From 5d3aa2bda4bea7af39607a4a01ce8f0eff6cd56b Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Thu, 13 Mar 2025 19:49:26 +0000 Subject: [PATCH 0250/1429] fix: ensure transient writes to tracked parent effects works as expected (#15506) * ix: ensure transient writes to tracked parent effects works as expected * lint * format test * tweak changeset --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/brown-rockets-shake.md | 5 +++++ packages/svelte/src/internal/client/runtime.js | 8 ++++++++ .../samples/untracked-write-pre/_config.js | 7 +++++++ .../samples/untracked-write-pre/main.svelte | 13 +++++++++++++ 4 files changed, 33 insertions(+) create mode 100644 .changeset/brown-rockets-shake.md create mode 100644 packages/svelte/tests/runtime-runes/samples/untracked-write-pre/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/untracked-write-pre/main.svelte diff --git a/.changeset/brown-rockets-shake.md b/.changeset/brown-rockets-shake.md new file mode 100644 index 0000000000..3772a88f6e --- /dev/null +++ b/.changeset/brown-rockets-shake.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: invalidate parent effects when child effects update parent dependencies diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index aa0a41e71f..0a65c6e45a 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -460,6 +460,14 @@ export function update_reaction(reaction) { // the same version if (previous_reaction !== null) { read_version++; + + if (untracked_writes !== null) { + if (previous_untracked_writes === null) { + previous_untracked_writes = untracked_writes; + } else { + previous_untracked_writes.push(.../** @type {Source[]} */ (untracked_writes)); + } + } } return result; diff --git a/packages/svelte/tests/runtime-runes/samples/untracked-write-pre/_config.js b/packages/svelte/tests/runtime-runes/samples/untracked-write-pre/_config.js new file mode 100644 index 0000000000..0310ec4fbb --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/untracked-write-pre/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + test({ assert, target, logs }) { + assert.deepEqual(logs, ['Outer', 'Inner', 'Outer', 'Inner']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/untracked-write-pre/main.svelte b/packages/svelte/tests/runtime-runes/samples/untracked-write-pre/main.svelte new file mode 100644 index 0000000000..5e95dbfd41 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/untracked-write-pre/main.svelte @@ -0,0 +1,13 @@ +<script> + let value = $state(0); + + $effect.pre(() => { + console.log("Outer"); + value; + + $effect.pre(() => { + console.log("Inner"); + value = 10; + }); + }); +</script> From 489f463d7bf80da22c92b686e507eb7c0dc41967 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Fri, 14 Mar 2025 03:26:45 -0700 Subject: [PATCH 0251/1429] fix: replace `undefined` with `void 0` to avoid edge case (#15511) * replace 'undefined' with 'void 0' * lint * YALF * reuse expression * oops --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/curvy-countries-flow.md | 5 +++++ .../3-transform/client/visitors/VariableDeclaration.js | 3 +-- .../phases/3-transform/server/visitors/CallExpression.js | 4 ++-- .../3-transform/server/visitors/VariableDeclaration.js | 5 ++--- packages/svelte/src/compiler/utils/builders.js | 2 ++ 5 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 .changeset/curvy-countries-flow.md diff --git a/.changeset/curvy-countries-flow.md b/.changeset/curvy-countries-flow.md new file mode 100644 index 0000000000..6ef8545804 --- /dev/null +++ b/.changeset/curvy-countries-flow.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: replace `undefined` with `void 0` to avoid edge case diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js index 31e712cdcc..baffc5dec3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js @@ -116,8 +116,7 @@ export function VariableDeclaration(node, context) { } const args = /** @type {CallExpression} */ (init).arguments; - const value = - args.length === 0 ? b.id('undefined') : /** @type {Expression} */ (context.visit(args[0])); + const value = args.length > 0 ? /** @type {Expression} */ (context.visit(args[0])) : b.void0; if (rune === '$state' || rune === '$state.raw') { /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js index 386c6b6ff3..a425bc5ec4 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js @@ -13,11 +13,11 @@ export function CallExpression(node, context) { const rune = get_rune(node, context.state.scope); if (rune === '$host') { - return b.id('undefined'); + return b.void0; } if (rune === '$effect.tracking') { - return b.literal(false); + return b.false; } if (rune === '$effect.root') { diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js index c4c31d7eb3..a9c9777335 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js @@ -45,7 +45,7 @@ export function VariableDeclaration(node, context) { ) { const right = node.right.arguments.length ? /** @type {Expression} */ (context.visit(node.right.arguments[0])) - : b.id('undefined'); + : b.void0; return b.assignment_pattern(node.left, right); } } @@ -75,8 +75,7 @@ export function VariableDeclaration(node, context) { } const args = /** @type {CallExpression} */ (init).arguments; - const value = - args.length === 0 ? b.id('undefined') : /** @type {Expression} */ (context.visit(args[0])); + const value = args.length > 0 ? /** @type {Expression} */ (context.visit(args[0])) : b.void0; if (rune === '$derived.by') { declarations.push( diff --git a/packages/svelte/src/compiler/utils/builders.js b/packages/svelte/src/compiler/utils/builders.js index ecb595d74d..4ec2930cc2 100644 --- a/packages/svelte/src/compiler/utils/builders.js +++ b/packages/svelte/src/compiler/utils/builders.js @@ -154,6 +154,8 @@ export function unary(operator, argument) { return { type: 'UnaryExpression', argument, operator, prefix: true }; } +export const void0 = unary('void', literal(0)); + /** * @param {ESTree.Expression} test * @param {ESTree.Expression} consequent From dab1a1b467b8c5e85964d74c378357bea00e5fe9 Mon Sep 17 00:00:00 2001 From: Scott Wu <sw@scottwu.ca> Date: Fri, 14 Mar 2025 19:11:08 +0800 Subject: [PATCH 0252/1429] docs: Update 99-faq.md (#15510) --- documentation/docs/07-misc/99-faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/07-misc/99-faq.md b/documentation/docs/07-misc/99-faq.md index b56c27af86..7e25cdab55 100644 --- a/documentation/docs/07-misc/99-faq.md +++ b/documentation/docs/07-misc/99-faq.md @@ -46,7 +46,7 @@ It will show up on hover. - You can use markdown here. - You can also use code blocks here. - Usage: - ```tsx + ```svelte <main name="Arethra"> ``` --> From 18d71fd5288c04d5574f5abe18e7c990053c5876 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 14 Mar 2025 07:12:02 -0400 Subject: [PATCH 0253/1429] chore: reuse expression nodes (#15513) --- .../compiler/phases/3-transform/client/transform-client.js | 2 +- .../phases/3-transform/client/visitors/AnimateDirective.js | 2 +- .../compiler/phases/3-transform/client/visitors/AwaitBlock.js | 2 +- .../phases/3-transform/client/visitors/BinaryExpression.js | 4 ++-- .../compiler/phases/3-transform/client/visitors/IfBlock.js | 4 +--- .../phases/3-transform/client/visitors/RegularElement.js | 2 +- .../phases/3-transform/client/visitors/SlotElement.js | 2 +- .../phases/3-transform/client/visitors/shared/element.js | 4 ++-- .../phases/3-transform/server/visitors/SlotElement.js | 2 +- packages/svelte/src/compiler/utils/builders.js | 4 ++-- 10 files changed, 13 insertions(+), 15 deletions(-) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index cf5ba285cb..ac8263b916 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -596,7 +596,7 @@ export function client_component(analysis, options) { /** @type {ESTree.Property[]} */ ( [ prop_def.attribute ? b.init('attribute', b.literal(prop_def.attribute)) : undefined, - prop_def.reflect ? b.init('reflect', b.literal(true)) : undefined, + prop_def.reflect ? b.init('reflect', b.true) : undefined, prop_def.type ? b.init('type', b.literal(prop_def.type)) : undefined ].filter(Boolean) ) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AnimateDirective.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AnimateDirective.js index 510f32cde5..2e051ec674 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AnimateDirective.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AnimateDirective.js @@ -11,7 +11,7 @@ import { parse_directive_name } from './shared/utils.js'; export function AnimateDirective(node, context) { const expression = node.expression === null - ? b.literal(null) + ? b.null : b.thunk(/** @type {Expression} */ (context.visit(node.expression))); // in after_update to ensure it always happens after bind:this diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js index e0aef2d316..7588b24280 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js @@ -60,7 +60,7 @@ export function AwaitBlock(node, context) { expression, node.pending ? b.arrow([b.id('$$anchor')], /** @type {BlockStatement} */ (context.visit(node.pending))) - : b.literal(null), + : b.null, then_block, catch_block ) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/BinaryExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/BinaryExpression.js index c8c54a5a59..c563920855 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/BinaryExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/BinaryExpression.js @@ -16,7 +16,7 @@ export function BinaryExpression(node, context) { '$.strict_equals', /** @type {Expression} */ (context.visit(node.left)), /** @type {Expression} */ (context.visit(node.right)), - operator === '!==' && b.literal(false) + operator === '!==' && b.false ); } @@ -25,7 +25,7 @@ export function BinaryExpression(node, context) { '$.equals', /** @type {Expression} */ (context.visit(node.left)), /** @type {Expression} */ (context.visit(node.right)), - operator === '!=' && b.literal(false) + operator === '!=' && b.false ); } } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js index 0876fa30b6..fdd21b2b7e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js @@ -40,9 +40,7 @@ export function IfBlock(node, context) { b.if( /** @type {Expression} */ (context.visit(node.test)), b.stmt(b.call(b.id('$$render'), b.id(consequent_id))), - alternate_id - ? b.stmt(b.call(b.id('$$render'), b.id(alternate_id), b.literal(false))) - : undefined + alternate_id ? b.stmt(b.call(b.id('$$render'), b.id(alternate_id), b.false)) : undefined ) ]) ) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 9b3ecc922d..45a594af1f 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -689,7 +689,7 @@ function build_element_special_value_attribute(element, node_id, attribute, cont '=', b.member(node_id, 'value'), b.conditional( - b.binary('==', b.literal(null), b.assignment('=', b.member(node_id, '__value'), value)), + b.binary('==', b.null, b.assignment('=', b.member(node_id, '__value'), value)), b.literal(''), // render null/undefined values as empty string to support placeholder options value ) diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js index fdd705e32e..c6f4ba1ed3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js @@ -59,7 +59,7 @@ export function SlotElement(node, context) { const fallback = node.fragment.nodes.length === 0 - ? b.literal(null) + ? b.null : b.arrow([b.id('$$anchor')], /** @type {BlockStatement} */ (context.visit(node.fragment))); const slot = b.call( diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index 084c1e7c67..97cec7a729 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -95,7 +95,7 @@ export function build_set_attributes( const call = b.call( '$.set_attributes', element_id, - is_dynamic ? attributes_id : b.literal(null), + is_dynamic ? attributes_id : b.null, b.object(values), element.metadata.scoped && context.state.analysis.css.hash !== '' && @@ -120,7 +120,7 @@ export function build_set_attributes( */ export function build_attribute_value(value, context, memoize = (value) => value) { if (value === true) { - return { value: b.literal(true), has_state: false }; + return { value: b.true, has_state: false }; } if (!Array.isArray(value) || value.length === 1) { diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SlotElement.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SlotElement.js index 7ece04ae3d..e7925071cd 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SlotElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SlotElement.js @@ -38,7 +38,7 @@ export function SlotElement(node, context) { const fallback = node.fragment.nodes.length === 0 - ? b.literal(null) + ? b.null : b.thunk(/** @type {BlockStatement} */ (context.visit(node.fragment))); const slot = b.call( diff --git a/packages/svelte/src/compiler/utils/builders.js b/packages/svelte/src/compiler/utils/builders.js index 4ec2930cc2..736738d19f 100644 --- a/packages/svelte/src/compiler/utils/builders.js +++ b/packages/svelte/src/compiler/utils/builders.js @@ -485,7 +485,7 @@ export function do_while(test, body) { const true_instance = literal(true); const false_instance = literal(false); -const null_instane = literal(null); +const null_instance = literal(null); /** @type {ESTree.DebuggerStatement} */ const debugger_builder = { @@ -647,7 +647,7 @@ export { return_builder as return, if_builder as if, this_instance as this, - null_instane as null, + null_instance as null, debugger_builder as debugger }; From e74fbcbbacc529bbb7ff8cc7d6b8b5d75d647cfa Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Fri, 14 Mar 2025 13:19:24 -0700 Subject: [PATCH 0254/1429] chore: don't distribute unused types definitions (#15473) hopefully helps with #15182, also makes the package smaller --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- .changeset/hungry-dancers-tap.md | 5 +++++ packages/svelte/package.json | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changeset/hungry-dancers-tap.md diff --git a/.changeset/hungry-dancers-tap.md b/.changeset/hungry-dancers-tap.md new file mode 100644 index 0000000000..51b2f86019 --- /dev/null +++ b/.changeset/hungry-dancers-tap.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: don't distribute unused types definitions diff --git a/packages/svelte/package.json b/packages/svelte/package.json index b3ac0a5b51..c74c9d34ca 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -9,11 +9,12 @@ "node": ">=18" }, "files": [ + "*.d.ts", "src", "!src/**/*.test.*", + "!src/**/*.d.ts", "types", "compiler", - "*.d.ts", "README.md" ], "module": "src/index-client.js", From f227cfcea86b46e6a8ee389484d855c306ed66eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?E=CC=81ric=20NICOLAS?= <ccjmne@gmail.com> Date: Fri, 14 Mar 2025 22:30:49 +0100 Subject: [PATCH 0255/1429] fix: Allow global-like pseudo-selectors refinement (#15313) For instance, specifying a tree-structural pseudo-class to `::view-transition-new` should still constitute a valid global-like selector. Fixes #15312 --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- .changeset/gold-hairs-jog.md | 5 +++++ .../src/compiler/phases/2-analyze/css/css-analyze.js | 8 +++++++- .../svelte/tests/css/samples/view-transition/expected.css | 6 ++++++ .../svelte/tests/css/samples/view-transition/input.svelte | 6 ++++++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .changeset/gold-hairs-jog.md diff --git a/.changeset/gold-hairs-jog.md b/.changeset/gold-hairs-jog.md new file mode 100644 index 0000000000..eaafced314 --- /dev/null +++ b/.changeset/gold-hairs-jog.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow global-like pseudo-selectors refinement diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js index ed22838582..362ac9dcad 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js @@ -133,7 +133,13 @@ const css_visitors = { node.metadata.is_global = node.selectors.length >= 1 && is_global(node); - if (node.selectors.length === 1) { + if ( + node.selectors.length >= 1 && + node.selectors.every( + (selector) => + selector.type === 'PseudoClassSelector' || selector.type === 'PseudoElementSelector' + ) + ) { const first = node.selectors[0]; node.metadata.is_global_like ||= (first.type === 'PseudoClassSelector' && first.name === 'host') || diff --git a/packages/svelte/tests/css/samples/view-transition/expected.css b/packages/svelte/tests/css/samples/view-transition/expected.css index afc84d52eb..e216a4d3ad 100644 --- a/packages/svelte/tests/css/samples/view-transition/expected.css +++ b/packages/svelte/tests/css/samples/view-transition/expected.css @@ -8,9 +8,15 @@ ::view-transition-old { animation-duration: 0.5s; } + ::view-transition-old:only-child { + animation-duration: 0.5s; + } ::view-transition-new { animation-duration: 0.5s; } + ::view-transition-new:only-child { + animation-duration: 0.5s; + } ::view-transition-image-pair { animation-duration: 0.5s; } diff --git a/packages/svelte/tests/css/samples/view-transition/input.svelte b/packages/svelte/tests/css/samples/view-transition/input.svelte index ebb2b3fd88..345213ccd3 100644 --- a/packages/svelte/tests/css/samples/view-transition/input.svelte +++ b/packages/svelte/tests/css/samples/view-transition/input.svelte @@ -8,9 +8,15 @@ ::view-transition-old { animation-duration: 0.5s; } + ::view-transition-old:only-child { + animation-duration: 0.5s; + } ::view-transition-new { animation-duration: 0.5s; } + ::view-transition-new:only-child { + animation-duration: 0.5s; + } ::view-transition-image-pair { animation-duration: 0.5s; } From 8e9a21e374c3bafb79e046e64ef4d625f466749f Mon Sep 17 00:00:00 2001 From: 7nik <kfiiranet@gmail.com> Date: Fri, 14 Mar 2025 23:44:28 +0200 Subject: [PATCH 0256/1429] fix: correctly match `:has()`'s selector during css pruning (#15277) Fixes #14072 `:has()` was matching only against descendants or siblings, but not sibling's descendants. This makes the logic be able to go forward or backwards, simplifying a lot of cases along the way. --- .changeset/cuddly-chefs-refuse.md | 5 + .../phases/2-analyze/css/css-prune.js | 427 +++++++++--------- .../svelte/tests/css/samples/has/_config.js | 148 +++--- .../svelte/tests/css/samples/has/expected.css | 13 + .../svelte/tests/css/samples/has/input.svelte | 21 + .../css/samples/render-tag-loop/_config.js | 17 +- .../css/samples/render-tag-loop/expected.css | 9 +- .../css/samples/render-tag-loop/input.svelte | 10 +- 8 files changed, 353 insertions(+), 297 deletions(-) create mode 100644 .changeset/cuddly-chefs-refuse.md diff --git a/.changeset/cuddly-chefs-refuse.md b/.changeset/cuddly-chefs-refuse.md new file mode 100644 index 0000000000..6672ac4ab3 --- /dev/null +++ b/.changeset/cuddly-chefs-refuse.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly match `:has()` selector during css pruning diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index fc8108e46e..070ec7cd34 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -5,9 +5,12 @@ import { regex_ends_with_whitespace, regex_starts_with_whitespace } from '../../ import { get_attribute_chunks, is_text_attribute } from '../../../utils/ast.js'; /** @typedef {NODE_PROBABLY_EXISTS | NODE_DEFINITELY_EXISTS} NodeExistsValue */ +/** @typedef {FORWARD | BACKWARD} Direction */ const NODE_PROBABLY_EXISTS = 0; const NODE_DEFINITELY_EXISTS = 1; +const FORWARD = 0; +const BACKWARD = 1; const whitelist_attribute_selector = new Map([ ['details', ['open']], @@ -43,6 +46,27 @@ const nesting_selector = { } }; +/** @type {Compiler.AST.CSS.RelativeSelector} */ +const any_selector = { + type: 'RelativeSelector', + start: -1, + end: -1, + combinator: null, + selectors: [ + { + type: 'TypeSelector', + name: '*', + start: -1, + end: -1 + } + ], + metadata: { + is_global: false, + is_global_like: false, + scoped: false + } +}; + /** * Snippets encountered already (avoids infinite loops) * @type {Set<Compiler.AST.SnippetBlock>} @@ -72,7 +96,8 @@ export function prune(stylesheet, element) { apply_selector( selectors, /** @type {Compiler.AST.CSS.Rule} */ (node.metadata.rule), - element + element, + BACKWARD ) ) { node.metadata.used = true; @@ -159,16 +184,17 @@ function truncate(node) { * @param {Compiler.AST.CSS.RelativeSelector[]} relative_selectors * @param {Compiler.AST.CSS.Rule} rule * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element + * @param {Direction} direction * @returns {boolean} */ -function apply_selector(relative_selectors, rule, element) { - const parent_selectors = relative_selectors.slice(); - const relative_selector = parent_selectors.pop(); +function apply_selector(relative_selectors, rule, element, direction) { + const rest_selectors = relative_selectors.slice(); + const relative_selector = direction === FORWARD ? rest_selectors.shift() : rest_selectors.pop(); const matched = !!relative_selector && - relative_selector_might_apply_to_node(relative_selector, rule, element) && - apply_combinator(relative_selector, parent_selectors, rule, element); + relative_selector_might_apply_to_node(relative_selector, rule, element, direction) && + apply_combinator(relative_selector, rest_selectors, rule, element, direction); if (matched) { if (!is_outer_global(relative_selector)) { @@ -183,76 +209,63 @@ function apply_selector(relative_selectors, rule, element) { /** * @param {Compiler.AST.CSS.RelativeSelector} relative_selector - * @param {Compiler.AST.CSS.RelativeSelector[]} parent_selectors + * @param {Compiler.AST.CSS.RelativeSelector[]} rest_selectors * @param {Compiler.AST.CSS.Rule} rule * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.RenderTag | Compiler.AST.Component | Compiler.AST.SvelteComponent | Compiler.AST.SvelteSelf} node + * @param {Direction} direction * @returns {boolean} */ -function apply_combinator(relative_selector, parent_selectors, rule, node) { - if (!relative_selector.combinator) return true; +function apply_combinator(relative_selector, rest_selectors, rule, node, direction) { + const combinator = + direction == FORWARD ? rest_selectors[0]?.combinator : relative_selector.combinator; + if (!combinator) return true; - const name = relative_selector.combinator.name; - - switch (name) { + switch (combinator.name) { case ' ': case '>': { + const is_adjacent = combinator.name === '>'; + const parents = + direction === FORWARD + ? get_descendant_elements(node, is_adjacent) + : get_ancestor_elements(node, is_adjacent); let parent_matched = false; - const path = node.metadata.path; - let i = path.length; - - while (i--) { - const parent = path[i]; - - if (parent.type === 'SnippetBlock') { - if (seen.has(parent)) { - parent_matched = true; - } else { - seen.add(parent); - - for (const site of parent.metadata.sites) { - if (apply_combinator(relative_selector, parent_selectors, rule, site)) { - parent_matched = true; - } - } - } - - break; - } - - if (parent.type === 'RegularElement' || parent.type === 'SvelteElement') { - if (apply_selector(parent_selectors, rule, parent)) { - parent_matched = true; - } - - if (name === '>') return parent_matched; + for (const parent of parents) { + if (apply_selector(rest_selectors, rule, parent, direction)) { + parent_matched = true; } } - return parent_matched || parent_selectors.every((selector) => is_global(selector, rule)); + return ( + parent_matched || + (direction === BACKWARD && + (!is_adjacent || parents.length === 0) && + rest_selectors.every((selector) => is_global(selector, rule))) + ); } case '+': case '~': { - const siblings = get_possible_element_siblings(node, name === '+'); + const siblings = get_possible_element_siblings(node, direction, combinator.name === '+'); let sibling_matched = false; for (const possible_sibling of siblings.keys()) { if (possible_sibling.type === 'RenderTag' || possible_sibling.type === 'SlotElement') { // `{@render foo()}<p>foo</p>` with `:global(.x) + p` is a match - if (parent_selectors.length === 1 && parent_selectors[0].metadata.is_global) { + if (rest_selectors.length === 1 && rest_selectors[0].metadata.is_global) { sibling_matched = true; } - } else if (apply_selector(parent_selectors, rule, possible_sibling)) { + } else if (apply_selector(rest_selectors, rule, possible_sibling, direction)) { sibling_matched = true; } } return ( sibling_matched || - (get_element_parent(node) === null && - parent_selectors.every((selector) => is_global(selector, rule))) + (direction === BACKWARD && + get_element_parent(node) === null && + rest_selectors.every((selector) => is_global(selector, rule))) ); } @@ -313,9 +326,10 @@ const regex_backslash_and_following_character = /\\(.)/g; * @param {Compiler.AST.CSS.RelativeSelector} relative_selector * @param {Compiler.AST.CSS.Rule} rule * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element + * @param {Direction} direction * @returns {boolean} */ -function relative_selector_might_apply_to_node(relative_selector, rule, element) { +function relative_selector_might_apply_to_node(relative_selector, rule, element, direction) { // Sort :has(...) selectors in one bucket and everything else into another const has_selectors = []; const other_selectors = []; @@ -331,13 +345,6 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element) // If we're called recursively from a :has(...) selector, we're on the way of checking if the other selectors match. // In that case ignore this check (because we just came from this) to avoid an infinite loop. if (has_selectors.length > 0) { - /** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */ - const child_elements = []; - /** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */ - const descendant_elements = []; - /** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */ - let sibling_elements; // do them lazy because it's rarely used and expensive to calculate - // If this is a :has inside a global selector, we gotta include the element itself, too, // because the global selector might be for an element that's outside the component, // e.g. :root:has(.scoped), :global(.foo):has(.scoped), or :root { &:has(.scoped) {} } @@ -353,46 +360,6 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element) ) ) ); - if (include_self) { - child_elements.push(element); - descendant_elements.push(element); - } - - const seen = new Set(); - - /** - * @param {Compiler.AST.SvelteNode} node - * @param {{ is_child: boolean }} state - */ - function walk_children(node, state) { - walk(node, state, { - _(node, context) { - if (node.type === 'RegularElement' || node.type === 'SvelteElement') { - descendant_elements.push(node); - - if (context.state.is_child) { - child_elements.push(node); - context.state.is_child = false; - context.next(); - context.state.is_child = true; - } else { - context.next(); - } - } else if (node.type === 'RenderTag') { - for (const snippet of node.metadata.snippets) { - if (seen.has(snippet)) continue; - - seen.add(snippet); - walk_children(snippet.body, context.state); - } - } else { - context.next(); - } - } - }); - } - - walk_children(element.fragment, { is_child: true }); // :has(...) is special in that it means "look downwards in the CSS tree". Since our matching algorithm goes // upwards and back-to-front, we need to first check the selectors inside :has(...), then check the rest of the @@ -403,37 +370,34 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element) let matched = false; for (const complex_selector of complex_selectors) { - const selectors = truncate(complex_selector); - const left_most_combinator = selectors[0]?.combinator ?? descendant_combinator; - // In .x:has(> y), we want to search for y, ignoring the left-most combinator - // (else it would try to walk further up and fail because there are no selectors left) - if (selectors.length > 0) { - selectors[0] = { - ...selectors[0], - combinator: null - }; + const [first, ...rest] = truncate(complex_selector); + // if it was just a :global(...) + if (!first) { + complex_selector.metadata.used = true; + matched = true; + continue; } - const descendants = - left_most_combinator.name === '+' || left_most_combinator.name === '~' - ? (sibling_elements ??= get_following_sibling_elements(element, include_self)) - : left_most_combinator.name === '>' - ? child_elements - : descendant_elements; - - let selector_matched = false; - - // Iterate over all descendant elements and check if the selector inside :has matches - for (const element of descendants) { - if ( - selectors.length === 0 /* is :global(...) */ || - (element.metadata.scoped && selector_matched) || - apply_selector(selectors, rule, element) - ) { + if (include_self) { + const selector_including_self = [ + first.combinator ? { ...first, combinator: null } : first, + ...rest + ]; + if (apply_selector(selector_including_self, rule, element, FORWARD)) { complex_selector.metadata.used = true; - selector_matched = matched = true; + matched = true; } } + + const selector_excluding_self = [ + any_selector, + first.combinator ? first : { ...first, combinator: descendant_combinator }, + ...rest + ]; + if (apply_selector(selector_excluding_self, rule, element, FORWARD)) { + complex_selector.metadata.used = true; + matched = true; + } } if (!matched) { @@ -458,7 +422,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element) ) { const args = selector.args; const complex_selector = args.children[0]; - return apply_selector(complex_selector.children, rule, element); + return apply_selector(complex_selector.children, rule, element, BACKWARD); } // We came across a :global, everything beyond it is global and therefore a potential match @@ -507,7 +471,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element) if (is_global) { complex_selector.metadata.used = true; matched = true; - } else if (apply_selector(relative, rule, element)) { + } else if (apply_selector(relative, rule, element, BACKWARD)) { complex_selector.metadata.used = true; matched = true; } else if (complex_selector.children.length > 1 && (name == 'is' || name == 'where')) { @@ -591,7 +555,7 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element) for (const complex_selector of parent.prelude.children) { if ( - apply_selector(get_relative_selectors(complex_selector), parent, element) || + apply_selector(get_relative_selectors(complex_selector), parent, element, direction) || complex_selector.children.every((s) => is_global(s, parent)) ) { complex_selector.metadata.used = true; @@ -612,80 +576,6 @@ function relative_selector_might_apply_to_node(relative_selector, rule, element) return true; } -/** - * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement} element - * @param {boolean} include_self - */ -function get_following_sibling_elements(element, include_self) { - const path = element.metadata.path; - let i = path.length; - - /** @type {Compiler.AST.SvelteNode} */ - let start = element; - let nodes = /** @type {Compiler.AST.SvelteNode[]} */ ( - /** @type {Compiler.AST.Fragment} */ (path[0]).nodes - ); - - // find the set of nodes to walk... - while (i--) { - const node = path[i]; - - if (node.type === 'RegularElement' || node.type === 'SvelteElement') { - nodes = node.fragment.nodes; - break; - } - - if (node.type !== 'Fragment') { - start = node; - } - } - - /** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */ - const siblings = []; - - // ...then walk them, starting from the node containing the element in question - // skipping nodes that appears before the element - - const seen = new Set(); - let skip = true; - - /** @param {Compiler.AST.SvelteNode} node */ - function get_siblings(node) { - walk(node, null, { - RegularElement(node) { - if (node === element) { - skip = false; - if (include_self) siblings.push(node); - } else if (!skip) { - siblings.push(node); - } - }, - SvelteElement(node) { - if (node === element) { - skip = false; - if (include_self) siblings.push(node); - } else if (!skip) { - siblings.push(node); - } - }, - RenderTag(node) { - for (const snippet of node.metadata.snippets) { - if (seen.has(snippet)) continue; - - seen.add(snippet); - get_siblings(snippet.body); - } - } - }); - } - - for (const node of nodes.slice(nodes.indexOf(start))) { - get_siblings(node); - } - - return siblings; -} - /** * @param {any} operator * @param {any} expected_value @@ -822,6 +712,84 @@ function unquote(str) { return str; } +/** + * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.RenderTag | Compiler.AST.Component | Compiler.AST.SvelteComponent | Compiler.AST.SvelteSelf} node + * @param {boolean} adjacent_only + * @param {Set<Compiler.AST.SnippetBlock>} seen + */ +function get_ancestor_elements(node, adjacent_only, seen = new Set()) { + /** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */ + const ancestors = []; + + const path = node.metadata.path; + let i = path.length; + + while (i--) { + const parent = path[i]; + + if (parent.type === 'SnippetBlock') { + if (!seen.has(parent)) { + seen.add(parent); + + for (const site of parent.metadata.sites) { + ancestors.push(...get_ancestor_elements(site, adjacent_only, seen)); + } + } + + break; + } + + if (parent.type === 'RegularElement' || parent.type === 'SvelteElement') { + ancestors.push(parent); + if (adjacent_only) { + break; + } + } + } + + return ancestors; +} + +/** + * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.RenderTag | Compiler.AST.Component | Compiler.AST.SvelteComponent | Compiler.AST.SvelteSelf} node + * @param {boolean} adjacent_only + * @param {Set<Compiler.AST.SnippetBlock>} seen + */ +function get_descendant_elements(node, adjacent_only, seen = new Set()) { + /** @type {Array<Compiler.AST.RegularElement | Compiler.AST.SvelteElement>} */ + const descendants = []; + + /** + * @param {Compiler.AST.SvelteNode} node + */ + function walk_children(node) { + walk(node, null, { + _(node, context) { + if (node.type === 'RegularElement' || node.type === 'SvelteElement') { + descendants.push(node); + + if (!adjacent_only) { + context.next(); + } + } else if (node.type === 'RenderTag') { + for (const snippet of node.metadata.snippets) { + if (seen.has(snippet)) continue; + + seen.add(snippet); + walk_children(snippet.body); + } + } else { + context.next(); + } + } + }); + } + + walk_children(node.type === 'RenderTag' ? node : node.fragment); + + return descendants; +} + /** * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.RenderTag | Compiler.AST.Component | Compiler.AST.SvelteComponent | Compiler.AST.SvelteSelf} node * @returns {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | null} @@ -843,11 +811,12 @@ function get_element_parent(node) { /** * @param {Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.RenderTag | Compiler.AST.Component | Compiler.AST.SvelteComponent | Compiler.AST.SvelteSelf} node + * @param {Direction} direction * @param {boolean} adjacent_only * @param {Set<Compiler.AST.SnippetBlock>} seen * @returns {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag, NodeExistsValue>} */ -function get_possible_element_siblings(node, adjacent_only, seen = new Set()) { +function get_possible_element_siblings(node, direction, adjacent_only, seen = new Set()) { /** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag, NodeExistsValue>} */ const result = new Map(); const path = node.metadata.path; @@ -859,9 +828,9 @@ function get_possible_element_siblings(node, adjacent_only, seen = new Set()) { while (i--) { const fragment = /** @type {Compiler.AST.Fragment} */ (path[i--]); - let j = fragment.nodes.indexOf(current); + let j = fragment.nodes.indexOf(current) + (direction === FORWARD ? 1 : -1); - while (j--) { + while (j >= 0 && j < fragment.nodes.length) { const node = fragment.nodes[j]; if (node.type === 'RegularElement') { @@ -876,21 +845,28 @@ function get_possible_element_siblings(node, adjacent_only, seen = new Set()) { return result; } } + // Special case: slots, render tags and svelte:element tags could resolve to no siblings, + // so we want to continue until we find a definite sibling even with the adjacent-only combinator } else if (is_block(node)) { if (node.type === 'SlotElement') { result.set(node, NODE_PROBABLY_EXISTS); } - const possible_last_child = get_possible_last_child(node, adjacent_only); + const possible_last_child = get_possible_nested_siblings(node, direction, adjacent_only); add_to_map(possible_last_child, result); if (adjacent_only && has_definite_elements(possible_last_child)) { return result; } - } else if (node.type === 'RenderTag' || node.type === 'SvelteElement') { + } else if (node.type === 'SvelteElement') { result.set(node, NODE_PROBABLY_EXISTS); - // Special case: slots, render tags and svelte:element tags could resolve to no siblings, - // so we want to continue until we find a definite sibling even with the adjacent-only combinator + } else if (node.type === 'RenderTag') { + result.set(node, NODE_PROBABLY_EXISTS); + for (const snippet of node.metadata.snippets) { + add_to_map(get_possible_nested_siblings(snippet, direction, adjacent_only), result); + } } + + j = direction === FORWARD ? j + 1 : j - 1; } current = path[i]; @@ -910,7 +886,7 @@ function get_possible_element_siblings(node, adjacent_only, seen = new Set()) { seen.add(current); for (const site of current.metadata.sites) { - const siblings = get_possible_element_siblings(site, adjacent_only, seen); + const siblings = get_possible_element_siblings(site, direction, adjacent_only, seen); add_to_map(siblings, result); if (adjacent_only && current.metadata.sites.size === 1 && has_definite_elements(siblings)) { @@ -923,7 +899,7 @@ function get_possible_element_siblings(node, adjacent_only, seen = new Set()) { if (current.type === 'EachBlock' && fragment === current.body) { // `{#each ...}<a /><b />{/each}` — `<b>` can be previous sibling of `<a />` - add_to_map(get_possible_last_child(current, adjacent_only), result); + add_to_map(get_possible_nested_siblings(current, direction, adjacent_only), result); } } @@ -931,11 +907,13 @@ function get_possible_element_siblings(node, adjacent_only, seen = new Set()) { } /** - * @param {Compiler.AST.EachBlock | Compiler.AST.IfBlock | Compiler.AST.AwaitBlock | Compiler.AST.KeyBlock | Compiler.AST.SlotElement} node + * @param {Compiler.AST.EachBlock | Compiler.AST.IfBlock | Compiler.AST.AwaitBlock | Compiler.AST.KeyBlock | Compiler.AST.SlotElement | Compiler.AST.SnippetBlock} node + * @param {Direction} direction * @param {boolean} adjacent_only + * @param {Set<Compiler.AST.SnippetBlock>} seen * @returns {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement, NodeExistsValue>} */ -function get_possible_last_child(node, adjacent_only) { +function get_possible_nested_siblings(node, direction, adjacent_only, seen = new Set()) { /** @type {Array<Compiler.AST.Fragment | undefined | null>} */ let fragments = []; @@ -956,12 +934,20 @@ function get_possible_last_child(node, adjacent_only) { case 'SlotElement': fragments.push(node.fragment); break; + + case 'SnippetBlock': + if (seen.has(node)) { + return new Map(); + } + seen.add(node); + fragments.push(node.body); + break; } /** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement, NodeExistsValue>} NodeMap */ const result = new Map(); - let exhaustive = node.type !== 'SlotElement'; + let exhaustive = node.type !== 'SlotElement' && node.type !== 'SnippetBlock'; for (const fragment of fragments) { if (fragment == null) { @@ -969,7 +955,7 @@ function get_possible_last_child(node, adjacent_only) { continue; } - const map = loop_child(fragment.nodes, adjacent_only); + const map = loop_child(fragment.nodes, direction, adjacent_only, seen); exhaustive &&= has_definite_elements(map); add_to_map(map, result); @@ -1012,27 +998,28 @@ function add_to_map(from, to) { } /** - * @param {NodeExistsValue | undefined} exist1 + * @param {NodeExistsValue} exist1 * @param {NodeExistsValue | undefined} exist2 * @returns {NodeExistsValue} */ function higher_existence(exist1, exist2) { - // @ts-expect-error TODO figure out if this is a bug - if (exist1 === undefined || exist2 === undefined) return exist1 || exist2; + if (exist2 === undefined) return exist1; return exist1 > exist2 ? exist1 : exist2; } /** * @param {Compiler.AST.SvelteNode[]} children + * @param {Direction} direction * @param {boolean} adjacent_only + * @param {Set<Compiler.AST.SnippetBlock>} seen */ -function loop_child(children, adjacent_only) { +function loop_child(children, direction, adjacent_only, seen) { /** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement, NodeExistsValue>} */ const result = new Map(); - let i = children.length; + let i = direction === FORWARD ? 0 : children.length - 1; - while (i--) { + while (i >= 0 && i < children.length) { const child = children[i]; if (child.type === 'RegularElement') { @@ -1042,13 +1029,19 @@ function loop_child(children, adjacent_only) { } } else if (child.type === 'SvelteElement') { result.set(child, NODE_PROBABLY_EXISTS); + } else if (child.type === 'RenderTag') { + for (const snippet of child.metadata.snippets) { + add_to_map(get_possible_nested_siblings(snippet, direction, adjacent_only, seen), result); + } } else if (is_block(child)) { - const child_result = get_possible_last_child(child, adjacent_only); + const child_result = get_possible_nested_siblings(child, direction, adjacent_only, seen); add_to_map(child_result, result); if (adjacent_only && has_definite_elements(child_result)) { break; } } + + i = direction === FORWARD ? i + 1 : i - 1; } return result; diff --git a/packages/svelte/tests/css/samples/has/_config.js b/packages/svelte/tests/css/samples/has/_config.js index 8d89d98cbd..5700a09b96 100644 --- a/packages/svelte/tests/css/samples/has/_config.js +++ b/packages/svelte/tests/css/samples/has/_config.js @@ -6,210 +6,238 @@ export default test({ code: 'css_unused_selector', message: 'Unused CSS selector ".unused:has(y)"', start: { - line: 33, + line: 41, column: 1, - character: 330 + character: 378 }, end: { - line: 33, + line: 41, column: 15, - character: 344 + character: 392 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ".unused:has(:global(y))"', start: { - line: 36, + line: 44, column: 1, - character: 365 + character: 413 }, end: { - line: 36, + line: 44, column: 24, - character: 388 + character: 436 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "x:has(.unused)"', start: { - line: 39, + line: 47, column: 1, - character: 409 + character: 457 }, end: { - line: 39, + line: 47, column: 15, - character: 423 + character: 471 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ":global(.foo):has(.unused)"', start: { - line: 42, + line: 50, column: 1, - character: 444 + character: 492 }, end: { - line: 42, + line: 50, column: 27, - character: 470 + character: 518 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "x:has(y):has(.unused)"', start: { - line: 52, + line: 60, column: 1, - character: 578 + character: 626 }, end: { - line: 52, + line: 60, column: 22, - character: 599 + character: 647 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ".unused"', start: { - line: 71, + line: 79, column: 2, - character: 804 + character: 852 }, end: { - line: 71, + line: 79, column: 9, - character: 811 + character: 859 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ".unused x:has(y)"', start: { - line: 87, + line: 95, column: 1, - character: 958 + character: 1006 }, end: { - line: 87, + line: 95, column: 17, - character: 974 + character: 1022 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ".unused:has(.unused)"', start: { - line: 90, + line: 98, column: 1, - character: 995 + character: 1043 }, end: { - line: 90, + line: 98, column: 21, - character: 1015 + character: 1063 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "x:has(> z)"', start: { - line: 100, + line: 108, column: 1, - character: 1115 + character: 1163 }, end: { - line: 100, + line: 108, column: 11, - character: 1125 + character: 1173 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "x:has(> d)"', start: { - line: 103, + line: 111, column: 1, - character: 1146 + character: 1194 }, end: { - line: 103, + line: 111, column: 11, - character: 1156 + character: 1204 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "x:has(~ y)"', start: { - line: 123, + line: 131, column: 1, - character: 1348 + character: 1396 }, end: { - line: 123, + line: 131, column: 11, - character: 1358 + character: 1406 + } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector "d:has(+ f)"', + start: { + line: 141, + column: 1, + character: 1494 + }, + end: { + line: 141, + column: 11, + character: 1504 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "f:has(~ d)"', start: { - line: 133, + line: 144, column: 1, - character: 1446 + character: 1525 }, end: { - line: 133, + line: 144, column: 11, - character: 1456 + character: 1535 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ":has(.unused)"', start: { - line: 141, + line: 152, column: 2, - character: 1529 + character: 1608 }, end: { - line: 141, + line: 152, column: 15, - character: 1542 + character: 1621 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "&:has(.unused)"', start: { - line: 147, + line: 158, column: 2, - character: 1600 + character: 1679 }, end: { - line: 147, + line: 158, column: 16, - character: 1614 + character: 1693 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ":global(.foo):has(.unused)"', start: { - line: 155, + line: 166, column: 1, - character: 1684 + character: 1763 }, end: { - line: 155, + line: 166, column: 27, - character: 1710 + character: 1789 + } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector "h:has(> h > i)"', + start: { + line: 173, + column: 1, + character: 1848 + }, + end: { + line: 173, + column: 15, + character: 1862 } } ] diff --git a/packages/svelte/tests/css/samples/has/expected.css b/packages/svelte/tests/css/samples/has/expected.css index b257370d61..2ce4d2bec5 100644 --- a/packages/svelte/tests/css/samples/has/expected.css +++ b/packages/svelte/tests/css/samples/has/expected.css @@ -118,6 +118,9 @@ d.svelte-xyz:has(~ f:where(.svelte-xyz)) { color: green; } + /* (unused) d:has(+ f) { + color: red; + }*/ /* (unused) f:has(~ d) { color: red; }*/ @@ -143,3 +146,13 @@ /* (unused) :global(.foo):has(.unused) { color: red; }*/ + + g.svelte-xyz:has(> h:where(.svelte-xyz) > i:where(.svelte-xyz)) { + color: green; + } + /* (unused) h:has(> h > i) { + color: red; + }*/ + g.svelte-xyz:has(+ j:where(.svelte-xyz) > k:where(.svelte-xyz)) { + color: green; + } \ No newline at end of file diff --git a/packages/svelte/tests/css/samples/has/input.svelte b/packages/svelte/tests/css/samples/has/input.svelte index 9b254996bf..033471bc16 100644 --- a/packages/svelte/tests/css/samples/has/input.svelte +++ b/packages/svelte/tests/css/samples/has/input.svelte @@ -9,6 +9,14 @@ </y> </x> <c></c> +<g> + <h> + <i></i> + </h> +</g> +<j> + <k></k> +</j> <style> x:has(y) { @@ -130,6 +138,9 @@ d:has(~ f) { color: green; } + d:has(+ f) { + color: red; + } f:has(~ d) { color: red; } @@ -155,4 +166,14 @@ :global(.foo):has(.unused) { color: red; } + + g:has(> h > i) { + color: green; + } + h:has(> h > i) { + color: red; + } + g:has(+ j > k) { + color: green; + } </style> diff --git a/packages/svelte/tests/css/samples/render-tag-loop/_config.js b/packages/svelte/tests/css/samples/render-tag-loop/_config.js index f623b92cc3..292c6c49ac 100644 --- a/packages/svelte/tests/css/samples/render-tag-loop/_config.js +++ b/packages/svelte/tests/css/samples/render-tag-loop/_config.js @@ -1,20 +1,5 @@ import { test } from '../../test'; export default test({ - warnings: [ - { - code: 'css_unused_selector', - message: 'Unused CSS selector "div + div"', - start: { - line: 19, - column: 1, - character: 185 - }, - end: { - line: 19, - column: 10, - character: 194 - } - } - ] + warnings: [] }); diff --git a/packages/svelte/tests/css/samples/render-tag-loop/expected.css b/packages/svelte/tests/css/samples/render-tag-loop/expected.css index 9ced15e964..3e449286c9 100644 --- a/packages/svelte/tests/css/samples/render-tag-loop/expected.css +++ b/packages/svelte/tests/css/samples/render-tag-loop/expected.css @@ -2,9 +2,12 @@ div.svelte-xyz div:where(.svelte-xyz) { color: green; } - /* (unused) div + div { - color: red; /* this is marked as unused, but only because we've written an infinite loop - worth fixing? *\/ - }*/ + div.svelte-xyz + div:where(.svelte-xyz) { + color: green; + } div.svelte-xyz:has(div:where(.svelte-xyz)) { color: green; } + span.svelte-xyz:has(~span:where(.svelte-xyz)) { + color: green; + } diff --git a/packages/svelte/tests/css/samples/render-tag-loop/input.svelte b/packages/svelte/tests/css/samples/render-tag-loop/input.svelte index ade8df5744..3c55261f18 100644 --- a/packages/svelte/tests/css/samples/render-tag-loop/input.svelte +++ b/packages/svelte/tests/css/samples/render-tag-loop/input.svelte @@ -12,14 +12,22 @@ </div> {/snippet} +{#snippet c()} + <span></span> + {@render c()} +{/snippet} + <style> div div { color: green; } div + div { - color: red; /* this is marked as unused, but only because we've written an infinite loop - worth fixing? */ + color: green; } div:has(div) { color: green; } + span:has(~span) { + color: green; + } </style> From aaeda65f2f31585a4e48b452b73874e10fd4ebfc Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 14 Mar 2025 22:46:52 +0100 Subject: [PATCH 0257/1429] docs: add docs on state_unsafe_mutation error (#14932) closes #14752 --- .../98-reference/.generated/client-errors.md | 24 +++++++++++++++++++ .../svelte/messages/client-errors/errors.md | 24 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index 2c2e0707ea..0beb3cb9a9 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -133,3 +133,27 @@ Reading state that was created inside the same derived is forbidden. Consider us ``` Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state` ``` + +This error is thrown in a situation like this: + +```svelte +<script> + let count = $state(0); + let multiple = $derived.by(() => { + const result = count * 2; + if (result > 10) { + count = 0; + } + return result; + }); +</script> + +<button onclick={() => count++}>{count} / {multiple}</button> +``` + +Here, the `$derived` updates `count`, which is `$state` and therefore forbidden to do. It is forbidden because the reactive graph could become unstable as a result, leading to subtle bugs, like values being stale or effects firing in the wrong order. To prevent this, Svelte errors when detecting an update to a `$state` variable. + +To fix this: +- See if it's possible to refactor your `$derived` such that the update becomes unnecessary +- Think about why you need to update `$state` inside a `$derived` in the first place. Maybe it's because you're using `bind:`, which leads you down a bad code path, and separating input and output path (by splitting it up to an attribute and an event, or by using [Function bindings](bind#Function-bindings)) makes it possible avoid the update +- If it's unavoidable, you may need to use an [`$effect`]($effect) instead. This could include splitting parts of the `$derived` into an [`$effect`]($effect) which does the updates diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index ce1f222c63..ab4d1519c1 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -87,3 +87,27 @@ See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-long ## state_unsafe_mutation > Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state` + +This error is thrown in a situation like this: + +```svelte +<script> + let count = $state(0); + let multiple = $derived.by(() => { + const result = count * 2; + if (result > 10) { + count = 0; + } + return result; + }); +</script> + +<button onclick={() => count++}>{count} / {multiple}</button> +``` + +Here, the `$derived` updates `count`, which is `$state` and therefore forbidden to do. It is forbidden because the reactive graph could become unstable as a result, leading to subtle bugs, like values being stale or effects firing in the wrong order. To prevent this, Svelte errors when detecting an update to a `$state` variable. + +To fix this: +- See if it's possible to refactor your `$derived` such that the update becomes unnecessary +- Think about why you need to update `$state` inside a `$derived` in the first place. Maybe it's because you're using `bind:`, which leads you down a bad code path, and separating input and output path (by splitting it up to an attribute and an event, or by using [Function bindings](bind#Function-bindings)) makes it possible avoid the update +- If it's unavoidable, you may need to use an [`$effect`]($effect) instead. This could include splitting parts of the `$derived` into an [`$effect`]($effect) which does the updates From 32ee6c1bc252023e79acb9c6c255079964dfb665 Mon Sep 17 00:00:00 2001 From: adiGuba <frederic.martini@gmail.com> Date: Sat, 15 Mar 2025 18:46:44 +0100 Subject: [PATCH 0258/1429] rune_invalid_arguments_length (#15516) --- .changeset/two-spies-lie.md | 5 +++++ .../compiler/phases/2-analyze/visitors/CallExpression.js | 2 +- .../samples/runes-wrong-state-raw-args/_config.js | 8 ++++++++ .../samples/runes-wrong-state-raw-args/main.svelte | 3 +++ .../samples/runes-wrong-state-raw-args/main.svelte.js | 1 + 5 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 .changeset/two-spies-lie.md create mode 100644 packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/_config.js create mode 100644 packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/main.svelte create mode 100644 packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/main.svelte.js diff --git a/.changeset/two-spies-lie.md b/.changeset/two-spies-lie.md new file mode 100644 index 0000000000..2ea7fd6136 --- /dev/null +++ b/.changeset/two-spies-lie.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: throw rune_invalid_arguments_length when $state.raw() is used with more than 1 arg diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index 6c21717852..6ef323725b 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -117,7 +117,7 @@ export function CallExpression(node, context) { if ((rune === '$derived' || rune === '$derived.by') && node.arguments.length !== 1) { e.rune_invalid_arguments_length(node, rune, 'exactly one argument'); - } else if (rune === '$state' && node.arguments.length > 1) { + } else if (node.arguments.length > 1) { e.rune_invalid_arguments_length(node, rune, 'zero or one arguments'); } diff --git a/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/_config.js new file mode 100644 index 0000000000..af226559d1 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'rune_invalid_arguments_length', + message: '`$state.raw` must be called with zero or one arguments' + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/main.svelte new file mode 100644 index 0000000000..2b50b43b9a --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/main.svelte @@ -0,0 +1,3 @@ +<script> + const foo = $state.raw(1, 2, 3); +</script> diff --git a/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/main.svelte.js b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/main.svelte.js new file mode 100644 index 0000000000..442aaad142 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/runes-wrong-state-raw-args/main.svelte.js @@ -0,0 +1 @@ +const foo = $state.raw(1, 2, 3); From f30d75ab7e289f741379de7d715c0cb2508a3596 Mon Sep 17 00:00:00 2001 From: Garik Asplund <111464359+garikAsplund@users.noreply.github.com> Date: Sun, 16 Mar 2025 14:25:52 -0700 Subject: [PATCH 0259/1429] =?UTF-8?q?updated=20->=20to=20=20=E2=86=92=20in?= =?UTF-8?q?=20v5-migration-guide=20(#15526)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/07-misc/07-v5-migration-guide.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/documentation/docs/07-misc/07-v5-migration-guide.md b/documentation/docs/07-misc/07-v5-migration-guide.md index 87ff40cf47..36e9776364 100644 --- a/documentation/docs/07-misc/07-v5-migration-guide.md +++ b/documentation/docs/07-misc/07-v5-migration-guide.md @@ -10,7 +10,7 @@ You don't have to migrate to the new syntax right away - Svelte 5 still supports At the heart of Svelte 5 is the new runes API. Runes are basically compiler instructions that inform Svelte about reactivity. Syntactically, runes are functions starting with a dollar-sign. -### let -> $state +### let → $state In Svelte 4, a `let` declaration at the top level of a component was implicitly reactive. In Svelte 5, things are more explicit: a variable is reactive when created using the `$state` rune. Let's migrate the counter to runes mode by wrapping the counter in `$state`: @@ -25,7 +25,7 @@ Nothing else changes. `count` is still the number itself, and you read and write > [!DETAILS] Why we did this > `let` being implicitly reactive at the top level worked great, but it meant that reactivity was constrained - a `let` declaration anywhere else was not reactive. This forced you to resort to using stores when refactoring code out of the top level of components for reuse. This meant you had to learn an entirely separate reactivity model, and the result often wasn't as nice to work with. Because reactivity is more explicit in Svelte 5, you can keep using the same API outside the top level of components. Head to [the tutorial](/tutorial) to learn more. -### $: -> $derived/$effect +### $: → $derived/$effect In Svelte 4, a `$:` statement at the top level of a component could be used to declare a derivation, i.e. state that is entirely defined through a computation of other state. In Svelte 5, this is achieved using the `$derived` rune: @@ -73,7 +73,7 @@ Note that [when `$effect` runs is different]($effect#Understanding-dependencies) > - executing dependencies as needed and therefore being immune to ordering problems > - being TypeScript-friendly -### export let -> $props +### export let → $props In Svelte 4, properties of a component were declared using `export let`. Each property was one declaration. In Svelte 5, all properties are declared through the `$props` rune, through destructuring: @@ -466,11 +466,11 @@ By now you should have a pretty good understanding of the before/after and how t We thought the same, which is why we provide a migration script to do most of the migration automatically. You can upgrade your project by using `npx sv migrate svelte-5`. This will do the following things: - bump core dependencies in your `package.json` -- migrate to runes (`let` -> `$state` etc) -- migrate to event attributes for DOM elements (`on:click` -> `onclick`) -- migrate slot creations to render tags (`<slot />` -> `{@render children()}`) -- migrate slot usages to snippets (`<div slot="x">...</div>` -> `{#snippet x()}<div>...</div>{/snippet}`) -- migrate obvious component creations (`new Component(...)` -> `mount(Component, ...)`) +- migrate to runes (`let` → `$state` etc) +- migrate to event attributes for DOM elements (`on:click` → `onclick`) +- migrate slot creations to render tags (`<slot />` → `{@render children()}`) +- migrate slot usages to snippets (`<div slot="x">...</div>` → `{#snippet x()}<div>...</div>{/snippet}`) +- migrate obvious component creations (`new Component(...)` → `mount(Component, ...)`) You can also migrate a single component in VS Code through the `Migrate Component to Svelte 5 Syntax` command, or in our Playground through the `Migrate` button. From e5881eade3e53316ee4329349cc1297c79d8522d Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Sun, 16 Mar 2025 17:32:22 -0400 Subject: [PATCH 0260/1429] chore: tweak migration doc diff blocks (#15527) --- .../docs/07-misc/07-v5-migration-guide.md | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/documentation/docs/07-misc/07-v5-migration-guide.md b/documentation/docs/07-misc/07-v5-migration-guide.md index 36e9776364..e502b7921a 100644 --- a/documentation/docs/07-misc/07-v5-migration-guide.md +++ b/documentation/docs/07-misc/07-v5-migration-guide.md @@ -16,7 +16,7 @@ In Svelte 4, a `let` declaration at the top level of a component was implicitly ```svelte <script> - let count = +++$state(+++0+++)+++; + let count = +++$state(0)+++; </script> ``` @@ -31,8 +31,8 @@ In Svelte 4, a `$:` statement at the top level of a component could be used to d ```svelte <script> - let count = +++$state(+++0+++)+++; - ---$:--- +++const+++ double = +++$derived(+++count * 2+++)+++; + let count = $state(0); + ---$:--- +++const+++ double = +++$derived(count * 2)+++; </script> ``` @@ -42,7 +42,8 @@ A `$:` statement could also be used to create side effects. In Svelte 5, this is ```svelte <script> - let count = +++$state(+++0+++)+++; + let count = $state(0); + ---$:---+++$effect(() =>+++ { if (count > 5) { alert('Count is too high!'); @@ -79,8 +80,8 @@ In Svelte 4, properties of a component were declared using `export let`. Each pr ```svelte <script> - ---export let optional = 'unset'; - export let required;--- + ---export let optional = 'unset';--- + ---export let required;--- +++let { optional = 'unset', required } = $props();+++ </script> ``` @@ -105,8 +106,8 @@ In Svelte 5, the `$props` rune makes this straightforward without any additional ```svelte <script> - ---let klass = ''; - export { klass as class};--- + ---let klass = '';--- + ---export { klass as class};--- +++let { class: klass, ...rest } = $props();+++ </script> <button class={klass} {...---$$restProps---+++rest+++}>click me</button> @@ -192,9 +193,9 @@ This function is deprecated in Svelte 5. Instead, components should accept _call ```svelte <!--- file: Pump.svelte ---> <script> - ---import { createEventDispatcher } from 'svelte'; - const dispatch = createEventDispatcher(); - --- + ---import { createEventDispatcher } from 'svelte';--- + ---const dispatch = createEventDispatcher();--- + +++let { inflate, deflate } = $props();+++ let power = $state(5); </script> From 74917ae7039e512d6bf26b2b04f433eea7da8cd3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 16 Mar 2025 20:25:35 -0400 Subject: [PATCH 0261/1429] Version Packages (#15501) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/brown-rockets-shake.md | 5 ----- .changeset/cuddly-chefs-refuse.md | 5 ----- .changeset/curvy-countries-flow.md | 5 ----- .changeset/gold-hairs-jog.md | 5 ----- .changeset/hungry-dancers-tap.md | 5 ----- .changeset/plenty-bats-lay.md | 5 ----- .changeset/two-spies-lie.md | 5 ----- packages/svelte/CHANGELOG.md | 18 ++++++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 10 files changed, 20 insertions(+), 37 deletions(-) delete mode 100644 .changeset/brown-rockets-shake.md delete mode 100644 .changeset/cuddly-chefs-refuse.md delete mode 100644 .changeset/curvy-countries-flow.md delete mode 100644 .changeset/gold-hairs-jog.md delete mode 100644 .changeset/hungry-dancers-tap.md delete mode 100644 .changeset/plenty-bats-lay.md delete mode 100644 .changeset/two-spies-lie.md diff --git a/.changeset/brown-rockets-shake.md b/.changeset/brown-rockets-shake.md deleted file mode 100644 index 3772a88f6e..0000000000 --- a/.changeset/brown-rockets-shake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: invalidate parent effects when child effects update parent dependencies diff --git a/.changeset/cuddly-chefs-refuse.md b/.changeset/cuddly-chefs-refuse.md deleted file mode 100644 index 6672ac4ab3..0000000000 --- a/.changeset/cuddly-chefs-refuse.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly match `:has()` selector during css pruning diff --git a/.changeset/curvy-countries-flow.md b/.changeset/curvy-countries-flow.md deleted file mode 100644 index 6ef8545804..0000000000 --- a/.changeset/curvy-countries-flow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: replace `undefined` with `void 0` to avoid edge case diff --git a/.changeset/gold-hairs-jog.md b/.changeset/gold-hairs-jog.md deleted file mode 100644 index eaafced314..0000000000 --- a/.changeset/gold-hairs-jog.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: allow global-like pseudo-selectors refinement diff --git a/.changeset/hungry-dancers-tap.md b/.changeset/hungry-dancers-tap.md deleted file mode 100644 index 51b2f86019..0000000000 --- a/.changeset/hungry-dancers-tap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: don't distribute unused types definitions diff --git a/.changeset/plenty-bats-lay.md b/.changeset/plenty-bats-lay.md deleted file mode 100644 index cd5ce66e42..0000000000 --- a/.changeset/plenty-bats-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: add `files` and `group` to HTMLInputAttributes in elements.d.ts diff --git a/.changeset/two-spies-lie.md b/.changeset/two-spies-lie.md deleted file mode 100644 index 2ea7fd6136..0000000000 --- a/.changeset/two-spies-lie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: throw rune_invalid_arguments_length when $state.raw() is used with more than 1 arg diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 65b3edd1fd..e10a606fe4 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,23 @@ # svelte +## 5.23.1 + +### Patch Changes + +- fix: invalidate parent effects when child effects update parent dependencies ([#15506](https://github.com/sveltejs/svelte/pull/15506)) + +- fix: correctly match `:has()` selector during css pruning ([#15277](https://github.com/sveltejs/svelte/pull/15277)) + +- fix: replace `undefined` with `void 0` to avoid edge case ([#15511](https://github.com/sveltejs/svelte/pull/15511)) + +- fix: allow global-like pseudo-selectors refinement ([#15313](https://github.com/sveltejs/svelte/pull/15313)) + +- chore: don't distribute unused types definitions ([#15473](https://github.com/sveltejs/svelte/pull/15473)) + +- fix: add `files` and `group` to HTMLInputAttributes in elements.d.ts ([#15492](https://github.com/sveltejs/svelte/pull/15492)) + +- fix: throw rune_invalid_arguments_length when $state.raw() is used with more than 1 arg ([#15516](https://github.com/sveltejs/svelte/pull/15516)) + ## 5.23.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index c74c9d34ca..6f10b2a9ea 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.23.0", + "version": "5.23.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 5f06fd0753..32a50f3bce 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.23.0'; +export const VERSION = '5.23.1'; export const PUBLIC_VERSION = '5'; From 5b9f0df8ee97ba43a3d3af18f99f2dd44bd86965 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Tue, 18 Mar 2025 11:51:26 +0100 Subject: [PATCH 0262/1429] fix: don't hoist listeners that access non hoistable snippets (#15534) * fix: don't hoist listeners that access non hoistable snippets * chore: add comment * chore: fix auto import fumble --- .changeset/thick-pans-fold.md | 5 +++++ .../compiler/phases/2-analyze/visitors/Attribute.js | 9 +++++++++ .../unhoist-function-accessing-snippet/_config.js | 12 ++++++++++++ .../unhoist-function-accessing-snippet/main.svelte | 12 ++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 .changeset/thick-pans-fold.md create mode 100644 packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/main.svelte diff --git a/.changeset/thick-pans-fold.md b/.changeset/thick-pans-fold.md new file mode 100644 index 0000000000..b5b5cee53e --- /dev/null +++ b/.changeset/thick-pans-fold.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't hoist listeners that access non hoistable snippets diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js index 9124a8822f..3ba81767cc 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -183,6 +183,15 @@ function get_delegated_event(event_name, handler, context) { const binding = scope.get(reference); const local_binding = context.state.scope.get(reference); + // if the function access a snippet that can't be hoisted we bail out + if ( + local_binding !== null && + local_binding.initial?.type === 'SnippetBlock' && + !local_binding.initial.metadata.can_hoist + ) { + return unhoisted; + } + // If we are referencing a binding that is shadowed in another scope then bail out. if (local_binding !== null && binding !== null && local_binding.node !== binding.node) { return unhoisted; diff --git a/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/_config.js b/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/_config.js new file mode 100644 index 0000000000..b1229f5a8a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/_config.js @@ -0,0 +1,12 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target, errors }) { + const button = target.querySelector('button'); + flushSync(() => { + button?.click(); + }); + assert.deepEqual(errors, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/main.svelte b/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/main.svelte new file mode 100644 index 0000000000..e909d77fd6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/main.svelte @@ -0,0 +1,12 @@ +<script> + const log = () => { + console.log(snip); + } + let x = $state(0); +</script> + +<button onclick={log}></button> + +{#snippet snip()} + snippet {x} +{/snippet} \ No newline at end of file From 0af6f20c77c209a5ea5691f2d1c15e0e359fbed6 Mon Sep 17 00:00:00 2001 From: henrykrinkle01 <162001892+henrykrinkle01@users.noreply.github.com> Date: Tue, 18 Mar 2025 17:54:32 +0700 Subject: [PATCH 0263/1429] Fix grammar (#15533) --- documentation/docs/06-runtime/02-context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/06-runtime/02-context.md b/documentation/docs/06-runtime/02-context.md index 30799215b6..87b93a92b2 100644 --- a/documentation/docs/06-runtime/02-context.md +++ b/documentation/docs/06-runtime/02-context.md @@ -30,7 +30,7 @@ export const myGlobalState = $state({ This has a few drawbacks though: - it only safely works when your global state is only used client-side - for example, when you're building a single page application that does not render any of your components on the server. If your state ends up being managed and updated on the server, it could end up being shared between sessions and/or users, causing bugs -- it may give the false impression that certain state is global when in reality it should only used in a certain part of your app +- it may give the false impression that certain state is global when in reality it should only be used in a certain part of your app To solve these drawbacks, Svelte provides a few `context` primitives which alleviate these problems. From 190c0c7653435fd983d13a9594f5d70bdb4dd26f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 18 Mar 2025 13:28:29 -0400 Subject: [PATCH 0264/1429] Version Packages (#15536) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/thick-pans-fold.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/thick-pans-fold.md diff --git a/.changeset/thick-pans-fold.md b/.changeset/thick-pans-fold.md deleted file mode 100644 index b5b5cee53e..0000000000 --- a/.changeset/thick-pans-fold.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: don't hoist listeners that access non hoistable snippets diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index e10a606fe4..6461df1d25 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.23.2 + +### Patch Changes + +- fix: don't hoist listeners that access non hoistable snippets ([#15534](https://github.com/sveltejs/svelte/pull/15534)) + ## 5.23.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 6f10b2a9ea..d005eca0b9 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.23.1", + "version": "5.23.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 32a50f3bce..191b52ecef 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.23.1'; +export const VERSION = '5.23.2'; export const PUBLIC_VERSION = '5'; From 8f940ee0ff12be2ae6b393b4e021507d3f3e2068 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 19 Mar 2025 10:41:13 -0400 Subject: [PATCH 0265/1429] docs: use function bindings in "when not to use effect" (#15544) --- documentation/docs/02-runes/04-$effect.md | 42 ++++------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md index e346bceba8..6a2b565aea 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -280,7 +280,7 @@ You might be tempted to do something convoluted with effects to link one value t </label> ``` -Instead, use callbacks where possible ([demo](/playground/untitled#H4sIAAAAAAAACo1SMW6EMBD8imWluFMSIEUaDiKlvy5lSOHjlhOSMRZeTiDkv8deMEEJRcqdmZ1ZjzzxqpZgePo5cRw18JQA_sSVaPz0rnVk7iDRYxdhYA8vW4Wg0NnwzJRdrfGtUAVKQIYtCsly9pIkp4AZ7cQOezAoEA7JcWUkVBuCdol0dNWrEutWsV5fHfnhPQ5wZJMnCwyejxCh6G6A0V3IHk4zu_jOxzzPBxBld83PTr7xXrb3rUNw8PbiYJ3FP22oTIoLSComq5XuXTeu8LzgnVA3KDgj13wiQ8taRaJ82rzXskYM-URRlsXktejjgNLoo9e4fyf70_8EnwncySX1GuunX6kGRwnzR_BgaPNaGy3FmLJKwrCUeBM6ZUn0Cs2mOlp3vwthQJ5i14P9st9vZqQlsQIAAA==)): +Instead, use `oninput` callbacks or — better still — [function bindings](bind#Function-bindings) where possible ([demo](/playground/untitled#H4sIAAAAAAAAE51SsW6DMBT8FcvqABINdOhCIFKXTt06lg4GHpElYyz8iECIf69tcIIipo6-u3f3fPZMJWuBpvRzkBXyTpKSy5rLq6YRbbgATdOfmeKkrMgCBt9GPpQ66RsItFjJNBzhVScRJBobmumq5wovhSxQABLskAmSk7ckOXtMKyM22ItGhhAk4Z0R0OwIN-tIQzd-90HVhvy2HsGNiQFCMltBgd7XoecV2xzXNV7XaEcth7ZfRv7kujnsTX2Qd7USb5rFjwZkJlgJwpWRcakG04cpOS9oz-QVCuoeInXW-RyEJL-sG0b7Wy6kZWM-u7CFxM5tdrIl9qg72vB74H-y7T2iXROHyVb0CLanp1yNk4D1A1jQ91hzrQSbUtIIGLcir0ylJDm9Q7urz42bX4UwIk2xH2D5Xf4A7SeMcMQCAAA=)): ```svelte <script> @@ -288,54 +288,26 @@ Instead, use callbacks where possible ([demo](/playground/untitled#H4sIAAAAAAAAC let spent = $state(0); let left = $state(total); - function updateSpent(e) { - spent = +e.target.value; + function updateSpent(value) { + spent = value; left = total - spent; } - function updateLeft(e) { - left = +e.target.value; + function updateLeft(value) { + left = value; spent = total - left; } </script> <label> - <input type="range" value={spent} oninput={updateSpent} max={total} /> + <input type="range" bind:value={() => spent, updateSpent} max={total} /> {spent}/{total} spent </label> <label> - <input type="range" value={left} oninput={updateLeft} max={total} /> + <input type="range" bind:value={() => left, updateLeft} max={total} /> {left}/{total} left </label> ``` -If you need to use bindings, for whatever reason (for example when you want some kind of "writable `$derived`"), consider using getters and setters to synchronise state ([demo](/playground/untitled#H4sIAAAAAAAACpWRwW6DMBBEf8WyekikFOihFwcq9TvqHkyyQUjGsfCCQMj_XnvBNKpy6Qn2DTOD1wu_tRocF18Lx9kCFwT4iRvVxenT2syNoDGyWjl4xi93g2AwxPDSXfrW4oc0EjUgwzsqzSr2VhTnxJwNHwf24lAhHIpjVDZNwy1KS5wlNoGMSg9wOCYksQccerMlv65p51X0p_Xpdt_4YEy9yTkmV3z4MJT579-bUqsaNB2kbI0dwlnCgirJe2UakJzVrbkKaqkWivasU1O1ULxnOVk3JU-Uxti0p_-vKO4no_enbQ_yXhnZn0aHs4b1jiJMK7q2zmo1C3bTMG3LaZQVrMjeoSPgaUtkDxePMCEX2Ie6b_8D4WyJJEwCAAA=)): - -```svelte -<script> - let total = 100; - let spent = $state(0); - - let left = { - get value() { - return total - spent; - }, - set value(v) { - spent = total - v; - } - }; -</script> - -<label> - <input type="range" bind:value={spent} max={total} /> - {spent}/{total} spent -</label> - -<label> - <input type="range" bind:value={left.value} max={total} /> - {left.value}/{total} left -</label> -``` - If you absolutely have to update `$state` within an effect and run into an infinite loop because you read and write to the same `$state`, use [untrack](svelte#untrack). From 701f085c82d11e6064433731d36b33d4894c706a Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 19 Mar 2025 10:43:45 -0400 Subject: [PATCH 0266/1429] docs: rewrite context docs (#15541) --- documentation/docs/06-runtime/02-context.md | 164 ++++++++++---------- 1 file changed, 86 insertions(+), 78 deletions(-) diff --git a/documentation/docs/06-runtime/02-context.md b/documentation/docs/06-runtime/02-context.md index 87b93a92b2..b698323a04 100644 --- a/documentation/docs/06-runtime/02-context.md +++ b/documentation/docs/06-runtime/02-context.md @@ -2,129 +2,137 @@ title: Context --- -<!-- - get/set/hasContext -- how to use, best practises (like encapsulating them) --> +Context allows components to access values owned by parent components without passing them down as props (potentially through many layers of intermediate components, known as 'prop-drilling'). The parent component sets context with `setContext(key, value)`... -Most state is component-level state that lives as long as its component lives. There's also section-wide or app-wide state however, which also needs to be handled somehow. - -The easiest way to do that is to create global state and just import that. +```svelte +<!--- file: Parent.svelte ---> +<script> + import { setContext } from 'svelte'; -```ts -/// file: state.svelte.js -export const myGlobalState = $state({ - user: { - /* ... */ - } - /* ... */ -}); + setContext('my-context', 'hello from Parent.svelte'); +</script> ``` +...and the child retrieves it with `getContext`: + ```svelte -<!--- file: App.svelte ---> +<!--- file: Child.svelte ---> <script> - import { myGlobalState } from './state.svelte.js'; - // ... + import { getContext } from 'svelte'; + + const message = getContext('my-context'); </script> + +<h1>{message}, inside Child.svelte</h1> ``` -This has a few drawbacks though: +This is particularly useful when `Parent.svelte` is not directly aware of `Child.svelte`, but instead renders it as part of a `children` [snippet](snippet) ([demo](/playground/untitled#H4sIAAAAAAAAE42Q3W6DMAyFX8WyJgESK-oto6hTX2D3YxcM3IIUQpR40yqUd58CrCXsp7tL7HNsf2dAWXaEKR56yfTBGOOxFWQwfR6Qz8q1XAHjL-GjUhvzToJd7bU09FO9ctMkG0wxM5VuFeeFLLjtVK8ZnkpNkuGo-w6CTTJ9Z3PwsBAemlbUF934W8iy5DpaZtOUcU02-ZLcaS51jHEkTFm_kY1_wfOO8QnXrb8hBzDEc6pgZ4gFoyz4KgiD7nxfTe8ghqAhIfrJ46cTzVZBbkPlODVJsLCDO6V7ZcJoncyw1yRr0hd1GNn_ZbEM3I9i1bmVxOlWElUvDUNHxpQngt3C4CXzjS1rtvkw22wMrTRtTbC8Lkuabe7jvthPPe3DofYCAAA=)): + +```svelte +<Parent> + <Child /> +</Parent> +``` -- it only safely works when your global state is only used client-side - for example, when you're building a single page application that does not render any of your components on the server. If your state ends up being managed and updated on the server, it could end up being shared between sessions and/or users, causing bugs -- it may give the false impression that certain state is global when in reality it should only be used in a certain part of your app +The key (`'my-context'`, in the example above) and the context itself can be any JavaScript value. -To solve these drawbacks, Svelte provides a few `context` primitives which alleviate these problems. +In addition to [`setContext`](svelte#setContext) and [`getContext`](svelte#getContext), Svelte exposes [`hasContext`](svelte#hasContext) and [`getAllContexts`](svelte#getAllContexts) functions. -## Setting and getting context +## Using context with state -To associate an arbitrary object with the current component, use `setContext`. +You can store reactive state in context ([demo](/playground/untitled#H4sIAAAAAAAAE41R0W6DMAz8FSuaBNUQdK8MkKZ-wh7HHihzu6hgosRMm1D-fUpSVNq12x4iEvvOx_kmQU2PIhfP3DCCJGgHYvxkkYid7NCI_GUS_KUcxhVEMjOelErNB3bsatvG4LW6n0ZsRC4K02qpuKqpZtmrQTNMYJA3QRAs7PTQQxS40eMCt3mX3duxnWb-lS5h7nTI0A4jMWoo4c44P_Hku-zrOazdy64chWo-ScfRkRgl8wgHKrLTH1OxHZkHgoHaTraHcopXUFYzPPVfuC_hwQaD1GrskdiNCdQwJljJqlvXfyqVsA5CGg0uRUQifHw56xFtciO75QrP07vo_JXf_tf8yK2ezDKY_ZWt_1y2qqYzv7bI1IW1V_sN19m-07wCAAA=))... ```svelte <script> import { setContext } from 'svelte'; + import Child from './Child.svelte'; - setContext('key', value); + let counter = $state({ + count: 0 + }); + + setContext('counter', counter); </script> + +<button onclick={() => counter.count += 1}> + increment +</button> + +<Child /> +<Child /> +<Child /> ``` -The context is then available to children of the component (including slotted content) with `getContext`. +...though note that if you _reassign_ `counter` instead of updating it, you will 'break the link' — in other words instead of this... ```svelte -<script> - import { getContext } from 'svelte'; - - const value = getContext('key'); -</script> +<button onclick={() => counter = { count: 0 }}> + reset +</button> ``` -`setContext` and `getContext` solve the above problems: +...you must do this: -- the state is not global, it's scoped to the component. That way it's safe to render your components on the server and not leak state -- it's clear that the state is not global but rather scoped to a specific component tree and therefore can't be used in other parts of your app +```svelte +<button onclick={() => +++counter.count = 0+++}> + reset +</button> +``` -> [!NOTE] `setContext`/`getContext` must be called during component initialisation. +Svelte will warn you if you get it wrong. -Context is not inherently reactive. If you need reactive values in context then you can pass a `$state` object into context, whose properties _will_ be reactive. +## Type-safe context -```svelte -<!--- file: Parent.svelte ---> -<script> - import { setContext } from 'svelte'; +A useful pattern is to wrap the calls to `setContext` and `getContext` inside helper functions that let you preserve type safety: - let value = $state({ count: 0 }); - setContext('counter', value); -</script> +```js +/// file: context.js +// @filename: ambient.d.ts +interface User {} -<button onclick={() => value.count++}>increment</button> -``` +// @filename: index.js +// ---cut--- +import { getContext, setContext } from 'svelte'; -```svelte -<!--- file: Child.svelte ---> -<script> - import { getContext } from 'svelte'; +let key = {}; - const value = getContext('counter'); -</script> +/** @param {User} user */ +export function setUserContext(user) { + setContext(key, user); +} -<p>Count is {value.count}</p> +export function getUserContext() { + return /** @type {User} */ (getContext(key)); +} ``` -To check whether a given `key` has been set in the context of a parent component, use `hasContext`. +## Replacing global state -```svelte -<script> - import { hasContext } from 'svelte'; +When you have state shared by many different components, you might be tempted to put it in its own module and just import it wherever it's needed: - if (hasContext('key')) { - // do something +```js +/// file: state.svelte.js +export const myGlobalState = $state({ + user: { + // ... } -</script> + // ... +}); ``` -You can also retrieve the whole context map that belongs to the closest parent component using `getAllContexts`. This is useful, for example, if you programmatically create a component and want to pass the existing context to it. +In many cases this is perfectly fine, but there is a risk: if you mutate the state during server-side rendering (which is discouraged, but entirely possible!)... ```svelte +<!--- file: App.svelte ----> <script> - import { getAllContexts } from 'svelte'; + import { myGlobalState } from 'svelte'; - const contexts = getAllContexts(); + let { data } = $props(); + + if (data.user) { + myGlobalState.user = data.user; + } </script> ``` -## Encapsulating context interactions - -The above methods are very unopinionated about how to use them. When your app grows in scale, it's worthwhile to encapsulate setting and getting the context into functions and properly type them. - -```ts -// @errors: 2304 -import { getContext, setContext } from 'svelte'; - -let userKey = Symbol('user'); - -export function setUserContext(user: User) { - setContext(userKey, user); -} - -export function getUserContext(): User { - return getContext(userKey) as User; -} -``` +...then the data may be accessible by the _next_ user. Context solves this problem because it is not shared between requests. From 99ca7a4d7f5948f94c2fa0137a481b57c4a6c17b Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 19 Mar 2025 14:06:20 -0400 Subject: [PATCH 0267/1429] chore: create stack lazily when proxying value (#15547) --- packages/svelte/src/internal/client/proxy.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index 4c262880f1..29828a7c99 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -25,11 +25,6 @@ import { tracing_mode_flag } from '../flags/index.js'; * @returns {T} */ export function proxy(value, parent = null, prev) { - /** @type {Error | null} */ - var stack = null; - if (DEV && tracing_mode_flag) { - stack = get_stack('CreatedAt'); - } // if non-proxyable, or is already a proxy, return `value` if (typeof value !== 'object' || value === null || STATE_SYMBOL in value) { return value; @@ -46,6 +41,8 @@ export function proxy(value, parent = null, prev) { var is_proxied_array = is_array(value); var version = source(0); + var stack = DEV && tracing_mode_flag ? get_stack('CreatedAt') : null; + if (is_proxied_array) { // We need to create the length source eagerly to ensure that // mutations to the array are properly synced with our proxy From c436b6cdbe01577b219ddbfd09e23c5765515004 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 19 Mar 2025 17:21:26 -0400 Subject: [PATCH 0268/1429] fix: simplify set calls for proxyable values (#15548) * chore: simplify set calls for proxyable values * changeset --- .changeset/nine-laws-rush.md | 5 ++ .../phases/3-transform/client/types.d.ts | 2 +- .../phases/3-transform/client/utils.js | 8 ---- .../client/visitors/AssignmentExpression.js | 46 +++++++------------ .../3-transform/client/visitors/ClassBody.js | 15 ++---- .../client/visitors/shared/declarations.js | 4 +- .../src/internal/client/reactivity/sources.js | 8 +++- .../_expected/client/index.svelte.js | 2 +- .../_expected/client/index.svelte.js | 4 +- .../_expected/client/index.svelte.js | 4 +- .../_expected/client/index.svelte.js | 2 +- 11 files changed, 40 insertions(+), 60 deletions(-) create mode 100644 .changeset/nine-laws-rush.md diff --git a/.changeset/nine-laws-rush.md b/.changeset/nine-laws-rush.md new file mode 100644 index 0000000000..e0a0fc15a0 --- /dev/null +++ b/.changeset/nine-laws-rush.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: simplify set calls for proxyable values diff --git a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts index 63fe3223cf..243e1c64a3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts +++ b/packages/svelte/src/compiler/phases/3-transform/client/types.d.ts @@ -30,7 +30,7 @@ export interface ClientTransformState extends TransformState { /** turn `foo` into e.g. `$.get(foo)` */ read: (id: Identifier) => Expression; /** turn `foo = bar` into e.g. `$.set(foo, bar)` */ - assign?: (node: Identifier, value: Expression) => Expression; + assign?: (node: Identifier, value: Expression, proxy?: boolean) => Expression; /** turn `foo.bar = baz` into e.g. `$.mutate(foo, $.get(foo).bar = baz);` */ mutate?: (node: Identifier, mutation: AssignmentExpression | UpdateExpression) => Expression; /** turn `foo++` into e.g. `$.update(foo)` */ diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index 421118cf68..28e3fabb19 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -45,14 +45,6 @@ export function build_getter(node, state) { return node; } -/** - * @param {Expression} value - * @param {Expression} previous - */ -export function build_proxy_reassignment(value, previous) { - return dev ? b.call('$.proxy', value, b.null, previous) : b.call('$.proxy', value); -} - /** * @param {FunctionDeclaration | FunctionExpression | ArrowFunctionExpression} node * @param {ComponentContext} context diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js index a8c615af93..150c56e166 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js @@ -1,5 +1,4 @@ -/** @import { Location } from 'locate-character' */ -/** @import { AssignmentExpression, AssignmentOperator, Expression, Identifier, Literal, MemberExpression, Pattern } from 'estree' */ +/** @import { AssignmentExpression, AssignmentOperator, Expression, Identifier, Pattern } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { Context } from '../types.js' */ import * as b from '../../../../utils/builders.js'; @@ -8,8 +7,8 @@ import { get_attribute_expression, is_event_attribute } from '../../../../utils/ast.js'; -import { dev, filename, is_ignored, locate_node, locator } from '../../../../state.js'; -import { build_proxy_reassignment, should_proxy } from '../utils.js'; +import { dev, is_ignored, locate_node } from '../../../../state.js'; +import { should_proxy } from '../utils.js'; import { visit_assignment_expression } from '../../shared/assignments.js'; /** @@ -65,21 +64,12 @@ function build_assignment(operator, left, right, context) { context.visit(build_assignment_value(operator, left, right)) ); - if ( + const needs_proxy = private_state.kind === 'state' && is_non_coercive_operator(operator) && - should_proxy(value, context.state.scope) - ) { - value = build_proxy_reassignment(value, b.member(b.this, private_state.id)); - } - - if (context.state.in_constructor) { - // inside the constructor, we can assign to `this.#foo.v` rather than using `$.set`, - // since nothing is tracking the signal at this point - return b.assignment(operator, /** @type {Pattern} */ (context.visit(left)), value); - } + should_proxy(value, context.state.scope); - return b.call('$.set', left, value); + return b.call('$.set', left, value, needs_proxy && b.true); } } @@ -113,20 +103,18 @@ function build_assignment(operator, left, right, context) { context.visit(build_assignment_value(operator, left, right)) ); - if ( + return transform.assign( + object, + value, !is_primitive && - binding.kind !== 'prop' && - binding.kind !== 'bindable_prop' && - binding.kind !== 'raw_state' && - binding.kind !== 'store_sub' && - context.state.analysis.runes && - should_proxy(right, context.state.scope) && - is_non_coercive_operator(operator) - ) { - value = build_proxy_reassignment(value, object); - } - - return transform.assign(object, value); + binding.kind !== 'prop' && + binding.kind !== 'bindable_prop' && + binding.kind !== 'raw_state' && + binding.kind !== 'store_sub' && + context.state.analysis.runes && + should_proxy(right, context.state.scope) && + is_non_coercive_operator(operator) + ); } // mutation diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js index ed800e5226..5787b590a8 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js @@ -5,7 +5,7 @@ import { dev, is_ignored } from '../../../../state.js'; import * as b from '../../../../utils/builders.js'; import { regex_invalid_identifier_chars } from '../../../patterns.js'; import { get_rune } from '../../../scope.js'; -import { build_proxy_reassignment, should_proxy } from '../utils.js'; +import { should_proxy } from '../utils.js'; /** * @param {ClassBody} node @@ -142,29 +142,20 @@ export function ClassBody(node, context) { // get foo() { return this.#foo; } body.push(b.method('get', definition.key, [], [b.return(b.call('$.get', member))])); - if (field.kind === 'state') { + if (field.kind === 'state' || field.kind === 'raw_state') { // set foo(value) { this.#foo = value; } const value = b.id('value'); - const prev = b.member(b.this, field.id); body.push( b.method( 'set', definition.key, [value], - [b.stmt(b.call('$.set', member, build_proxy_reassignment(value, prev)))] + [b.stmt(b.call('$.set', member, value, field.kind === 'state' && b.true))] ) ); } - if (field.kind === 'raw_state') { - // set foo(value) { this.#foo = value; } - const value = b.id('value'); - body.push( - b.method('set', definition.key, [value], [b.stmt(b.call('$.set', member, value))]) - ); - } - if (dev && (field.kind === 'derived' || field.kind === 'derived_by')) { body.push( b.method( diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/declarations.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/declarations.js index 0bd8c352f6..a13ecfed2c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/declarations.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/declarations.js @@ -24,8 +24,8 @@ export function add_state_transformers(context) { ) { context.state.transform[name] = { read: binding.declaration_kind === 'var' ? (node) => b.call('$.safe_get', node) : get_value, - assign: (node, value) => { - let call = b.call('$.set', node, value); + assign: (node, value, proxy = false) => { + let call = b.call('$.set', node, value, proxy && b.true); if (context.state.scope.get(`$${node.name}`)?.kind === 'store_sub') { call = b.call('$.store_unsub', call, b.literal(`$${node.name}`), b.id('$$stores')); diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 49584e8626..92508945c9 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -33,6 +33,7 @@ import * as e from '../errors.js'; import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js'; import { get_stack } from '../dev/tracing.js'; import { component_context, is_runes } from '../context.js'; +import { proxy } from '../proxy.js'; export let inspect_effects = new Set(); export const old_values = new Map(); @@ -143,9 +144,10 @@ export function mutate(source, value) { * @template V * @param {Source<V>} source * @param {V} value + * @param {boolean} [should_proxy] * @returns {V} */ -export function set(source, value) { +export function set(source, value, should_proxy = false) { if ( active_reaction !== null && !untracking && @@ -158,7 +160,9 @@ export function set(source, value) { e.state_unsafe_mutation(); } - return internal_set(source, value); + let new_value = should_proxy ? proxy(value, null, source) : value; + + return internal_set(source, new_value); } /** diff --git a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js index fa990b33ee..390e86a351 100644 --- a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js @@ -23,7 +23,7 @@ export default function Bind_component_snippet($$anchor) { return $.get(value); }, set value($$value) { - $.set(value, $.proxy($$value)); + $.set(value, $$value, true); } }); diff --git a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js index 2898f31a6f..2133974176 100644 --- a/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/class-state-field-constructor-assignment/_expected/client/index.svelte.js @@ -12,14 +12,14 @@ export default function Class_state_field_constructor_assignment($$anchor, $$pro } set a(value) { - $.set(this.#a, $.proxy(value)); + $.set(this.#a, value, true); } #b = $.state(); constructor() { this.a = 1; - this.#b.v = 2; + $.set(this.#b, 2); } } diff --git a/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js index 9651713c52..47f297bce9 100644 --- a/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js @@ -8,8 +8,8 @@ let d = 4; export function update(array) { ( - $.set(a, $.proxy(array[0])), - $.set(b, $.proxy(array[1])) + $.set(a, array[0], true), + $.set(b, array[1], true) ); [c, d] = array; diff --git a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js index c545608bca..762a23754c 100644 --- a/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/function-prop-no-getter/_expected/client/index.svelte.js @@ -13,7 +13,7 @@ export default function Function_prop_no_getter($$anchor) { Button($$anchor, { onmousedown: () => $.set(count, $.get(count) + 1), onmouseup, - onmouseenter: () => $.set(count, $.proxy(plusOne($.get(count)))), + onmouseenter: () => $.set(count, plusOne($.get(count)), true), children: ($$anchor, $$slotProps) => { $.next(); From c7ce9fc004325d5e5c957943f4c5e342e8304905 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Thu, 20 Mar 2025 14:19:42 -0400 Subject: [PATCH 0269/1429] fix benchmarks (#15560) --- benchmarking/compare/index.js | 1 - benchmarking/compare/runner.js | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/benchmarking/compare/index.js b/benchmarking/compare/index.js index a5fc6d10a9..9d8d279c35 100644 --- a/benchmarking/compare/index.js +++ b/benchmarking/compare/index.js @@ -2,7 +2,6 @@ import fs from 'node:fs'; import path from 'node:path'; import { execSync, fork } from 'node:child_process'; import { fileURLToPath } from 'node:url'; -import { benchmarks } from '../benchmarks.js'; // if (execSync('git status --porcelain').toString().trim()) { // console.error('Working directory is not clean'); diff --git a/benchmarking/compare/runner.js b/benchmarking/compare/runner.js index 6fa58e2bac..a2e8646379 100644 --- a/benchmarking/compare/runner.js +++ b/benchmarking/compare/runner.js @@ -1,7 +1,7 @@ -import { benchmarks } from '../benchmarks.js'; +import { reactivity_benchmarks } from '../benchmarks/reactivity/index.js'; const results = []; -for (const benchmark of benchmarks) { +for (const benchmark of reactivity_benchmarks) { const result = await benchmark(); console.error(result.benchmark); results.push(result); From 6915c12b583f4d62c125161be07f5d09573918c9 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Thu, 20 Mar 2025 16:04:00 -0400 Subject: [PATCH 0270/1429] feat: allow state created in deriveds/effects to be written/read locally without self-invalidation (#15553) * move parent property onto Signal * don't self-invalidate when updating a source create inside current reaction * lazily create deep state with parent reaction * no need to push_derived_source with mutable_state, as it never coexists with $.derived * reduce indirection * remove state_unsafe_local_read error * changeset * tests * fix test * inelegant fix * remove arg * tweak * some progress * more * tidy up * parent -> p * tmp * alternative approach * tidy up * reduce diff size * more * update comment --- .changeset/dirty-pianos-sparkle.md | 5 ++ .../98-reference/.generated/client-errors.md | 6 -- .../svelte/messages/client-errors/errors.md | 4 -- .../3-transform/client/transform-client.js | 5 +- .../client/visitors/VariableDeclaration.js | 4 +- .../svelte/src/internal/client/constants.js | 1 + packages/svelte/src/internal/client/errors.js | 15 ----- packages/svelte/src/internal/client/index.js | 9 ++- packages/svelte/src/internal/client/proxy.js | 67 +++++++++++++++---- .../src/internal/client/reactivity/sources.js | 57 +++++----------- .../svelte/src/internal/client/runtime.js | 30 +++++---- packages/svelte/tests/signals/test.ts | 49 ++++++++++++-- 12 files changed, 151 insertions(+), 101 deletions(-) create mode 100644 .changeset/dirty-pianos-sparkle.md diff --git a/.changeset/dirty-pianos-sparkle.md b/.changeset/dirty-pianos-sparkle.md new file mode 100644 index 0000000000..b3e4dd1d8c --- /dev/null +++ b/.changeset/dirty-pianos-sparkle.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: allow state created in deriveds/effects to be written/read locally without self-invalidation diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index 0beb3cb9a9..62d9c3302a 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -122,12 +122,6 @@ Property descriptors defined on `$state` objects must contain `value` and always Cannot set prototype of `$state` object ``` -### state_unsafe_local_read - -``` -Reading state that was created inside the same derived is forbidden. Consider using `untrack` to read locally created state -``` - ### state_unsafe_mutation ``` diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index ab4d1519c1..bc8ec36256 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -80,10 +80,6 @@ See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-long > Cannot set prototype of `$state` object -## state_unsafe_local_read - -> Reading state that was created inside the same derived is forbidden. Consider using `untrack` to read locally created state - ## state_unsafe_mutation > Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state` diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index ac8263b916..0bdfbae746 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -219,7 +219,10 @@ export function client_component(analysis, options) { for (const [name, binding] of analysis.instance.scope.declarations) { if (binding.kind === 'legacy_reactive') { legacy_reactive_declarations.push( - b.const(name, b.call('$.mutable_state', undefined, analysis.immutable ? b.true : undefined)) + b.const( + name, + b.call('$.mutable_source', undefined, analysis.immutable ? b.true : undefined) + ) ); } if (binding.kind === 'store_sub') { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js index baffc5dec3..3a914fb560 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js @@ -299,7 +299,7 @@ function create_state_declarators(declarator, { scope, analysis }, value) { return [ b.declarator( declarator.id, - b.call('$.mutable_state', value, analysis.immutable ? b.true : undefined) + b.call('$.mutable_source', value, analysis.immutable ? b.true : undefined) ) ]; } @@ -314,7 +314,7 @@ function create_state_declarators(declarator, { scope, analysis }, value) { return b.declarator( path.node, binding?.kind === 'state' - ? b.call('$.mutable_state', value, analysis.immutable ? b.true : undefined) + ? b.call('$.mutable_source', value, analysis.immutable ? b.true : undefined) : value ); }) diff --git a/packages/svelte/src/internal/client/constants.js b/packages/svelte/src/internal/client/constants.js index a4840ce4eb..21377c1cc8 100644 --- a/packages/svelte/src/internal/client/constants.js +++ b/packages/svelte/src/internal/client/constants.js @@ -20,6 +20,7 @@ export const LEGACY_DERIVED_PROP = 1 << 17; export const INSPECT_EFFECT = 1 << 18; export const HEAD_EFFECT = 1 << 19; export const EFFECT_HAS_DERIVED = 1 << 20; +export const EFFECT_IS_UPDATING = 1 << 21; export const STATE_SYMBOL = Symbol('$state'); export const STATE_SYMBOL_METADATA = Symbol('$state metadata'); diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 682816e1d6..8a5b5033a7 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -307,21 +307,6 @@ export function state_prototype_fixed() { } } -/** - * Reading state that was created inside the same derived is forbidden. Consider using `untrack` to read locally created state - * @returns {never} - */ -export function state_unsafe_local_read() { - if (DEV) { - const error = new Error(`state_unsafe_local_read\nReading state that was created inside the same derived is forbidden. Consider using \`untrack\` to read locally created state\nhttps://svelte.dev/e/state_unsafe_local_read`); - - error.name = 'Svelte error'; - throw error; - } else { - throw new Error(`https://svelte.dev/e/state_unsafe_local_read`); - } -} - /** * Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state` * @returns {never} diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 31da00dbb4..723ff57678 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -113,7 +113,14 @@ export { user_effect, user_pre_effect } from './reactivity/effects.js'; -export { mutable_state, mutate, set, state, update, update_pre } from './reactivity/sources.js'; +export { + mutable_source, + mutate, + set, + source as state, + update, + update_pre +} from './reactivity/sources.js'; export { prop, rest_props, diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index 29828a7c99..9c3c0cf29f 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -1,6 +1,6 @@ /** @import { ProxyMetadata, Source } from '#client' */ import { DEV } from 'esm-env'; -import { get, active_effect } from './runtime.js'; +import { get, active_effect, active_reaction, set_active_reaction } from './runtime.js'; import { component_context } from './context.js'; import { array_prototype, @@ -17,14 +17,16 @@ import * as e from './errors.js'; import { get_stack } from './dev/tracing.js'; import { tracing_mode_flag } from '../flags/index.js'; +/** @type {ProxyMetadata | null} */ +var parent_metadata = null; + /** * @template T * @param {T} value - * @param {ProxyMetadata | null} [parent] * @param {Source<T>} [prev] dev mode only * @returns {T} */ -export function proxy(value, parent = null, prev) { +export function proxy(value, prev) { // if non-proxyable, or is already a proxy, return `value` if (typeof value !== 'object' || value === null || STATE_SYMBOL in value) { return value; @@ -42,6 +44,31 @@ export function proxy(value, parent = null, prev) { var version = source(0); var stack = DEV && tracing_mode_flag ? get_stack('CreatedAt') : null; + var reaction = active_reaction; + + /** + * @template T + * @param {() => T} fn + */ + var with_parent = (fn) => { + var previous_reaction = active_reaction; + set_active_reaction(reaction); + + /** @type {T} */ + var result; + + if (DEV) { + var previous_metadata = parent_metadata; + parent_metadata = metadata; + result = fn(); + parent_metadata = previous_metadata; + } else { + result = fn(); + } + + set_active_reaction(previous_reaction); + return result; + }; if (is_proxied_array) { // We need to create the length source eagerly to ensure that @@ -54,7 +81,7 @@ export function proxy(value, parent = null, prev) { if (DEV) { metadata = { - parent, + parent: parent_metadata, owners: null }; @@ -66,7 +93,7 @@ export function proxy(value, parent = null, prev) { metadata.owners = prev_owners ? new Set(prev_owners) : null; } else { metadata.owners = - parent === null + parent_metadata === null ? component_context !== null ? new Set([component_context.function]) : null @@ -92,10 +119,13 @@ export function proxy(value, parent = null, prev) { var s = sources.get(prop); if (s === undefined) { - s = source(descriptor.value, stack); + s = with_parent(() => source(descriptor.value, stack)); sources.set(prop, s); } else { - set(s, proxy(descriptor.value, metadata)); + set( + s, + with_parent(() => proxy(descriptor.value)) + ); } return true; @@ -106,7 +136,10 @@ export function proxy(value, parent = null, prev) { if (s === undefined) { if (prop in target) { - sources.set(prop, source(UNINITIALIZED, stack)); + sources.set( + prop, + with_parent(() => source(UNINITIALIZED, stack)) + ); } } else { // When working with arrays, we need to also ensure we update the length when removing @@ -140,7 +173,7 @@ export function proxy(value, parent = null, prev) { // create a source, but only if it's an own property and not a prototype property if (s === undefined && (!exists || get_descriptor(target, prop)?.writable)) { - s = source(proxy(exists ? target[prop] : UNINITIALIZED, metadata), stack); + s = with_parent(() => source(proxy(exists ? target[prop] : UNINITIALIZED), stack)); sources.set(prop, s); } @@ -208,7 +241,7 @@ export function proxy(value, parent = null, prev) { (active_effect !== null && (!has || get_descriptor(target, prop)?.writable)) ) { if (s === undefined) { - s = source(has ? proxy(target[prop], metadata) : UNINITIALIZED, stack); + s = with_parent(() => source(has ? proxy(target[prop]) : UNINITIALIZED, stack)); sources.set(prop, s); } @@ -235,7 +268,7 @@ export function proxy(value, parent = null, prev) { // If the item exists in the original, we need to create a uninitialized source, // else a later read of the property would result in a source being created with // the value of the original item at that index. - other_s = source(UNINITIALIZED, stack); + other_s = with_parent(() => source(UNINITIALIZED, stack)); sources.set(i + '', other_s); } } @@ -247,13 +280,19 @@ export function proxy(value, parent = null, prev) { // object property before writing to that property. if (s === undefined) { if (!has || get_descriptor(target, prop)?.writable) { - s = source(undefined, stack); - set(s, proxy(value, metadata)); + s = with_parent(() => source(undefined, stack)); + set( + s, + with_parent(() => proxy(value)) + ); sources.set(prop, s); } } else { has = s.v !== UNINITIALIZED; - set(s, proxy(value, metadata)); + set( + s, + with_parent(() => proxy(value)) + ); } if (DEV) { diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 92508945c9..cac8431b4e 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -11,8 +11,8 @@ import { untrack, increment_write_version, update_effect, - derived_sources, - set_derived_sources, + reaction_sources, + set_reaction_sources, check_dirtiness, untracking, is_destroying_effect @@ -27,7 +27,8 @@ import { UNOWNED, MAYBE_DIRTY, BLOCK_EFFECT, - ROOT_EFFECT + ROOT_EFFECT, + EFFECT_IS_UPDATING } from '../constants.js'; import * as e from '../errors.js'; import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js'; @@ -51,6 +52,7 @@ export function set_inspect_effects(v) { * @param {Error | null} [stack] * @returns {Source<V>} */ +// TODO rename this to `state` throughout the codebase export function source(v, stack) { /** @type {Value} */ var signal = { @@ -62,6 +64,14 @@ export function source(v, stack) { wv: 0 }; + if (active_reaction !== null && active_reaction.f & EFFECT_IS_UPDATING) { + if (reaction_sources === null) { + set_reaction_sources([signal]); + } else { + reaction_sources.push(signal); + } + } + if (DEV && tracing_mode_flag) { signal.created = stack ?? get_stack('CreatedAt'); signal.debug = null; @@ -70,14 +80,6 @@ export function source(v, stack) { return signal; } -/** - * @template V - * @param {V} v - */ -export function state(v) { - return push_derived_source(source(v)); -} - /** * @template V * @param {V} initial_value @@ -100,33 +102,6 @@ export function mutable_source(initial_value, immutable = false) { return s; } -/** - * @template V - * @param {V} v - * @param {boolean} [immutable] - * @returns {Source<V>} - */ -export function mutable_state(v, immutable = false) { - return push_derived_source(mutable_source(v, immutable)); -} - -/** - * @template V - * @param {Source<V>} source - */ -/*#__NO_SIDE_EFFECTS__*/ -function push_derived_source(source) { - if (active_reaction !== null && !untracking && (active_reaction.f & DERIVED) !== 0) { - if (derived_sources === null) { - set_derived_sources([source]); - } else { - derived_sources.push(source); - } - } - - return source; -} - /** * @template V * @param {Value<V>} source @@ -153,14 +128,12 @@ export function set(source, value, should_proxy = false) { !untracking && is_runes() && (active_reaction.f & (DERIVED | BLOCK_EFFECT)) !== 0 && - // If the source was created locally within the current derived, then - // we allow the mutation. - (derived_sources === null || !derived_sources.includes(source)) + !reaction_sources?.includes(source) ) { e.state_unsafe_mutation(); } - let new_value = should_proxy ? proxy(value, null, source) : value; + let new_value = should_proxy ? proxy(value, source) : value; return internal_set(source, new_value); } diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 0a65c6e45a..74b58ee1a9 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -22,7 +22,8 @@ import { ROOT_EFFECT, LEGACY_DERIVED_PROP, DISCONNECTED, - BOUNDARY_EFFECT + BOUNDARY_EFFECT, + EFFECT_IS_UPDATING } from './constants.js'; import { flush_tasks } from './dom/task.js'; import { internal_set, old_values } from './reactivity/sources.js'; @@ -87,17 +88,17 @@ export function set_active_effect(effect) { } /** - * When sources are created within a derived, we record them so that we can safely allow - * local mutations to these sources without the side-effect error being invoked unnecessarily. + * When sources are created within a reaction, reading and writing + * them should not cause a re-run * @type {null | Source[]} */ -export let derived_sources = null; +export let reaction_sources = null; /** * @param {Source[] | null} sources */ -export function set_derived_sources(sources) { - derived_sources = sources; +export function set_reaction_sources(sources) { + reaction_sources = sources; } /** @@ -367,6 +368,9 @@ function schedule_possible_effect_self_invalidation(signal, effect, root = true) for (var i = 0; i < reactions.length; i++) { var reaction = reactions[i]; + + if (reaction_sources?.includes(signal)) continue; + if ((reaction.f & DERIVED) !== 0) { schedule_possible_effect_self_invalidation(/** @type {Derived} */ (reaction), effect, false); } else if (effect === reaction) { @@ -391,9 +395,10 @@ export function update_reaction(reaction) { var previous_untracked_writes = untracked_writes; var previous_reaction = active_reaction; var previous_skip_reaction = skip_reaction; - var prev_derived_sources = derived_sources; + var previous_reaction_sources = reaction_sources; var previous_component_context = component_context; var previous_untracking = untracking; + var flags = reaction.f; new_deps = /** @type {null | Value[]} */ (null); @@ -403,11 +408,13 @@ export function update_reaction(reaction) { (flags & UNOWNED) !== 0 && (untracking || !is_updating_effect || active_reaction === null); active_reaction = (flags & (BRANCH_EFFECT | ROOT_EFFECT)) === 0 ? reaction : null; - derived_sources = null; + reaction_sources = null; set_component_context(reaction.ctx); untracking = false; read_version++; + reaction.f |= EFFECT_IS_UPDATING; + try { var result = /** @type {Function} */ (0, reaction.fn)(); var deps = reaction.deps; @@ -477,9 +484,11 @@ export function update_reaction(reaction) { untracked_writes = previous_untracked_writes; active_reaction = previous_reaction; skip_reaction = previous_skip_reaction; - derived_sources = prev_derived_sources; + reaction_sources = previous_reaction_sources; set_component_context(previous_component_context); untracking = previous_untracking; + + reaction.f ^= EFFECT_IS_UPDATING; } } @@ -866,9 +875,6 @@ export function get(signal) { // Register the dependency on the current reaction signal. if (active_reaction !== null && !untracking) { - if (derived_sources !== null && derived_sources.includes(signal)) { - e.state_unsafe_local_read(); - } var deps = active_reaction.deps; if (signal.rv < read_version) { signal.rv = read_version; diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index ef4cf16d3b..72f99c90e5 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -8,7 +8,12 @@ import { render_effect, user_effect } from '../../src/internal/client/reactivity/effects'; -import { state, set, update, update_pre } from '../../src/internal/client/reactivity/sources'; +import { + source as state, + set, + update, + update_pre +} from '../../src/internal/client/reactivity/sources'; import type { Derived, Effect, Value } from '../../src/internal/client/types'; import { proxy } from '../../src/internal/client/proxy'; import { derived } from '../../src/internal/client/reactivity/deriveds'; @@ -487,6 +492,26 @@ describe('signals', () => { }; }); + test('schedules rerun when updating deeply nested value', (runes) => { + if (!runes) return () => {}; + + const value = proxy({ a: { b: { c: 0 } } }); + user_effect(() => { + value.a.b.c += 1; + }); + + return () => { + let errored = false; + try { + flushSync(); + } catch (e: any) { + assert.include(e.message, 'effect_update_depth_exceeded'); + errored = true; + } + assert.equal(errored, true); + }; + }); + test('schedules rerun when writing to signal before reading it', (runes) => { if (!runes) return () => {}; @@ -958,14 +983,30 @@ describe('signals', () => { }; }); - test('deriveds cannot depend on state they own', () => { + test('deriveds do not depend on state they own', () => { return () => { + let s; + const d = derived(() => { - const s = state(0); + s = state(0); return $.get(s); }); - assert.throws(() => $.get(d), 'state_unsafe_local_read'); + assert.equal($.get(d), 0); + + set(s!, 1); + assert.equal($.get(d), 0); + }; + }); + + test('effects do not depend on state they own', () => { + user_effect(() => { + const value = state(0); + set(value, $.get(value) + 1); + }); + + return () => { + flushSync(); }; }); From 1a5fb8fd51cdec1a72df9ec3100317bea83698aa Mon Sep 17 00:00:00 2001 From: Robert Gieseke <rob.g@web.de> Date: Fri, 21 Mar 2025 14:28:44 +0100 Subject: [PATCH 0271/1429] fix: Keep inlined JSDoc comments in property conversion of svelte-migrate (#15567) * Add failing JSDoc property svelte-migrate conversion tests * Add further test case and remove default value in JSDoc output * Look for inlined JSDoc comments after a hyphen * Add changeset --- .changeset/happy-cameras-bow.md | 5 +++++ packages/svelte/src/compiler/migrate/index.js | 7 +++++-- .../samples/jsdoc-with-comments/input.svelte | 9 +++++++++ .../samples/jsdoc-with-comments/output.svelte | 14 +++++++++++++- 4 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 .changeset/happy-cameras-bow.md diff --git a/.changeset/happy-cameras-bow.md b/.changeset/happy-cameras-bow.md new file mode 100644 index 0000000000..47188f4f6d --- /dev/null +++ b/.changeset/happy-cameras-bow.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +Keep inlined trailing JSDoc comments of properties when running svelte-migrate diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 1bb7a69a20..02bb5b1443 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -1592,7 +1592,6 @@ function extract_type_and_comment(declarator, state, path) { const comment_start = /** @type {any} */ (comment_node)?.start; const comment_end = /** @type {any} */ (comment_node)?.end; let comment = comment_node && str.original.substring(comment_start, comment_end); - if (comment_node) { str.update(comment_start, comment_end, ''); } @@ -1673,6 +1672,11 @@ function extract_type_and_comment(declarator, state, path) { state.has_type_or_fallback = true; const match = /@type {(.+)}/.exec(comment_node.value); if (match) { + // try to find JSDoc comments after a hyphen `-` + const jsdocComment = /@type {.+} (?:\w+|\[.*?\]) - (.+)/.exec(comment_node.value); + if (jsdocComment) { + cleaned_comment += jsdocComment[1]?.trim(); + } return { type: match[1], comment: cleaned_comment, @@ -1693,7 +1697,6 @@ function extract_type_and_comment(declarator, state, path) { }; } } - return { type: 'any', comment: state.uses_ts ? comment : cleaned_comment, diff --git a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte index f2efb1db80..f138c3a070 100644 --- a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte +++ b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/input.svelte @@ -21,6 +21,9 @@ */ export let type_no_comment; + /** @type {boolean} type_with_comment - One-line declaration with comment */ + export let type_with_comment; + /** * This is optional */ @@ -40,4 +43,10 @@ export let inline_multiline_trailing_comment = 'world'; /* * this is a same-line trailing multiline comment **/ + + /** @type {number} [default_value=1] */ + export let default_value = 1; + + /** @type {number} [comment_default_value=1] - This has a comment and an optional value. */ + export let comment_default_value = 1; </script> \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte index 19fbe38b50..32133ccd4c 100644 --- a/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte +++ b/packages/svelte/tests/migrate/samples/jsdoc-with-comments/output.svelte @@ -9,12 +9,18 @@ + + + + + + /** * @typedef {Object} Props * @property {string} comment - My wonderful comment @@ -22,11 +28,14 @@ * @property {any} one_line - one line comment * @property {any} no_comment * @property {boolean} type_no_comment + * @property {boolean} type_with_comment - One-line declaration with comment * @property {any} [optional] - This is optional * @property {any} inline_commented - this should stay a comment * @property {any} inline_commented_merged - This comment should be merged - with this inline comment * @property {string} [inline_multiline_leading_comment] - this is a same-line leading multiline comment * @property {string} [inline_multiline_trailing_comment] - this is a same-line trailing multiline comment + * @property {number} [default_value] + * @property {number} [comment_default_value] - This has a comment and an optional value. */ /** @type {Props} */ @@ -36,10 +45,13 @@ one_line, no_comment, type_no_comment, + type_with_comment, optional = {stuff: true}, inline_commented, inline_commented_merged, inline_multiline_leading_comment = 'world', - inline_multiline_trailing_comment = 'world' + inline_multiline_trailing_comment = 'world', + default_value = 1, + comment_default_value = 1 } = $props(); </script> \ No newline at end of file From 1d10a65b7858ca4da8d7ade113ec5b6f9c1afb43 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Fri, 21 Mar 2025 13:30:17 +0000 Subject: [PATCH 0272/1429] fix: check if DOM prototypes are extensible (#15569) --- .changeset/dry-ducks-roll.md | 5 +++ .../src/internal/client/dom/operations.js | 35 +++++++++++-------- packages/svelte/src/internal/shared/utils.js | 1 + 3 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 .changeset/dry-ducks-roll.md diff --git a/.changeset/dry-ducks-roll.md b/.changeset/dry-ducks-roll.md new file mode 100644 index 0000000000..2dea8174dd --- /dev/null +++ b/.changeset/dry-ducks-roll.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: check if DOM prototypes are extensible diff --git a/packages/svelte/src/internal/client/dom/operations.js b/packages/svelte/src/internal/client/dom/operations.js index 0ad9045b20..aae44d4b39 100644 --- a/packages/svelte/src/internal/client/dom/operations.js +++ b/packages/svelte/src/internal/client/dom/operations.js @@ -2,7 +2,7 @@ import { hydrate_node, hydrating, set_hydrate_node } from './hydration.js'; import { DEV } from 'esm-env'; import { init_array_prototype_warnings } from '../dev/equality.js'; -import { get_descriptor } from '../../shared/utils.js'; +import { get_descriptor, is_extensible } from '../../shared/utils.js'; // export these for reference in the compiled code, making global name deduplication unnecessary /** @type {Window} */ @@ -34,26 +34,31 @@ export function init_operations() { var element_prototype = Element.prototype; var node_prototype = Node.prototype; + var text_prototype = Text.prototype; // @ts-ignore first_child_getter = get_descriptor(node_prototype, 'firstChild').get; // @ts-ignore next_sibling_getter = get_descriptor(node_prototype, 'nextSibling').get; - // the following assignments improve perf of lookups on DOM nodes - // @ts-expect-error - element_prototype.__click = undefined; - // @ts-expect-error - element_prototype.__className = undefined; - // @ts-expect-error - element_prototype.__attributes = null; - // @ts-expect-error - element_prototype.__style = undefined; - // @ts-expect-error - element_prototype.__e = undefined; - - // @ts-expect-error - Text.prototype.__t = undefined; + if (is_extensible(element_prototype)) { + // the following assignments improve perf of lookups on DOM nodes + // @ts-expect-error + element_prototype.__click = undefined; + // @ts-expect-error + element_prototype.__className = undefined; + // @ts-expect-error + element_prototype.__attributes = null; + // @ts-expect-error + element_prototype.__style = undefined; + // @ts-expect-error + element_prototype.__e = undefined; + } + + if (is_extensible(text_prototype)) { + // @ts-expect-error + text_prototype.__t = undefined; + } if (DEV) { // @ts-expect-error diff --git a/packages/svelte/src/internal/shared/utils.js b/packages/svelte/src/internal/shared/utils.js index f9d52cb065..5e7f3152d8 100644 --- a/packages/svelte/src/internal/shared/utils.js +++ b/packages/svelte/src/internal/shared/utils.js @@ -10,6 +10,7 @@ export var get_descriptors = Object.getOwnPropertyDescriptors; export var object_prototype = Object.prototype; export var array_prototype = Array.prototype; export var get_prototype_of = Object.getPrototypeOf; +export var is_extensible = Object.isExtensible; /** * @param {any} thing From d2e79326c7d3810cd4ec657660d4aeec464cd689 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 21 Mar 2025 09:31:03 -0400 Subject: [PATCH 0273/1429] fix: don't depend on deriveds created inside the current reaction (#15564) * WIP * WIP * add test * update test * changeset * oops * lint --- .changeset/young-poets-wait.md | 5 +++ packages/svelte/src/internal/client/index.js | 11 +---- packages/svelte/src/internal/client/proxy.js | 2 +- .../internal/client/reactivity/deriveds.js | 16 ++++++- .../src/internal/client/reactivity/sources.js | 24 +++++++---- .../svelte/src/internal/client/runtime.js | 43 ++++++++++++------- .../samples/effect-cleanup/_config.js | 2 +- .../samples/untrack-own-deriveds/_config.js | 20 +++++++++ .../samples/untrack-own-deriveds/main.svelte | 26 +++++++++++ packages/svelte/tests/signals/test.ts | 7 +-- 10 files changed, 114 insertions(+), 42 deletions(-) create mode 100644 .changeset/young-poets-wait.md create mode 100644 packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/main.svelte diff --git a/.changeset/young-poets-wait.md b/.changeset/young-poets-wait.md new file mode 100644 index 0000000000..479f5027ef --- /dev/null +++ b/.changeset/young-poets-wait.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't depend on deriveds created inside the current reaction diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 723ff57678..a5f93e8b17 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -101,7 +101,7 @@ export { text, props_id } from './dom/template.js'; -export { derived, derived_safe_equal } from './reactivity/deriveds.js'; +export { user_derived as derived, derived_safe_equal } from './reactivity/deriveds.js'; export { effect_tracking, effect_root, @@ -113,14 +113,7 @@ export { user_effect, user_pre_effect } from './reactivity/effects.js'; -export { - mutable_source, - mutate, - set, - source as state, - update, - update_pre -} from './reactivity/sources.js'; +export { mutable_source, mutate, set, state, update, update_pre } from './reactivity/sources.js'; export { prop, rest_props, diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index 9c3c0cf29f..ffe63f4b77 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -10,7 +10,7 @@ import { object_prototype } from '../shared/utils.js'; import { check_ownership, widen_ownership } from './dev/ownership.js'; -import { source, set } from './reactivity/sources.js'; +import { state as source, set } from './reactivity/sources.js'; import { STATE_SYMBOL, STATE_SYMBOL_METADATA } from './constants.js'; import { UNINITIALIZED } from '../../constants.js'; import * as e from './errors.js'; diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 795417cc0f..cd7bbba02f 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -8,7 +8,8 @@ import { skip_reaction, update_reaction, increment_write_version, - set_active_effect + set_active_effect, + push_reaction_value } from '../runtime.js'; import { equals, safe_equals } from './equality.js'; import * as e from '../errors.js'; @@ -61,6 +62,19 @@ export function derived(fn) { return signal; } +/** + * @template V + * @param {() => V} fn + * @returns {Derived<V>} + */ +export function user_derived(fn) { + const d = derived(fn); + + push_reaction_value(d); + + return d; +} + /** * @template V * @param {() => V} fn diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index cac8431b4e..e4834902fe 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -15,7 +15,8 @@ import { set_reaction_sources, check_dirtiness, untracking, - is_destroying_effect + is_destroying_effect, + push_reaction_value } from '../runtime.js'; import { equals, safe_equals } from './equality.js'; import { @@ -64,14 +65,6 @@ export function source(v, stack) { wv: 0 }; - if (active_reaction !== null && active_reaction.f & EFFECT_IS_UPDATING) { - if (reaction_sources === null) { - set_reaction_sources([signal]); - } else { - reaction_sources.push(signal); - } - } - if (DEV && tracing_mode_flag) { signal.created = stack ?? get_stack('CreatedAt'); signal.debug = null; @@ -80,6 +73,19 @@ export function source(v, stack) { return signal; } +/** + * @template V + * @param {V} v + * @param {Error | null} [stack] + */ +export function state(v, stack) { + const s = source(v, stack); + + push_reaction_value(s); + + return s; +} + /** * @template V * @param {V} initial_value diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 74b58ee1a9..a5d26412a4 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -101,6 +101,17 @@ export function set_reaction_sources(sources) { reaction_sources = sources; } +/** @param {Value} value */ +export function push_reaction_value(value) { + if (active_reaction !== null && active_reaction.f & EFFECT_IS_UPDATING) { + if (reaction_sources === null) { + set_reaction_sources([value]); + } else { + reaction_sources.push(value); + } + } +} + /** * The dependencies of the reaction that is currently being executed. In many cases, * the dependencies are unchanged between runs, and so this will be `null` unless @@ -875,21 +886,23 @@ export function get(signal) { // Register the dependency on the current reaction signal. if (active_reaction !== null && !untracking) { - var deps = active_reaction.deps; - if (signal.rv < read_version) { - signal.rv = read_version; - // If the signal is accessing the same dependencies in the same - // order as it did last time, increment `skipped_deps` - // rather than updating `new_deps`, which creates GC cost - if (new_deps === null && deps !== null && deps[skipped_deps] === signal) { - skipped_deps++; - } else if (new_deps === null) { - new_deps = [signal]; - } else if (!skip_reaction || !new_deps.includes(signal)) { - // Normally we can push duplicated dependencies to `new_deps`, but if we're inside - // an unowned derived because skip_reaction is true, then we need to ensure that - // we don't have duplicates - new_deps.push(signal); + if (!reaction_sources?.includes(signal)) { + var deps = active_reaction.deps; + if (signal.rv < read_version) { + signal.rv = read_version; + // If the signal is accessing the same dependencies in the same + // order as it did last time, increment `skipped_deps` + // rather than updating `new_deps`, which creates GC cost + if (new_deps === null && deps !== null && deps[skipped_deps] === signal) { + skipped_deps++; + } else if (new_deps === null) { + new_deps = [signal]; + } else if (!skip_reaction || !new_deps.includes(signal)) { + // Normally we can push duplicated dependencies to `new_deps`, but if we're inside + // an unowned derived because skip_reaction is true, then we need to ensure that + // we don't have duplicates + new_deps.push(signal); + } } } } else if ( diff --git a/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js index 6a3d9eef77..e55733c148 100644 --- a/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/effect-cleanup/_config.js @@ -10,6 +10,6 @@ export default test({ flushSync(() => { b1.click(); }); - assert.deepEqual(logs, ['init 0', 'cleanup 2', null, 'init 2', 'cleanup 4', null, 'init 4']); + assert.deepEqual(logs, ['init 0']); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js new file mode 100644 index 0000000000..18062b86fb --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/_config.js @@ -0,0 +1,20 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + test({ assert, target, logs }) { + const button = target.querySelector('button'); + + flushSync(() => button?.click()); + + assert.htmlEqual( + target.innerHTML, + ` + <button>increment</button> + <p>1/2</p + ` + ); + + assert.deepEqual(logs, [0, 0]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/main.svelte b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/main.svelte new file mode 100644 index 0000000000..bbad2cdf4a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/untrack-own-deriveds/main.svelte @@ -0,0 +1,26 @@ +<script> + class Foo { + value = $state(0); + double = $derived(this.value * 2); + + constructor() { + console.log(this.value, this.double); + } + + increment() { + this.value++; + } + } + + let foo = $state(); + + $effect(() => { + foo = new Foo(); + }); +</script> + +<button onclick={() => foo.increment()}>increment</button> + +{#if foo} + <p>{foo.value}/{foo.double}</p> +{/if} diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index 72f99c90e5..3977caae36 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -8,12 +8,7 @@ import { render_effect, user_effect } from '../../src/internal/client/reactivity/effects'; -import { - source as state, - set, - update, - update_pre -} from '../../src/internal/client/reactivity/sources'; +import { state, set, update, update_pre } from '../../src/internal/client/reactivity/sources'; import type { Derived, Effect, Value } from '../../src/internal/client/types'; import { proxy } from '../../src/internal/client/proxy'; import { derived } from '../../src/internal/client/reactivity/deriveds'; From e25c2812961f9bb74ab50f1b034d8e5a5d8ae412 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 09:40:37 -0400 Subject: [PATCH 0274/1429] Version Packages (#15551) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/dirty-pianos-sparkle.md | 5 ----- .changeset/dry-ducks-roll.md | 5 ----- .changeset/happy-cameras-bow.md | 5 ----- .changeset/nine-laws-rush.md | 5 ----- .changeset/young-poets-wait.md | 5 ----- packages/svelte/CHANGELOG.md | 16 ++++++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 8 files changed, 18 insertions(+), 27 deletions(-) delete mode 100644 .changeset/dirty-pianos-sparkle.md delete mode 100644 .changeset/dry-ducks-roll.md delete mode 100644 .changeset/happy-cameras-bow.md delete mode 100644 .changeset/nine-laws-rush.md delete mode 100644 .changeset/young-poets-wait.md diff --git a/.changeset/dirty-pianos-sparkle.md b/.changeset/dirty-pianos-sparkle.md deleted file mode 100644 index b3e4dd1d8c..0000000000 --- a/.changeset/dirty-pianos-sparkle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: allow state created in deriveds/effects to be written/read locally without self-invalidation diff --git a/.changeset/dry-ducks-roll.md b/.changeset/dry-ducks-roll.md deleted file mode 100644 index 2dea8174dd..0000000000 --- a/.changeset/dry-ducks-roll.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: check if DOM prototypes are extensible diff --git a/.changeset/happy-cameras-bow.md b/.changeset/happy-cameras-bow.md deleted file mode 100644 index 47188f4f6d..0000000000 --- a/.changeset/happy-cameras-bow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -Keep inlined trailing JSDoc comments of properties when running svelte-migrate diff --git a/.changeset/nine-laws-rush.md b/.changeset/nine-laws-rush.md deleted file mode 100644 index e0a0fc15a0..0000000000 --- a/.changeset/nine-laws-rush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: simplify set calls for proxyable values diff --git a/.changeset/young-poets-wait.md b/.changeset/young-poets-wait.md deleted file mode 100644 index 479f5027ef..0000000000 --- a/.changeset/young-poets-wait.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: don't depend on deriveds created inside the current reaction diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 6461df1d25..8cb7efd4ef 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,21 @@ # svelte +## 5.24.0 + +### Minor Changes + +- feat: allow state created in deriveds/effects to be written/read locally without self-invalidation ([#15553](https://github.com/sveltejs/svelte/pull/15553)) + +### Patch Changes + +- fix: check if DOM prototypes are extensible ([#15569](https://github.com/sveltejs/svelte/pull/15569)) + +- Keep inlined trailing JSDoc comments of properties when running svelte-migrate ([#15567](https://github.com/sveltejs/svelte/pull/15567)) + +- fix: simplify set calls for proxyable values ([#15548](https://github.com/sveltejs/svelte/pull/15548)) + +- fix: don't depend on deriveds created inside the current reaction ([#15564](https://github.com/sveltejs/svelte/pull/15564)) + ## 5.23.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index d005eca0b9..0aa6b29841 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.23.2", + "version": "5.24.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 191b52ecef..7cd43e74cb 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.23.2'; +export const VERSION = '5.24.0'; export const PUBLIC_VERSION = '5'; From 6b23a7c4777a123dc1ea4db6cb87e03268fbb45b Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 21 Mar 2025 09:52:30 -0400 Subject: [PATCH 0275/1429] chore: camelCase -> snake_case (#15573) --- packages/svelte/src/compiler/migrate/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 02bb5b1443..7f26d0d010 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -1673,9 +1673,9 @@ function extract_type_and_comment(declarator, state, path) { const match = /@type {(.+)}/.exec(comment_node.value); if (match) { // try to find JSDoc comments after a hyphen `-` - const jsdocComment = /@type {.+} (?:\w+|\[.*?\]) - (.+)/.exec(comment_node.value); - if (jsdocComment) { - cleaned_comment += jsdocComment[1]?.trim(); + const jsdoc_comment = /@type {.+} (?:\w+|\[.*?\]) - (.+)/.exec(comment_node.value); + if (jsdoc_comment) { + cleaned_comment += jsdoc_comment[1]?.trim(); } return { type: match[1], From 83d0c5894dc26c92274f162c9f9495038cabe37d Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 21 Mar 2025 09:52:51 -0400 Subject: [PATCH 0276/1429] docs: add note on effect-local state (#15572) --- documentation/docs/02-runes/04-$effect.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md index 6a2b565aea..75f59102f9 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -74,6 +74,8 @@ Teardown functions also run when the effect is destroyed, which happens when its `$effect` automatically picks up any reactive values (`$state`, `$derived`, `$props`) that are _synchronously_ read inside its function body (including indirectly, via function calls) and registers them as dependencies. When those dependencies change, the `$effect` schedules a re-run. +If `$state` and `$derived` are used directly inside the `$effect` (for example, during creation of a [reactive class](https://svelte.dev/docs/svelte/$state#Classes)), those values will _not_ be treated as dependencies. + Values that are read _asynchronously_ — after an `await` or inside a `setTimeout`, for example — will not be tracked. Here, the canvas will be repainted when `color` changes, but not when `size` changes ([demo](/playground/untitled#H4sIAAAAAAAAE31T246bMBD9lZF3pWSlBEirfaEQqdo_2PatVIpjBrDkGGQPJGnEv1e2IZfVal-wfHzmzJyZ4cIqqdCy9M-F0blDlnqArZjmB3f72XWRHVCRw_bc4me4aDWhJstSlllhZEfbQhekkMDKfwg5PFvihMvX5OXH_CJa1Zrb0-Kpqr5jkiwC48rieuDWQbqgZ6wqFLRcvkC-hYvnkWi1dWqa8ESQTxFRjfQWsOXiWzmr0sSLhEJu3p1YsoJkNUcdZUnN9dagrBu6FVRQHAM10sJRKgUG16bXcGxQ44AGdt7SDkTDdY02iqLHnJVU6hedlWuIp94JW6Tf8oBt_8GdTxlF0b4n0C35ZLBzXb3mmYn3ae6cOW74zj0YVzDNYXRHFt9mprNgHfZSl6mzml8CMoLvTV6wTZIUDEJv5us2iwMtiJRyAKG4tXnhl8O0yhbML0Wm-B7VNlSSSd31BG7z8oIZZ6dgIffAVY_5xdU9Qrz1Bnx8fCfwtZ7v8Qc9j3nB8PqgmMWlHIID6-bkVaPZwDySfWtKNGtquxQ23Qlsq2QJT0KIqb8dL0up6xQ2eIBkAg_c1FI_YqW0neLnFCqFpwmreedJYT7XX8FVOBfwWRhXstZrSXiwKQjUhOZeMIleb5JZfHWn2Yq5pWEpmR7Hv-N_wEqT8hEEAAA=)): ```ts From ade66c6feade92cfd932dcb4be2812305e518d2b Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Fri, 21 Mar 2025 15:21:40 +0100 Subject: [PATCH 0277/1429] fix: use `get` in constructor for deriveds (#15300) Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/new-cherries-leave.md | 5 +++++ .../client/visitors/MemberExpression.js | 4 +++- .../samples/deriveds-in-constructor/_config.js | 5 +++++ .../deriveds-in-constructor/main.svelte | 18 ++++++++++++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 .changeset/new-cherries-leave.md create mode 100644 packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/main.svelte diff --git a/.changeset/new-cherries-leave.md b/.changeset/new-cherries-leave.md new file mode 100644 index 0000000000..738a78b4a3 --- /dev/null +++ b/.changeset/new-cherries-leave.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: use `get` in constructor for deriveds diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/MemberExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/MemberExpression.js index 501ecda555..3f2aada1f5 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/MemberExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/MemberExpression.js @@ -11,7 +11,9 @@ export function MemberExpression(node, context) { if (node.property.type === 'PrivateIdentifier') { const field = context.state.private_state.get(node.property.name); if (field) { - return context.state.in_constructor ? b.member(node, 'v') : b.call('$.get', node); + return context.state.in_constructor && (field.kind === 'raw_state' || field.kind === 'state') + ? b.member(node, 'v') + : b.call('$.get', node); } } diff --git a/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/_config.js b/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/_config.js new file mode 100644 index 0000000000..b364a989f4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: `<p>state,derived state,derived.by derived state</p>` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/main.svelte b/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/main.svelte new file mode 100644 index 0000000000..bc8efba7e7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/deriveds-in-constructor/main.svelte @@ -0,0 +1,18 @@ +<script> + class Foo { + #state = $state('state') + #derived = $derived('derived ' + this.#state); + #derivedBy = $derived.by(() => { + return 'derived.by ' + this.#derived + }); + + initial + + constructor() { + this.initial = [this.#state, this.#derived, this.#derivedBy] + } + } + const foo = new Foo() +</script> + +<p>{foo.initial}</p> \ No newline at end of file From 1f37c02f918d6fa4d8a14de5d6868228e61dd05a Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Fri, 21 Mar 2025 14:25:46 +0000 Subject: [PATCH 0278/1429] fix: ensure toStore root effect is connected to correct parent effect (#15574) * fix: ensure toStore root effect is connected to correct parent effect * prettier --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/twelve-bananas-destroy.md | 5 +++ packages/svelte/src/store/index-client.js | 35 +++++++++++++++---- .../samples/toStore-subscribe2/_config.js | 16 +++++++++ .../samples/toStore-subscribe2/main.svelte | 11 ++++++ 4 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 .changeset/twelve-bananas-destroy.md create mode 100644 packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/main.svelte diff --git a/.changeset/twelve-bananas-destroy.md b/.changeset/twelve-bananas-destroy.md new file mode 100644 index 0000000000..873ee21877 --- /dev/null +++ b/.changeset/twelve-bananas-destroy.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure toStore root effect is connected to correct parent effect diff --git a/packages/svelte/src/store/index-client.js b/packages/svelte/src/store/index-client.js index ae6806ec76..2f0a1a831a 100644 --- a/packages/svelte/src/store/index-client.js +++ b/packages/svelte/src/store/index-client.js @@ -6,6 +6,12 @@ import { } from '../internal/client/reactivity/effects.js'; import { get, writable } from './shared/index.js'; import { createSubscriber } from '../reactivity/create-subscriber.js'; +import { + active_effect, + active_reaction, + set_active_effect, + set_active_reaction +} from '../internal/client/runtime.js'; export { derived, get, readable, readonly, writable } from './shared/index.js'; @@ -39,19 +45,34 @@ export { derived, get, readable, readonly, writable } from './shared/index.js'; * @returns {Writable<V> | Readable<V>} */ export function toStore(get, set) { - let init_value = get(); + var effect = active_effect; + var reaction = active_reaction; + var init_value = get(); + const store = writable(init_value, (set) => { // If the value has changed before we call subscribe, then // we need to treat the value as already having run - let ran = init_value !== get(); + var ran = init_value !== get(); // TODO do we need a different implementation on the server? - const teardown = effect_root(() => { - render_effect(() => { - const value = get(); - if (ran) set(value); + var teardown; + // Apply the reaction and effect at the time of toStore being called + var previous_reaction = active_reaction; + var previous_effect = active_effect; + set_active_reaction(reaction); + set_active_effect(effect); + + try { + teardown = effect_root(() => { + render_effect(() => { + const value = get(); + if (ran) set(value); + }); }); - }); + } finally { + set_active_reaction(previous_reaction); + set_active_effect(previous_effect); + } ran = true; diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/_config.js b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/_config.js new file mode 100644 index 0000000000..bc1793e7a4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/_config.js @@ -0,0 +1,16 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + let btn = target.querySelector('button'); + + btn?.click(); + flushSync(); + + assert.htmlEqual( + target.innerHTML, + `<div>Count 1!</div><div>Count from store 1!</div><button>Add 1</button>` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/main.svelte b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/main.svelte new file mode 100644 index 0000000000..82d20105b8 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/toStore-subscribe2/main.svelte @@ -0,0 +1,11 @@ +<script> + import { toStore } from "svelte/store"; + + let counter = $state(0); + const count = toStore(() => counter, value => counter = value); +</script> + +<div>Count {counter}!</div> +<div>Count from store {$count}!</div> + +<button onclick={() => counter+=1}>Add 1</button> From 842a7c6995f94f46b1839fcac91042fd541e52ca Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 21 Mar 2025 10:26:15 -0400 Subject: [PATCH 0279/1429] docs: update state_unsafe_mutation message (#15539) * docs: update state_unsafe_mutation message * regenerate * fix example --- .../98-reference/.generated/client-errors.md | 35 +++++++++++-------- .../svelte/messages/client-errors/errors.md | 35 +++++++++++-------- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index 62d9c3302a..901c49822c 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -128,26 +128,31 @@ Cannot set prototype of `$state` object Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state` ``` -This error is thrown in a situation like this: +This error occurs when state is updated while evaluating a `$derived`. You might encounter it while trying to 'derive' two pieces of state in one go: ```svelte <script> - let count = $state(0); - let multiple = $derived.by(() => { - const result = count * 2; - if (result > 10) { - count = 0; - } - return result; - }); + let count = $state(0); + + let even = $state(true); + + let odd = $derived.by(() => { + even = count % 2 === 0; + return !even; + }); </script> -<button onclick={() => count++}>{count} / {multiple}</button> +<button onclick={() => count++}>{count}</button> + +<p>{count} is even: {even}</p> +<p>{count} is odd: {odd}</p> ``` -Here, the `$derived` updates `count`, which is `$state` and therefore forbidden to do. It is forbidden because the reactive graph could become unstable as a result, leading to subtle bugs, like values being stale or effects firing in the wrong order. To prevent this, Svelte errors when detecting an update to a `$state` variable. +This is forbidden because it introduces instability: if `<p>{count} is even: {even}</p>` is updated before `odd` is recalculated, `even` will be stale. In most cases the solution is to make everything derived: + +```js +let even = $derived(count % 2 === 0); +let odd = $derived(!even); +``` -To fix this: -- See if it's possible to refactor your `$derived` such that the update becomes unnecessary -- Think about why you need to update `$state` inside a `$derived` in the first place. Maybe it's because you're using `bind:`, which leads you down a bad code path, and separating input and output path (by splitting it up to an attribute and an event, or by using [Function bindings](bind#Function-bindings)) makes it possible avoid the update -- If it's unavoidable, you may need to use an [`$effect`]($effect) instead. This could include splitting parts of the `$derived` into an [`$effect`]($effect) which does the updates +If side-effects are unavoidable, use [`$effect`]($effect) instead. diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index bc8ec36256..572930843e 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -84,26 +84,31 @@ See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-long > Updating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without `$state` -This error is thrown in a situation like this: +This error occurs when state is updated while evaluating a `$derived`. You might encounter it while trying to 'derive' two pieces of state in one go: ```svelte <script> - let count = $state(0); - let multiple = $derived.by(() => { - const result = count * 2; - if (result > 10) { - count = 0; - } - return result; - }); + let count = $state(0); + + let even = $state(true); + + let odd = $derived.by(() => { + even = count % 2 === 0; + return !even; + }); </script> -<button onclick={() => count++}>{count} / {multiple}</button> +<button onclick={() => count++}>{count}</button> + +<p>{count} is even: {even}</p> +<p>{count} is odd: {odd}</p> ``` -Here, the `$derived` updates `count`, which is `$state` and therefore forbidden to do. It is forbidden because the reactive graph could become unstable as a result, leading to subtle bugs, like values being stale or effects firing in the wrong order. To prevent this, Svelte errors when detecting an update to a `$state` variable. +This is forbidden because it introduces instability: if `<p>{count} is even: {even}</p>` is updated before `odd` is recalculated, `even` will be stale. In most cases the solution is to make everything derived: + +```js +let even = $derived(count % 2 === 0); +let odd = $derived(!even); +``` -To fix this: -- See if it's possible to refactor your `$derived` such that the update becomes unnecessary -- Think about why you need to update `$state` inside a `$derived` in the first place. Maybe it's because you're using `bind:`, which leads you down a bad code path, and separating input and output path (by splitting it up to an attribute and an event, or by using [Function bindings](bind#Function-bindings)) makes it possible avoid the update -- If it's unavoidable, you may need to use an [`$effect`]($effect) instead. This could include splitting parts of the `$derived` into an [`$effect`]($effect) which does the updates +If side-effects are unavoidable, use [`$effect`]($effect) instead. From 2d3b65dfbd589416d94661bd34ed7a99896bcde2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 10:28:23 -0400 Subject: [PATCH 0280/1429] Version Packages (#15575) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/new-cherries-leave.md | 5 ----- .changeset/twelve-bananas-destroy.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/new-cherries-leave.md delete mode 100644 .changeset/twelve-bananas-destroy.md diff --git a/.changeset/new-cherries-leave.md b/.changeset/new-cherries-leave.md deleted file mode 100644 index 738a78b4a3..0000000000 --- a/.changeset/new-cherries-leave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: use `get` in constructor for deriveds diff --git a/.changeset/twelve-bananas-destroy.md b/.changeset/twelve-bananas-destroy.md deleted file mode 100644 index 873ee21877..0000000000 --- a/.changeset/twelve-bananas-destroy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure toStore root effect is connected to correct parent effect diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 8cb7efd4ef..04ddfcadbd 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.24.1 + +### Patch Changes + +- fix: use `get` in constructor for deriveds ([#15300](https://github.com/sveltejs/svelte/pull/15300)) + +- fix: ensure toStore root effect is connected to correct parent effect ([#15574](https://github.com/sveltejs/svelte/pull/15574)) + ## 5.24.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 0aa6b29841..f321571e7a 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.24.0", + "version": "5.24.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 7cd43e74cb..565c190713 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.24.0'; +export const VERSION = '5.24.1'; export const PUBLIC_VERSION = '5'; From 5a8fa69dbf46e99beed812157ed78609f8054331 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 21 Mar 2025 12:54:53 -0400 Subject: [PATCH 0281/1429] feat: make deriveds writable (#15570) * feat: make deriveds writable * add optimistic UI example * add note to when-not-to-use-effect * add section on deep reactivity * root-relative URL * use hash URL * mention const * make handler async, move into script block --- .changeset/clever-terms-tell.md | 5 ++ documentation/docs/02-runes/03-$derived.md | 42 +++++++++++++++++ documentation/docs/02-runes/04-$effect.md | 2 + .../phases/2-analyze/visitors/shared/utils.js | 31 +------------ .../runes-no-derived-assignment/_config.js | 8 ---- .../runes-no-derived-assignment/main.svelte | 5 -- .../runes-no-derived-binding/_config.js | 8 ---- .../runes-no-derived-binding/main.svelte | 6 --- .../_config.js | 8 ---- .../main.svelte | 10 ---- .../_config.js | 8 ---- .../main.svelte | 10 ---- .../runes-no-derived-update/_config.js | 8 ---- .../runes-no-derived-update/main.svelte | 5 -- .../samples/writable-derived/_config.js | 46 +++++++++++++++++++ .../samples/writable-derived/main.svelte | 9 ++++ .../reassign-derived-literal/errors.json | 14 ------ .../reassign-derived-literal/input.svelte | 9 ---- .../errors.json | 14 ------ .../input.svelte | 9 ---- .../reassign-derived-public-field/errors.json | 14 ------ .../input.svelte | 9 ---- 22 files changed, 105 insertions(+), 175 deletions(-) create mode 100644 .changeset/clever-terms-tell.md delete mode 100644 packages/svelte/tests/compiler-errors/samples/runes-no-derived-assignment/_config.js delete mode 100644 packages/svelte/tests/compiler-errors/samples/runes-no-derived-assignment/main.svelte delete mode 100644 packages/svelte/tests/compiler-errors/samples/runes-no-derived-binding/_config.js delete mode 100644 packages/svelte/tests/compiler-errors/samples/runes-no-derived-binding/main.svelte delete mode 100644 packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-assignment/_config.js delete mode 100644 packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-assignment/main.svelte delete mode 100644 packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-update/_config.js delete mode 100644 packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-update/main.svelte delete mode 100644 packages/svelte/tests/compiler-errors/samples/runes-no-derived-update/_config.js delete mode 100644 packages/svelte/tests/compiler-errors/samples/runes-no-derived-update/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/writable-derived/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/writable-derived/main.svelte delete mode 100644 packages/svelte/tests/validator/samples/reassign-derived-literal/errors.json delete mode 100644 packages/svelte/tests/validator/samples/reassign-derived-literal/input.svelte delete mode 100644 packages/svelte/tests/validator/samples/reassign-derived-private-field/errors.json delete mode 100644 packages/svelte/tests/validator/samples/reassign-derived-private-field/input.svelte delete mode 100644 packages/svelte/tests/validator/samples/reassign-derived-public-field/errors.json delete mode 100644 packages/svelte/tests/validator/samples/reassign-derived-public-field/input.svelte diff --git a/.changeset/clever-terms-tell.md b/.changeset/clever-terms-tell.md new file mode 100644 index 0000000000..606868bce3 --- /dev/null +++ b/.changeset/clever-terms-tell.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: make deriveds writable diff --git a/documentation/docs/02-runes/03-$derived.md b/documentation/docs/02-runes/03-$derived.md index 24ab643b68..2464aa9295 100644 --- a/documentation/docs/02-runes/03-$derived.md +++ b/documentation/docs/02-runes/03-$derived.md @@ -52,6 +52,48 @@ Anything read synchronously inside the `$derived` expression (or `$derived.by` f To exempt a piece of state from being treated as a dependency, use [`untrack`](svelte#untrack). +## Overriding derived values + +Derived expressions are recalculated when their dependencies change, but you can temporarily override their values by reassigning them (unless they are declared with `const`). This can be useful for things like _optimistic UI_, where a value is derived from the 'source of truth' (such as data from your server) but you'd like to show immediate feedback to the user: + +```svelte +<script> + let { post, like } = $props(); + + let likes = $derived(post.likes); + + async function onclick() { + // increment the `likes` count immediately... + likes += 1; + + // and tell the server, which will eventually update `post` + try { + await like(); + } catch { + // failed! roll back the change + likes -= 1; + } + } +</script> + +<button {onclick}>🧡 {likes}</button> +``` + +> [!NOTE] Prior to Svelte 5.25, deriveds were read-only. + +## Deriveds and reactivity + +Unlike `$state`, which converts objects and arrays to [deeply reactive proxies]($state#Deep-state), `$derived` values are left as-is. For example, [in a case like this](/playground/untitled#H4sIAAAAAAAAE4VU22rjMBD9lUHd3aaQi9PdstS1A3t5XvpQ2Ic4D7I1iUUV2UjjNMX431eS7TRdSosxgjMzZ45mjt0yzffIYibvy0ojFJWqDKCQVBk2ZVup0LJ43TJ6rn2aBxw-FP2o67k9oCKP5dziW3hRaUJNjoYltjCyplWmM1JIIAn3FlL4ZIkTTtYez6jtj4w8WwyXv9GiIXiQxLVs9pfTMR7EuoSLIuLFbX7Z4930bZo_nBrD1bs834tlfvsBz9_SyX6PZXu9XaL4gOWn4sXjeyzftv4ZWfyxubpzxzg6LfD4MrooxELEosKCUPigQCMPKCZh0OtQE1iSxcsmdHuBvCiHZXALLXiN08EL3RRkaJ_kDVGle0HcSD5TPEeVtj67O4Nrg9aiSNtBY5oODJkrL5QsHtN2cgXp6nSJMWzpWWGasdlsGEMbzi5jPr5KFr0Ep7pdeM2-TCelCddIhDxAobi1jqF3cMaC1RKp64bAW9iFAmXGIHfd4wNXDabtOLN53w8W53VvJoZLh7xk4Rr3CoL-UNoLhWHrT1JQGcM17u96oES5K-kc2XOzkzqGCKL5De79OUTyyrg1zgwXsrEx3ESfx4Bz0M5UjVMHB24mw9SuXtXFoN13fYKOM1tyUT3FbvbWmSWCZX2Er-41u5xPoml45svRahl9Wb9aasbINJixDZwcPTbyTLZSUsAvrg_cPuCR7s782_WU8343Y72Qtlb8OYatwuOQvuN13M_hJKNfxann1v1U_B1KZ_D_mzhzhz24fw85CSz2irtN9w9HshBK7AQAAA==)... + +```svelte +let items = $state([...]); + +let index = $state(0); +let selected = $derived(items[index]); +``` + +...you can change (or `bind:` to) properties of `selected` and it will affect the underlying `items` array. If `items` was _not_ deeply reactive, mutating `selected` would have no effect. + ## Update propagation Svelte uses something called _push-pull reactivity_ — when state is updated, everything that depends on the state (whether directly or indirectly) is immediately notified of the change (the 'push'), but derived values are not re-evaluated until they are actually read (the 'pull'). diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md index 75f59102f9..ae1a2146c9 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -254,6 +254,8 @@ In general, `$effect` is best considered something of an escape hatch — useful > [!NOTE] For things that are more complicated than a simple expression like `count * 2`, you can also use `$derived.by`. +If you're using an effect because you want to be able to reassign the derived value (to build an optimistic UI, for example) note that [deriveds can be directly overridden]($derived#Overriding-derived-values) as of Svelte 5.25. + You might be tempted to do something convoluted with effects to link one value to another. The following example shows two inputs for "money spent" and "money left" that are connected to each other. If you update one, the other should update accordingly. Don't use effects for this ([demo](/playground/untitled#H4sIAAAAAAAACpVRy26DMBD8FcvKgUhtoIdeHBwp31F6MGSJkBbHwksEQvx77aWQqooq9bgzOzP7mGTdIHipPiZJowOpGJAv0po2VmfnDv4OSBErjYdneHWzBJaCjcx91TWOToUtCIEE3cig0OIty44r5l1oDtjOkyFIsv3GINQ_CNYyGegd1DVUlCR7oU9iilDUcP8S8roYs9n8p2wdYNVFm4csTx872BxNCcjr5I11fdgonEkXsjP2CoUUZWMv6m6wBz2x7yxaM-iJvWeRsvSbSVeUy5i0uf8vKA78NIeJLSZWv1I8jQjLdyK4XuTSeIdmVKJGGI4LdjVOiezwDu1yG74My8PLCQaSiroe5s_5C2PHrkVGAgAA)): ```svelte diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js index 04f4347a40..d6c74eddb6 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js @@ -21,10 +21,6 @@ export function validate_assignment(node, argument, state) { const binding = state.scope.get(argument.name); if (state.analysis.runes) { - if (binding?.kind === 'derived') { - e.constant_assignment(node, 'derived state'); - } - if (binding?.node === state.analysis.props_id) { e.constant_assignment(node, '$props.id()'); } @@ -38,25 +34,6 @@ export function validate_assignment(node, argument, state) { e.snippet_parameter_assignment(node); } } - if ( - argument.type === 'MemberExpression' && - argument.object.type === 'ThisExpression' && - (((argument.property.type === 'PrivateIdentifier' || argument.property.type === 'Identifier') && - state.derived_state.some( - (derived) => - derived.name === /** @type {PrivateIdentifier | Identifier} */ (argument.property).name && - derived.private === (argument.property.type === 'PrivateIdentifier') - )) || - (argument.property.type === 'Literal' && - argument.property.value && - typeof argument.property.value === 'string' && - state.derived_state.some( - (derived) => - derived.name === /** @type {Literal} */ (argument.property).value && !derived.private - ))) - ) { - e.constant_assignment(node, 'derived state'); - } } /** @@ -81,7 +58,6 @@ export function validate_no_const_assignment(node, argument, scope, is_binding) } else if (argument.type === 'Identifier') { const binding = scope.get(argument.name); if ( - binding?.kind === 'derived' || binding?.declaration_kind === 'import' || (binding?.declaration_kind === 'const' && binding.kind !== 'each') ) { @@ -96,12 +72,7 @@ export function validate_no_const_assignment(node, argument, scope, is_binding) // ); // TODO have a more specific error message for assignments to things like `{:then foo}` - const thing = - binding.declaration_kind === 'import' - ? 'import' - : binding.kind === 'derived' - ? 'derived state' - : 'constant'; + const thing = binding.declaration_kind === 'import' ? 'import' : 'constant'; if (is_binding) { e.constant_binding(node, thing); diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-assignment/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-assignment/_config.js deleted file mode 100644 index 94985a9939..0000000000 --- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-assignment/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -import { test } from '../../test'; - -export default test({ - error: { - code: 'constant_assignment', - message: 'Cannot assign to derived state' - } -}); diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-assignment/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-assignment/main.svelte deleted file mode 100644 index 3bf836f6c5..0000000000 --- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-assignment/main.svelte +++ /dev/null @@ -1,5 +0,0 @@ -<script> - const a = $state(0); - let b = $derived(a); - b = 1; -</script> diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-binding/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-binding/_config.js deleted file mode 100644 index 87b88d79cc..0000000000 --- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-binding/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -import { test } from '../../test'; - -export default test({ - error: { - code: 'constant_binding', - message: 'Cannot bind to derived state' - } -}); diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-binding/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-binding/main.svelte deleted file mode 100644 index 6c198dc068..0000000000 --- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-binding/main.svelte +++ /dev/null @@ -1,6 +0,0 @@ -<script> - let a = $state(0); - let b = $derived({ a }); -</script> - -<input type="number" bind:value={b} /> diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-assignment/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-assignment/_config.js deleted file mode 100644 index 94985a9939..0000000000 --- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-assignment/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -import { test } from '../../test'; - -export default test({ - error: { - code: 'constant_assignment', - message: 'Cannot assign to derived state' - } -}); diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-assignment/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-assignment/main.svelte deleted file mode 100644 index d44806757e..0000000000 --- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-assignment/main.svelte +++ /dev/null @@ -1,10 +0,0 @@ -<script> - class Counter { - count = $state(); - #doubled = $derived(this.count * 2); - - nope() { - this.#doubled += 1; - } - } -</script> diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-update/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-update/_config.js deleted file mode 100644 index 94985a9939..0000000000 --- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-update/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -import { test } from '../../test'; - -export default test({ - error: { - code: 'constant_assignment', - message: 'Cannot assign to derived state' - } -}); diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-update/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-update/main.svelte deleted file mode 100644 index e4ee2e8635..0000000000 --- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-state-field-update/main.svelte +++ /dev/null @@ -1,10 +0,0 @@ -<script> - class Counter { - count = $state(); - #doubled = $derived(this.count * 2); - - nope() { - this.#doubled++; - } - } -</script> diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-update/_config.js b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-update/_config.js deleted file mode 100644 index 94985a9939..0000000000 --- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-update/_config.js +++ /dev/null @@ -1,8 +0,0 @@ -import { test } from '../../test'; - -export default test({ - error: { - code: 'constant_assignment', - message: 'Cannot assign to derived state' - } -}); diff --git a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-update/main.svelte b/packages/svelte/tests/compiler-errors/samples/runes-no-derived-update/main.svelte deleted file mode 100644 index d266c95bb8..0000000000 --- a/packages/svelte/tests/compiler-errors/samples/runes-no-derived-update/main.svelte +++ /dev/null @@ -1,5 +0,0 @@ -<script> - const a = $state(0); - let b = $derived(a); - b++; -</script> diff --git a/packages/svelte/tests/runtime-runes/samples/writable-derived/_config.js b/packages/svelte/tests/runtime-runes/samples/writable-derived/_config.js new file mode 100644 index 0000000000..b48ccbdfd0 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/writable-derived/_config.js @@ -0,0 +1,46 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: ` + <input type="range"><input type="range"><p>0 * 2 = 0</p> + `, + + ssrHtml: ` + <input type="range" value="0"><input type="range" value="0"><p>0 * 2 = 0</p> + `, + + test({ assert, target, window }) { + const [input1, input2] = target.querySelectorAll('input'); + + flushSync(() => { + input1.value = '10'; + input1.dispatchEvent(new window.Event('input', { bubbles: true })); + }); + + assert.htmlEqual( + target.innerHTML, + `<input type="range"><input type="range"><p>10 * 2 = 20</p>` + ); + + flushSync(() => { + input2.value = '99'; + input2.dispatchEvent(new window.Event('input', { bubbles: true })); + }); + + assert.htmlEqual( + target.innerHTML, + `<input type="range"><input type="range"><p>10 * 2 = 99</p>` + ); + + flushSync(() => { + input1.value = '20'; + input1.dispatchEvent(new window.Event('input', { bubbles: true })); + }); + + assert.htmlEqual( + target.innerHTML, + `<input type="range"><input type="range"><p>20 * 2 = 40</p>` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/writable-derived/main.svelte b/packages/svelte/tests/runtime-runes/samples/writable-derived/main.svelte new file mode 100644 index 0000000000..ab1dde0b9b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/writable-derived/main.svelte @@ -0,0 +1,9 @@ +<script> + let count = $state(0); + let double = $derived(count * 2); +</script> + +<input type="range" bind:value={count} /> +<input type="range" bind:value={double} /> + +<p>{count} * 2 = {double}</p> diff --git a/packages/svelte/tests/validator/samples/reassign-derived-literal/errors.json b/packages/svelte/tests/validator/samples/reassign-derived-literal/errors.json deleted file mode 100644 index 8681d84ab2..0000000000 --- a/packages/svelte/tests/validator/samples/reassign-derived-literal/errors.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "code": "constant_assignment", - "message": "Cannot assign to derived state", - "start": { - "column": 3, - "line": 6 - }, - "end": { - "column": 29, - "line": 6 - } - } -] diff --git a/packages/svelte/tests/validator/samples/reassign-derived-literal/input.svelte b/packages/svelte/tests/validator/samples/reassign-derived-literal/input.svelte deleted file mode 100644 index 8f109c9e1f..0000000000 --- a/packages/svelte/tests/validator/samples/reassign-derived-literal/input.svelte +++ /dev/null @@ -1,9 +0,0 @@ -<script> - class Test{ - der = $derived({ test: 0 }); - - set test(v){ - this["der"] = { test: 45 }; - } - } -</script> \ No newline at end of file diff --git a/packages/svelte/tests/validator/samples/reassign-derived-private-field/errors.json b/packages/svelte/tests/validator/samples/reassign-derived-private-field/errors.json deleted file mode 100644 index c211aa4608..0000000000 --- a/packages/svelte/tests/validator/samples/reassign-derived-private-field/errors.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "code": "constant_assignment", - "message": "Cannot assign to derived state", - "start": { - "column": 3, - "line": 6 - }, - "end": { - "column": 27, - "line": 6 - } - } -] diff --git a/packages/svelte/tests/validator/samples/reassign-derived-private-field/input.svelte b/packages/svelte/tests/validator/samples/reassign-derived-private-field/input.svelte deleted file mode 100644 index 62e2317e03..0000000000 --- a/packages/svelte/tests/validator/samples/reassign-derived-private-field/input.svelte +++ /dev/null @@ -1,9 +0,0 @@ -<script> - class Test{ - #der = $derived({ test: 0 }); - - set test(v){ - this.#der = { test: 45 }; - } - } -</script> \ No newline at end of file diff --git a/packages/svelte/tests/validator/samples/reassign-derived-public-field/errors.json b/packages/svelte/tests/validator/samples/reassign-derived-public-field/errors.json deleted file mode 100644 index 98837589ac..0000000000 --- a/packages/svelte/tests/validator/samples/reassign-derived-public-field/errors.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "code": "constant_assignment", - "message": "Cannot assign to derived state", - "start": { - "column": 3, - "line": 6 - }, - "end": { - "column": 26, - "line": 6 - } - } -] diff --git a/packages/svelte/tests/validator/samples/reassign-derived-public-field/input.svelte b/packages/svelte/tests/validator/samples/reassign-derived-public-field/input.svelte deleted file mode 100644 index e2c4693e86..0000000000 --- a/packages/svelte/tests/validator/samples/reassign-derived-public-field/input.svelte +++ /dev/null @@ -1,9 +0,0 @@ -<script> - class Test{ - der = $derived({ test: 0 }); - - set test(v){ - this.der = { test: 45 }; - } - } -</script> \ No newline at end of file From 6e343b9ad7bca473947cbee0c7ea9455d9485599 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 13:01:58 -0400 Subject: [PATCH 0282/1429] Version Packages (#15578) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/clever-terms-tell.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/clever-terms-tell.md diff --git a/.changeset/clever-terms-tell.md b/.changeset/clever-terms-tell.md deleted file mode 100644 index 606868bce3..0000000000 --- a/.changeset/clever-terms-tell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: make deriveds writable diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 04ddfcadbd..4bac129169 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.25.0 + +### Minor Changes + +- feat: make deriveds writable ([#15570](https://github.com/sveltejs/svelte/pull/15570)) + ## 5.24.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index f321571e7a..e3824b89fb 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.24.1", + "version": "5.25.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 565c190713..a62190bb2e 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.24.1'; +export const VERSION = '5.25.0'; export const PUBLIC_VERSION = '5'; From 441108b8ff28a6c1aa8e38f5c041a2583446167e Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 21 Mar 2025 13:06:46 -0400 Subject: [PATCH 0283/1429] fix docs --- documentation/docs/98-reference/.generated/client-errors.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index 901c49822c..fd9419176d 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -151,6 +151,8 @@ This error occurs when state is updated while evaluating a `$derived`. You might This is forbidden because it introduces instability: if `<p>{count} is even: {even}</p>` is updated before `odd` is recalculated, `even` will be stale. In most cases the solution is to make everything derived: ```js +let count = 0; +// ---cut--- let even = $derived(count % 2 === 0); let odd = $derived(!even); ``` From ef98ccae8b27dbac393623c166ea890b515d5e1d Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 21 Mar 2025 15:44:23 -0400 Subject: [PATCH 0284/1429] doh --- documentation/docs/98-reference/.generated/client-errors.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index fd9419176d..901c49822c 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -151,8 +151,6 @@ This error occurs when state is updated while evaluating a `$derived`. You might This is forbidden because it introduces instability: if `<p>{count} is even: {even}</p>` is updated before `odd` is recalculated, `even` will be stale. In most cases the solution is to make everything derived: ```js -let count = 0; -// ---cut--- let even = $derived(count % 2 === 0); let odd = $derived(!even); ``` From d1bd32ec9ec06e6505740a0de5f5b2281546787c Mon Sep 17 00:00:00 2001 From: Blade Barringer <blade@crookedneighbor.com> Date: Fri, 21 Mar 2025 14:46:20 -0500 Subject: [PATCH 0285/1429] fix: allow get_proxied_value to return original value when error (#15577) * fix: allow get_proxied_value to return original value when error closes #15546 * Update packages/svelte/src/internal/client/proxy.js --------- Co-authored-by: Rich Harris <hello@rich-harris.dev> --- .changeset/afraid-penguins-battle.md | 5 +++++ packages/svelte/src/internal/client/proxy.js | 14 ++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 .changeset/afraid-penguins-battle.md diff --git a/.changeset/afraid-penguins-battle.md b/.changeset/afraid-penguins-battle.md new file mode 100644 index 0000000000..2cc5059b9a --- /dev/null +++ b/.changeset/afraid-penguins-battle.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent dev server from throwing errors when attempting to retrieve the proxied value of an iframe's contentWindow diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index ffe63f4b77..fab271c916 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -366,8 +366,18 @@ function update_version(signal, d = 1) { * @param {any} value */ export function get_proxied_value(value) { - if (value !== null && typeof value === 'object' && STATE_SYMBOL in value) { - return value[STATE_SYMBOL]; + try { + if (value !== null && typeof value === 'object' && STATE_SYMBOL in value) { + return value[STATE_SYMBOL]; + } + } catch { + // the above if check can throw an error if the value in question + // is the contentWindow of an iframe on another domain, in which + // case we want to just return the value (because it's definitely + // not a proxied value) so we don't break any JavaScript interacting + // with that iframe (such as various payment companies client side + // JavaScript libraries interacting with their iframes on the same + // domain) } return value; From c1ae8953aaa81b9191d8d944c4bf0df7fdf4f2ee Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 17:04:59 -0400 Subject: [PATCH 0286/1429] Version Packages (#15580) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/afraid-penguins-battle.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/afraid-penguins-battle.md diff --git a/.changeset/afraid-penguins-battle.md b/.changeset/afraid-penguins-battle.md deleted file mode 100644 index 2cc5059b9a..0000000000 --- a/.changeset/afraid-penguins-battle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: prevent dev server from throwing errors when attempting to retrieve the proxied value of an iframe's contentWindow diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 4bac129169..9e99e91b8e 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.25.1 + +### Patch Changes + +- fix: prevent dev server from throwing errors when attempting to retrieve the proxied value of an iframe's contentWindow ([#15577](https://github.com/sveltejs/svelte/pull/15577)) + ## 5.25.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index e3824b89fb..9d3902696d 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.25.0", + "version": "5.25.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index a62190bb2e..a4f5a15c8f 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.25.0'; +export const VERSION = '5.25.1'; export const PUBLIC_VERSION = '5'; From 33d118f8a29a376e4490f2d31b0b444bf8fa0c7c Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Fri, 21 Mar 2025 23:47:26 +0100 Subject: [PATCH 0287/1429] feat: migrate reassigned deriveds to `$derived` (#15581) --- .changeset/forty-snakes-lay.md | 5 +++++ packages/svelte/src/compiler/migrate/index.js | 17 ++++++++++++++++- .../samples/reassigned-deriveds/input.svelte | 10 ++++++++++ .../samples/reassigned-deriveds/output.svelte | 10 ++++++++++ 4 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 .changeset/forty-snakes-lay.md create mode 100644 packages/svelte/tests/migrate/samples/reassigned-deriveds/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/reassigned-deriveds/output.svelte diff --git a/.changeset/forty-snakes-lay.md b/.changeset/forty-snakes-lay.md new file mode 100644 index 0000000000..6cb4c2d761 --- /dev/null +++ b/.changeset/forty-snakes-lay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +feat: migrate reassigned deriveds to `$derived` diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 7f26d0d010..b336ebb2b8 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -19,6 +19,7 @@ import { migrate_svelte_ignore } from '../utils/extract_svelte_ignore.js'; import { validate_component_options } from '../validate-options.js'; import { is_reserved, is_svg, is_void } from '../../utils.js'; import { regex_is_valid_identifier } from '../phases/patterns.js'; +import { VERSION } from 'svelte/compiler'; const regex_style_tags = /(<style[^>]+>)([\S\s]*?)(<\/style>)/g; const style_placeholder = '/*$$__STYLE_CONTENT__$$*/'; @@ -113,6 +114,16 @@ function find_closing_parenthesis(start, code) { return end; } +function check_support_writable_deriveds() { + const [major, minor, patch] = VERSION.split('.'); + + if (+major < 5) return false; + if (+minor < 25) return false; + return true; +} + +const support_writable_derived = check_support_writable_deriveds(); + /** * Does a best-effort migration of Svelte code towards using runes, event attributes and render tags. * May throw an error if the code is too complex to migrate automatically. @@ -952,7 +963,11 @@ const instance_script = { const reassigned_bindings = bindings.filter((b) => b?.reassigned); if ( - reassigned_bindings.length === 0 && + // on version 5.25.0 deriveds are writable so we can use them even if + // reassigned (but if the right side is a literal we want to use `$state`) + (support_writable_derived + ? node.body.expression.right.type !== 'Literal' + : reassigned_bindings.length === 0) && !bindings.some((b) => b?.kind === 'store_sub') && node.body.expression.left.type !== 'MemberExpression' ) { diff --git a/packages/svelte/tests/migrate/samples/reassigned-deriveds/input.svelte b/packages/svelte/tests/migrate/samples/reassigned-deriveds/input.svelte new file mode 100644 index 0000000000..024f719fb9 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/reassigned-deriveds/input.svelte @@ -0,0 +1,10 @@ +<script> + let name = 'world'; + + $: upper = name.toUpperCase(); +</script> + +<input bind:value={name} /> +<input bind:value={upper} /> + +{upper} \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/reassigned-deriveds/output.svelte b/packages/svelte/tests/migrate/samples/reassigned-deriveds/output.svelte new file mode 100644 index 0000000000..0903299d95 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/reassigned-deriveds/output.svelte @@ -0,0 +1,10 @@ +<script> + let name = $state('world'); + + let upper = $derived(name.toUpperCase()); +</script> + +<input bind:value={name} /> +<input bind:value={upper} /> + +{upper} \ No newline at end of file From 78d238c5a34396afcc9edf02bb767d0936440ad9 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Sat, 22 Mar 2025 00:03:38 +0100 Subject: [PATCH 0288/1429] chore: revert version check in migrate (#15583) --- packages/svelte/src/compiler/migrate/index.js | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index b336ebb2b8..9d79d88b23 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -19,7 +19,6 @@ import { migrate_svelte_ignore } from '../utils/extract_svelte_ignore.js'; import { validate_component_options } from '../validate-options.js'; import { is_reserved, is_svg, is_void } from '../../utils.js'; import { regex_is_valid_identifier } from '../phases/patterns.js'; -import { VERSION } from 'svelte/compiler'; const regex_style_tags = /(<style[^>]+>)([\S\s]*?)(<\/style>)/g; const style_placeholder = '/*$$__STYLE_CONTENT__$$*/'; @@ -114,16 +113,6 @@ function find_closing_parenthesis(start, code) { return end; } -function check_support_writable_deriveds() { - const [major, minor, patch] = VERSION.split('.'); - - if (+major < 5) return false; - if (+minor < 25) return false; - return true; -} - -const support_writable_derived = check_support_writable_deriveds(); - /** * Does a best-effort migration of Svelte code towards using runes, event attributes and render tags. * May throw an error if the code is too complex to migrate automatically. @@ -963,11 +952,7 @@ const instance_script = { const reassigned_bindings = bindings.filter((b) => b?.reassigned); if ( - // on version 5.25.0 deriveds are writable so we can use them even if - // reassigned (but if the right side is a literal we want to use `$state`) - (support_writable_derived - ? node.body.expression.right.type !== 'Literal' - : reassigned_bindings.length === 0) && + node.body.expression.right.type !== 'Literal' && !bindings.some((b) => b?.kind === 'store_sub') && node.body.expression.left.type !== 'MemberExpression' ) { From 7fe9bf524bbf060a0acf8b91c8ca322b105423b4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 22 Mar 2025 00:04:01 +0100 Subject: [PATCH 0289/1429] Version Packages (#15582) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/forty-snakes-lay.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/forty-snakes-lay.md diff --git a/.changeset/forty-snakes-lay.md b/.changeset/forty-snakes-lay.md deleted file mode 100644 index 6cb4c2d761..0000000000 --- a/.changeset/forty-snakes-lay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -feat: migrate reassigned deriveds to `$derived` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 9e99e91b8e..86e1179029 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.25.2 + +### Patch Changes + +- feat: migrate reassigned deriveds to `$derived` ([#15581](https://github.com/sveltejs/svelte/pull/15581)) + ## 5.25.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 9d3902696d..56fa949391 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.25.1", + "version": "5.25.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index a4f5a15c8f..e2dc6c2c09 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.25.1'; +export const VERSION = '5.25.2'; export const PUBLIC_VERSION = '5'; From 3080c1334e8efc65756488c0cc36b6dec5fca00f Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Sat, 22 Mar 2025 01:56:31 +0100 Subject: [PATCH 0290/1429] fix: prevent state runes from being called with spread (#15585) * fix: prevent state runes from being called with spread * prevent spread arguments for all runes except $inspect --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/nice-pianos-punch.md | 5 +++++ .../docs/98-reference/.generated/compile-errors.md | 6 ++++++ packages/svelte/messages/compile-errors/script.md | 4 ++++ packages/svelte/src/compiler/errors.js | 10 ++++++++++ .../phases/2-analyze/visitors/CallExpression.js | 8 ++++++++ .../rune-invalid-spread-derived-by/errors.json | 14 ++++++++++++++ .../rune-invalid-spread-derived-by/input.svelte | 4 ++++ .../rune-invalid-spread-derived/errors.json | 14 ++++++++++++++ .../rune-invalid-spread-derived/input.svelte | 4 ++++ .../rune-invalid-spread-state-raw/errors.json | 14 ++++++++++++++ .../rune-invalid-spread-state-raw/input.svelte | 4 ++++ .../samples/rune-invalid-spread-state/errors.json | 14 ++++++++++++++ .../samples/rune-invalid-spread-state/input.svelte | 4 ++++ 13 files changed, 105 insertions(+) create mode 100644 .changeset/nice-pianos-punch.md create mode 100644 packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/errors.json create mode 100644 packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/input.svelte create mode 100644 packages/svelte/tests/validator/samples/rune-invalid-spread-derived/errors.json create mode 100644 packages/svelte/tests/validator/samples/rune-invalid-spread-derived/input.svelte create mode 100644 packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/errors.json create mode 100644 packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/input.svelte create mode 100644 packages/svelte/tests/validator/samples/rune-invalid-spread-state/errors.json create mode 100644 packages/svelte/tests/validator/samples/rune-invalid-spread-state/input.svelte diff --git a/.changeset/nice-pianos-punch.md b/.changeset/nice-pianos-punch.md new file mode 100644 index 0000000000..f70af9eb04 --- /dev/null +++ b/.changeset/nice-pianos-punch.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent state runes from being called with spread diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index ea116014e7..a8c39aaf97 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -660,6 +660,12 @@ Cannot access a computed property of a rune `%name%` is not a valid rune ``` +### rune_invalid_spread + +``` +`%rune%` cannot be called with a spread argument +``` + ### rune_invalid_usage ``` diff --git a/packages/svelte/messages/compile-errors/script.md b/packages/svelte/messages/compile-errors/script.md index 795c0b007d..aabcbeae48 100644 --- a/packages/svelte/messages/compile-errors/script.md +++ b/packages/svelte/messages/compile-errors/script.md @@ -162,6 +162,10 @@ This turned out to be buggy and unpredictable, particularly when working with de > `%name%` is not a valid rune +## rune_invalid_spread + +> `%rune%` cannot be called with a spread argument + ## rune_invalid_usage > Cannot use `%rune%` rune in non-runes mode diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index 677b99fcff..6bf973948b 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -383,6 +383,16 @@ export function rune_invalid_name(node, name) { e(node, 'rune_invalid_name', `\`${name}\` is not a valid rune\nhttps://svelte.dev/e/rune_invalid_name`); } +/** + * `%rune%` cannot be called with a spread argument + * @param {null | number | NodeLike} node + * @param {string} rune + * @returns {never} + */ +export function rune_invalid_spread(node, rune) { + e(node, 'rune_invalid_spread', `\`${rune}\` cannot be called with a spread argument\nhttps://svelte.dev/e/rune_invalid_spread`); +} + /** * Cannot use `%rune%` rune in non-runes mode * @param {null | number | NodeLike} node diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index 6ef323725b..2eac934b33 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -17,6 +17,14 @@ export function CallExpression(node, context) { const rune = get_rune(node, context.state.scope); + if (rune && rune !== '$inspect') { + for (const arg of node.arguments) { + if (arg.type === 'SpreadElement') { + e.rune_invalid_spread(node, rune); + } + } + } + switch (rune) { case null: if (!is_safe_identifier(node.callee, context.state.scope)) { diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/errors.json b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/errors.json new file mode 100644 index 0000000000..be59da95fa --- /dev/null +++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "rune_invalid_spread", + "end": { + "column": 35, + "line": 3 + }, + "message": "`$derived.by` cannot be called with a spread argument", + "start": { + "column": 15, + "line": 3 + } + } +] diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/input.svelte b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/input.svelte new file mode 100644 index 0000000000..49e8057aa5 --- /dev/null +++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived-by/input.svelte @@ -0,0 +1,4 @@ +<script> + const args = [0]; + const count = $derived.by(...args); +</script> \ No newline at end of file diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/errors.json b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/errors.json new file mode 100644 index 0000000000..6a333bc362 --- /dev/null +++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "rune_invalid_spread", + "end": { + "column": 32, + "line": 3 + }, + "message": "`$derived` cannot be called with a spread argument", + "start": { + "column": 15, + "line": 3 + } + } +] diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/input.svelte b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/input.svelte new file mode 100644 index 0000000000..9155493e17 --- /dev/null +++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-derived/input.svelte @@ -0,0 +1,4 @@ +<script> + const args = [0]; + const count = $derived(...args); +</script> \ No newline at end of file diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/errors.json b/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/errors.json new file mode 100644 index 0000000000..e08b498fcb --- /dev/null +++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "rune_invalid_spread", + "end": { + "column": 34, + "line": 3 + }, + "message": "`$state.raw` cannot be called with a spread argument", + "start": { + "column": 15, + "line": 3 + } + } +] diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/input.svelte b/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/input.svelte new file mode 100644 index 0000000000..d06fb053b3 --- /dev/null +++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-state-raw/input.svelte @@ -0,0 +1,4 @@ +<script> + const args = [0]; + const count = $state.raw(...args); +</script> \ No newline at end of file diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-state/errors.json b/packages/svelte/tests/validator/samples/rune-invalid-spread-state/errors.json new file mode 100644 index 0000000000..11ae2abce5 --- /dev/null +++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-state/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "rune_invalid_spread", + "end": { + "column": 30, + "line": 3 + }, + "message": "`$state` cannot be called with a spread argument", + "start": { + "column": 15, + "line": 3 + } + } +] diff --git a/packages/svelte/tests/validator/samples/rune-invalid-spread-state/input.svelte b/packages/svelte/tests/validator/samples/rune-invalid-spread-state/input.svelte new file mode 100644 index 0000000000..02feac893f --- /dev/null +++ b/packages/svelte/tests/validator/samples/rune-invalid-spread-state/input.svelte @@ -0,0 +1,4 @@ +<script> + const args = [0]; + const count = $state(...args); +</script> \ No newline at end of file From f498a21063894e6e515e62d753396410624b2e0f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 23 Mar 2025 09:59:21 -0400 Subject: [PATCH 0291/1429] Version Packages (#15587) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/nice-pianos-punch.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/nice-pianos-punch.md diff --git a/.changeset/nice-pianos-punch.md b/.changeset/nice-pianos-punch.md deleted file mode 100644 index f70af9eb04..0000000000 --- a/.changeset/nice-pianos-punch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: prevent state runes from being called with spread diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 86e1179029..e6b5de7e5f 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.25.3 + +### Patch Changes + +- fix: prevent state runes from being called with spread ([#15585](https://github.com/sveltejs/svelte/pull/15585)) + ## 5.25.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 56fa949391..0410520634 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.25.2", + "version": "5.25.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index e2dc6c2c09..e4ae33a9b1 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.25.2'; +export const VERSION = '5.25.3'; export const PUBLIC_VERSION = '5'; From f49856449dbabb0815066485b23bf404c9c789ff Mon Sep 17 00:00:00 2001 From: Scott Wu <sw@scottwu.ca> Date: Fri, 28 Mar 2025 23:53:10 +0800 Subject: [PATCH 0292/1429] docs: add a reference to the official hash router (#15611) --- documentation/docs/07-misc/99-faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/07-misc/99-faq.md b/documentation/docs/07-misc/99-faq.md index 7e25cdab55..ed5c6277c0 100644 --- a/documentation/docs/07-misc/99-faq.md +++ b/documentation/docs/07-misc/99-faq.md @@ -96,7 +96,7 @@ However, you can use any router library. A lot of people use [page.js](https://g If you prefer a declarative HTML approach, there's the isomorphic [svelte-routing](https://github.com/EmilTholin/svelte-routing) library and a fork of it called [svelte-navigator](https://github.com/mefechoel/svelte-navigator) containing some additional functionality. -If you need hash-based routing on the client side, check out [svelte-spa-router](https://github.com/ItalyPaleAle/svelte-spa-router) or [abstract-state-router](https://github.com/TehShrike/abstract-state-router/). +If you need hash-based routing on the client side, check out the [hash option](https://svelte.dev/docs/kit/configuration#router) in SvelteKit, [svelte-spa-router](https://github.com/ItalyPaleAle/svelte-spa-router), or [abstract-state-router](https://github.com/TehShrike/abstract-state-router/). [Routify](https://routify.dev) is another filesystem-based router, similar to SvelteKit's router. Version 3 supports Svelte's native SSR. From 04257925d22d8ecef37f50330ac258c7f97dca0c Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Sat, 29 Mar 2025 01:42:18 -0700 Subject: [PATCH 0293/1429] docs: clarify what you can build with SvelteKit (#15461) * docs: clarify what you can build with SvelteKit * try relative URLs * Update documentation/docs/01-introduction/02-getting-started.md Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- documentation/docs/01-introduction/02-getting-started.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/documentation/docs/01-introduction/02-getting-started.md b/documentation/docs/01-introduction/02-getting-started.md index e035e6d6df..c7351729ff 100644 --- a/documentation/docs/01-introduction/02-getting-started.md +++ b/documentation/docs/01-introduction/02-getting-started.md @@ -2,7 +2,7 @@ title: Getting started --- -We recommend using [SvelteKit](../kit), the official application framework from the Svelte team powered by [Vite](https://vite.dev/): +We recommend using [SvelteKit](../kit), which lets you [build almost anything](../kit/project-types). It's the official application framework from the Svelte team and powered by [Vite](https://vite.dev/). Create a new project with: ```bash npx sv create myapp @@ -15,7 +15,9 @@ Don't worry if you don't know Svelte yet! You can ignore all the nice features S ## Alternatives to SvelteKit -You can also use Svelte directly with Vite by running `npm create vite@latest` and selecting the `svelte` option. With this, `npm run build` will generate HTML, JS and CSS files inside the `dist` directory using [vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte). In most cases, you will probably need to [choose a routing library](faq#Is-there-a-router) as well. +You can also use Svelte directly with Vite by running `npm create vite@latest` and selecting the `svelte` option. With this, `npm run build` will generate HTML, JS, and CSS files inside the `dist` directory using [vite-plugin-svelte](https://github.com/sveltejs/vite-plugin-svelte). In most cases, you will probably need to [choose a routing library](faq#Is-there-a-router) as well. + +>[!NOTE] Vite is often used in standalone mode to build [single page apps (SPAs)](../kit/glossary#SPA), which you can also [build with SvelteKit](../kit/single-page-apps). There are also plugins for [Rollup](https://github.com/sveltejs/rollup-plugin-svelte), [Webpack](https://github.com/sveltejs/svelte-loader) [and a few others](https://sveltesociety.dev/packages?category=build-plugins), but we recommend Vite. From 6f8068637c6f18649d17687c588b06381318d578 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 31 Mar 2025 16:48:42 +0200 Subject: [PATCH 0294/1429] fix: support TS type assertions (#15642) fixes #15565 --- .changeset/lovely-windows-hang.md | 5 +++++ .../src/compiler/phases/1-parse/remove_typescript_nodes.js | 3 +++ .../tests/runtime-runes/samples/typescript/main.svelte | 1 + 3 files changed, 9 insertions(+) create mode 100644 .changeset/lovely-windows-hang.md diff --git a/.changeset/lovely-windows-hang.md b/.changeset/lovely-windows-hang.md new file mode 100644 index 0000000000..406e6c4961 --- /dev/null +++ b/.changeset/lovely-windows-hang.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: support TS type assertions diff --git a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js index 09eb0bfa68..37dc0e17a1 100644 --- a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js +++ b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js @@ -94,6 +94,9 @@ const visitors = { TSTypeAliasDeclaration() { return b.empty; }, + TSTypeAssertion(node, context) { + return context.visit(node.expression); + }, TSEnumDeclaration(node) { e.typescript_invalid_feature(node, 'enums'); }, diff --git a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte index e2942b21f3..d2a9da5439 100644 --- a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte @@ -45,6 +45,7 @@ export type { Hello }; const TypedFoo = Foo<true>; + const typeAssertion = <boolean>true; </script> <script> From b93d004d98608a9a7083ffc5bc1b940c8c472b82 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 31 Mar 2025 16:48:52 +0200 Subject: [PATCH 0295/1429] fix: ensure `undefined` class still applies scoping class, if necessary (#15643) fixes #15635 --- .changeset/weak-knives-battle.md | 5 +++++ packages/svelte/src/internal/client/dom/elements/class.js | 6 +++++- .../tests/css/samples/undefined-with-scope/expected.html | 3 ++- .../tests/css/samples/undefined-with-scope/input.svelte | 3 ++- 4 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 .changeset/weak-knives-battle.md diff --git a/.changeset/weak-knives-battle.md b/.changeset/weak-knives-battle.md new file mode 100644 index 0000000000..702053ee9e --- /dev/null +++ b/.changeset/weak-knives-battle.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure `undefined` class still applies scoping class, if necessary diff --git a/packages/svelte/src/internal/client/dom/elements/class.js b/packages/svelte/src/internal/client/dom/elements/class.js index ecbfcbc010..fc081b8956 100644 --- a/packages/svelte/src/internal/client/dom/elements/class.js +++ b/packages/svelte/src/internal/client/dom/elements/class.js @@ -14,7 +14,11 @@ export function set_class(dom, is_html, value, hash, prev_classes, next_classes) // @ts-expect-error need to add __className to patched prototype var prev = dom.__className; - if (hydrating || prev !== value) { + if ( + hydrating || + prev !== value || + prev === undefined // for edge case of `class={undefined}` + ) { var next_class_name = to_class(value, hash, next_classes); if (!hydrating || next_class_name !== dom.getAttribute('class')) { diff --git a/packages/svelte/tests/css/samples/undefined-with-scope/expected.html b/packages/svelte/tests/css/samples/undefined-with-scope/expected.html index 5eecaa9bb2..5548be6e5a 100644 --- a/packages/svelte/tests/css/samples/undefined-with-scope/expected.html +++ b/packages/svelte/tests/css/samples/undefined-with-scope/expected.html @@ -1 +1,2 @@ -<p class="svelte-xyz">Foo</p> \ No newline at end of file +<p class="svelte-xyz">Foo</p> +<p class="svelte-xyz">Bar</p> \ No newline at end of file diff --git a/packages/svelte/tests/css/samples/undefined-with-scope/input.svelte b/packages/svelte/tests/css/samples/undefined-with-scope/input.svelte index c68fb40dea..20639600d0 100644 --- a/packages/svelte/tests/css/samples/undefined-with-scope/input.svelte +++ b/packages/svelte/tests/css/samples/undefined-with-scope/input.svelte @@ -1,3 +1,4 @@ <style>p { color: red; }</style> -<p class={undefined}>Foo</p> \ No newline at end of file +<p class={undefined}>Foo</p> +<p class="{undefined}">Bar</p> From d6d99ca2fd198b67a536d8ee30c0df51c5ac788a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 14:40:31 -0400 Subject: [PATCH 0296/1429] Version Packages (#15647) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/lovely-windows-hang.md | 5 ----- .changeset/weak-knives-battle.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/lovely-windows-hang.md delete mode 100644 .changeset/weak-knives-battle.md diff --git a/.changeset/lovely-windows-hang.md b/.changeset/lovely-windows-hang.md deleted file mode 100644 index 406e6c4961..0000000000 --- a/.changeset/lovely-windows-hang.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: support TS type assertions diff --git a/.changeset/weak-knives-battle.md b/.changeset/weak-knives-battle.md deleted file mode 100644 index 702053ee9e..0000000000 --- a/.changeset/weak-knives-battle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure `undefined` class still applies scoping class, if necessary diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index e6b5de7e5f..699a094aa2 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.25.4 + +### Patch Changes + +- fix: support TS type assertions ([#15642](https://github.com/sveltejs/svelte/pull/15642)) + +- fix: ensure `undefined` class still applies scoping class, if necessary ([#15643](https://github.com/sveltejs/svelte/pull/15643)) + ## 5.25.3 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 0410520634..9c79dfc8bc 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.25.3", + "version": "5.25.4", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index e4ae33a9b1..b2cfbd6469 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.25.3'; +export const VERSION = '5.25.4'; export const PUBLIC_VERSION = '5'; From 046145fdd7a0bf245770f7866530ae263b927708 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 31 Mar 2025 20:41:20 +0200 Subject: [PATCH 0297/1429] fix: make sure CSS is preserved during SSR with bindings (#15645) fixes #15543 No test because this is a one-liner in a stable part of the code-base, also it can only happen within legacy mode when using bindings which is not getting any new updates at this point --- .changeset/smooth-mugs-grin.md | 5 +++++ packages/svelte/src/internal/server/index.js | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/smooth-mugs-grin.md diff --git a/.changeset/smooth-mugs-grin.md b/.changeset/smooth-mugs-grin.md new file mode 100644 index 0000000000..3302a63012 --- /dev/null +++ b/.changeset/smooth-mugs-grin.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: make sure CSS is preserved during SSR with bindings diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index 6098b496c5..bf36a595d8 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -49,6 +49,7 @@ export function copy_payload({ out, css, head, uid }) { */ export function assign_payload(p1, p2) { p1.out = p2.out; + p1.css = p2.css; p1.head = p2.head; p1.uid = p2.uid; } From 97542048dfc5f40d0ce31ccad29509613da07f21 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 31 Mar 2025 21:15:06 +0200 Subject: [PATCH 0298/1429] fix: silence assignment warning on more function bindings (#15644) * fix: silence assignment warning on more function bindings fixes #15550 * Update packages/svelte/tests/html_equal.js --- .changeset/rare-falcons-admire.md | 5 +++++ .../3-transform/client/visitors/AssignmentExpression.js | 4 +++- packages/svelte/tests/html_equal.js | 2 +- .../samples/proxy-coercive-assignment-warning/_config.js | 8 ++++---- .../samples/proxy-coercive-assignment-warning/main.svelte | 7 +++++++ 5 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 .changeset/rare-falcons-admire.md diff --git a/.changeset/rare-falcons-admire.md b/.changeset/rare-falcons-admire.md new file mode 100644 index 0000000000..ad13dd1cbc --- /dev/null +++ b/.changeset/rare-falcons-admire.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: silence assignment warning on more function bindings diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js index 150c56e166..850cd83b15 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js @@ -165,7 +165,9 @@ function build_assignment(operator, left, right, context) { path.at(-1) === 'SvelteComponent' || (path.at(-1) === 'ArrowFunctionExpression' && path.at(-2) === 'SequenceExpression' && - (path.at(-3) === 'Component' || path.at(-3) === 'SvelteComponent')) + (path.at(-3) === 'Component' || + path.at(-3) === 'SvelteComponent' || + path.at(-3) === 'BindDirective')) ) { should_transform = false; } diff --git a/packages/svelte/tests/html_equal.js b/packages/svelte/tests/html_equal.js index 0ebf1fa6bd..4c9e2a7253 100644 --- a/packages/svelte/tests/html_equal.js +++ b/packages/svelte/tests/html_equal.js @@ -86,7 +86,7 @@ export function normalize_html( clean_children(node); return node.innerHTML; } catch (err) { - throw new Error(`Failed to normalize HTML:\n${html}`); + throw new Error(`Failed to normalize HTML:\n${html}\nCause: ${err}`); } } diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js index f029227472..4462f492fa 100644 --- a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/_config.js @@ -6,7 +6,7 @@ export default test({ dev: true }, - html: `<button>items: null</button> <div>x</div> <input type="checkbox" value="1"><input type="checkbox" value="2">`, + html: `<button>items: null</button> <div>x</div> <input type="checkbox" value="1"><input type="checkbox" value="2"><input>`, test({ assert, target, warnings }) { const btn = target.querySelector('button'); @@ -15,13 +15,13 @@ export default test({ flushSync(() => btn.click()); assert.htmlEqual( target.innerHTML, - `<button>items: []</button> <div>x</div> <input type="checkbox" value="1"><input type="checkbox" value="2">` + `<button>items: []</button> <div>x</div> <input type="checkbox" value="1"><input type="checkbox" value="2"><input>` ); flushSync(() => btn.click()); assert.htmlEqual( target.innerHTML, - `<button>items: [0]</button> <div>x</div> <input type="checkbox" value="1"><input type="checkbox" value="2">` + `<button>items: [0]</button> <div>x</div> <input type="checkbox" value="1"><input type="checkbox" value="2"><input>` ); const input = target.querySelector('input'); @@ -30,7 +30,7 @@ export default test({ flushSync(() => input.dispatchEvent(new Event('change', { bubbles: true }))); assert.deepEqual(warnings, [ - 'Assignment to `items` property (main.svelte:8:24) will evaluate to the right-hand side, not the value of `items` following the assignment. This may result in unexpected behaviour.' + 'Assignment to `items` property (main.svelte:9:24) will evaluate to the right-hand side, not the value of `items` following the assignment. This may result in unexpected behaviour.' ]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte index ad94c4e56e..a79fe873b7 100644 --- a/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/proxy-coercive-assignment-warning/main.svelte @@ -3,6 +3,7 @@ let entries = $state([]); let object = $state({ items: null, group: [] }); + let elementFunBind = $state(); </script> <button onclick={() => (object.items ??= []).push(object.items.length)}> @@ -13,6 +14,12 @@ <div bind:this={entries[0]}>x</div> <input type="checkbox" value=1 bind:group={object.group}> <input type="checkbox" value=2 bind:group={object.group}> + <Test bind:this={entries[1]}></Test> <Test bind:this={() => entries[2], (v) => (entries[2] = v)}></Test> <Test bind:x={entries[3]}></Test> + +{#snippet funBind(context)} + <input bind:this={() => {}, (e) => (context.element = e)} /> +{/snippet} +{@render funBind({ set element(e) { elementFunBind = e } })} From 5d0eb27526caf457ef01c374cefd8740be7fc335 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 15:21:34 -0400 Subject: [PATCH 0299/1429] chore(deps-dev): bump vite from 5.4.14 to 5.4.16 (#15648) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.14 to 5.4.16. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v5.4.16/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.4.16/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- playgrounds/sandbox/package.json | 2 +- pnpm-lock.yaml | 478 +++++++++++++++++++++++++++---- 2 files changed, 420 insertions(+), 60 deletions(-) diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index 9e2e5f6738..cb44905309 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -18,7 +18,7 @@ "polka": "^1.0.0-next.25", "svelte": "workspace:*", "tinyglobby": "^0.2.12", - "vite": "^5.4.14", + "vite": "^5.4.16", "vite-plugin-inspect": "^0.8.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c687db12d4..eaf407e304 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,7 +152,7 @@ importers: devDependencies: '@sveltejs/vite-plugin-svelte': specifier: ^4.0.0-next.6 - version: 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) polka: specifier: ^1.0.0-next.25 version: 1.0.0-next.25 @@ -163,11 +163,11 @@ importers: specifier: ^0.2.12 version: 0.2.12 vite: - specifier: ^5.4.14 - version: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + specifier: ^5.4.16 + version: 5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vite-plugin-inspect: specifier: ^0.8.4 - version: 0.8.4(rollup@4.22.4)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 0.8.4(rollup@4.38.0)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) packages: @@ -408,6 +408,12 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.5.1': + resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/regexpp@4.12.1': resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} @@ -545,81 +551,181 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.38.0': + resolution: {integrity: sha512-ldomqc4/jDZu/xpYU+aRxo3V4mGCV9HeTgUBANI3oIQMOL+SsxB+S2lxMpkFp5UamSS3XuTMQVbsS24R4J4Qjg==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.22.4': resolution: {integrity: sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.38.0': + resolution: {integrity: sha512-VUsgcy4GhhT7rokwzYQP+aV9XnSLkkhlEJ0St8pbasuWO/vwphhZQxYEKUP3ayeCYLhk6gEtacRpYP/cj3GjyQ==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.22.4': resolution: {integrity: sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.38.0': + resolution: {integrity: sha512-buA17AYXlW9Rn091sWMq1xGUvWQFOH4N1rqUxGJtEQzhChxWjldGCCup7r/wUnaI6Au8sKXpoh0xg58a7cgcpg==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.22.4': resolution: {integrity: sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.38.0': + resolution: {integrity: sha512-Mgcmc78AjunP1SKXl624vVBOF2bzwNWFPMP4fpOu05vS0amnLcX8gHIge7q/lDAHy3T2HeR0TqrriZDQS2Woeg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.38.0': + resolution: {integrity: sha512-zzJACgjLbQTsscxWqvrEQAEh28hqhebpRz5q/uUd1T7VTwUNZ4VIXQt5hE7ncs0GrF+s7d3S4on4TiXUY8KoQA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.38.0': + resolution: {integrity: sha512-hCY/KAeYMCyDpEE4pTETam0XZS4/5GXzlLgpi5f0IaPExw9kuB+PDTOTLuPtM10TlRG0U9OSmXJ+Wq9J39LvAg==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.22.4': resolution: {integrity: sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.38.0': + resolution: {integrity: sha512-mimPH43mHl4JdOTD7bUMFhBdrg6f9HzMTOEnzRmXbOZqjijCw8LA5z8uL6LCjxSa67H2xiLFvvO67PT05PRKGg==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.22.4': resolution: {integrity: sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.38.0': + resolution: {integrity: sha512-tPiJtiOoNuIH8XGG8sWoMMkAMm98PUwlriOFCCbZGc9WCax+GLeVRhmaxjJtz6WxrPKACgrwoZ5ia/uapq3ZVg==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.22.4': resolution: {integrity: sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.38.0': + resolution: {integrity: sha512-wZco59rIVuB0tjQS0CSHTTUcEde+pXQWugZVxWaQFdQQ1VYub/sTrNdY76D1MKdN2NB48JDuGABP6o6fqos8mA==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.22.4': resolution: {integrity: sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.38.0': + resolution: {integrity: sha512-fQgqwKmW0REM4LomQ+87PP8w8xvU9LZfeLBKybeli+0yHT7VKILINzFEuggvnV9M3x1Ed4gUBmGUzCo/ikmFbQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.38.0': + resolution: {integrity: sha512-hz5oqQLXTB3SbXpfkKHKXLdIp02/w3M+ajp8p4yWOWwQRtHWiEOCKtc9U+YXahrwdk+3qHdFMDWR5k+4dIlddg==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': resolution: {integrity: sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.38.0': + resolution: {integrity: sha512-NXqygK/dTSibQ+0pzxsL3r4Xl8oPqVoWbZV9niqOnIHV/J92fe65pOir0xjkUZDRSPyFRvu+4YOpJF9BZHQImw==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.22.4': resolution: {integrity: sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.38.0': + resolution: {integrity: sha512-GEAIabR1uFyvf/jW/5jfu8gjM06/4kZ1W+j1nWTSSB3w6moZEBm7iBtzwQ3a1Pxos2F7Gz+58aVEnZHU295QTg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.38.0': + resolution: {integrity: sha512-9EYTX+Gus2EGPbfs+fh7l95wVADtSQyYw4DfSBcYdUEAmP2lqSZY0Y17yX/3m5VKGGJ4UmIH5LHLkMJft3bYoA==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.22.4': resolution: {integrity: sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.38.0': + resolution: {integrity: sha512-Mpp6+Z5VhB9VDk7RwZXoG2qMdERm3Jw07RNlXHE0bOnEeX+l7Fy4bg+NxfyN15ruuY3/7Vrbpm75J9QHFqj5+Q==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.22.4': resolution: {integrity: sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.38.0': + resolution: {integrity: sha512-vPvNgFlZRAgO7rwncMeE0+8c4Hmc+qixnp00/Uv3ht2x7KYrJ6ERVd3/R0nUtlE6/hu7/HiiNHJ/rP6knRFt1w==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.22.4': resolution: {integrity: sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.38.0': + resolution: {integrity: sha512-q5Zv+goWvQUGCaL7fU8NuTw8aydIL/C9abAVGCzRReuj5h30TPx4LumBtAidrVOtXnlB+RZkBtExMsfqkMfb8g==} + cpu: [x64] + os: [linux] + '@rollup/rollup-win32-arm64-msvc@4.22.4': resolution: {integrity: sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.38.0': + resolution: {integrity: sha512-u/Jbm1BU89Vftqyqbmxdq14nBaQjQX1HhmsdBWqSdGClNaKwhjsg5TpW+5Ibs1mb8Es9wJiMdl86BcmtUVXNZg==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.22.4': resolution: {integrity: sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.38.0': + resolution: {integrity: sha512-mqu4PzTrlpNHHbu5qleGvXJoGgHpChBlrBx/mEhTPpnAL1ZAYFlvHD7rLK839LLKQzqEQMFJfGrrOHItN4ZQqA==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.22.4': resolution: {integrity: sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.38.0': + resolution: {integrity: sha512-jjqy3uWlecfB98Psxb5cD6Fny9Fupv9LrDSPTQZUROqjvZmcCqNu4UMl7qqhlUUGpwiAkotj6GYu4SZdcr/nLw==} + cpu: [x64] + os: [win32] + '@stylistic/eslint-plugin-js@1.8.0': resolution: {integrity: sha512-jdvnzt+pZPg8TfclZlTZPiUbbima93ylvQ+wNgHLNmup3obY6heQvgewSu9i2CfS61BnRByv+F9fxQLPoNeHag==} engines: {node: ^16.0.0 || >=18.0.0} @@ -673,6 +779,9 @@ packages: '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -707,6 +816,10 @@ packages: resolution: {integrity: sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.29.0': + resolution: {integrity: sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/type-utils@8.26.0': resolution: {integrity: sha512-ruk0RNChLKz3zKGn2LwXuVoeBcUMh+jaqzN461uMMdxy5H9epZqIBtYj7UiPXRuOpaALXGbmRuZQhmwHhaS04Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -718,12 +831,22 @@ packages: resolution: {integrity: sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.29.0': + resolution: {integrity: sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.26.0': resolution: {integrity: sha512-tiJ1Hvy/V/oMVRTbEOIeemA2XoylimlDQ03CgPPNaHYZbpsc78Hmngnt+WXZfJX1pjQ711V7g0H7cSJThGYfPQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/typescript-estree@8.29.0': + resolution: {integrity: sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/utils@8.26.0': resolution: {integrity: sha512-2L2tU3FVwhvU14LndnQCA2frYC8JnPDVKyQtWFPf8IYFMt/ykEN1bPolNhNbCVgOmdzTlWdusCTKA/9nKrf8Ig==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -731,10 +854,21 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/utils@8.29.0': + resolution: {integrity: sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/visitor-keys@8.26.0': resolution: {integrity: sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.29.0': + resolution: {integrity: sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitest/coverage-v8@2.0.5': resolution: {integrity: sha512-qeFcySCg5FLO2bHHSa0tAZAOnAUbp4L6/A5JDuj9+bt53JREl8hpLjLHEWF0e/gWc8INVpJaqA7+Ene2rclpZg==} peerDependencies: @@ -779,6 +913,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@7.1.1: resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} engines: {node: '>= 14'} @@ -916,7 +1055,7 @@ packages: resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} concat-map@0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} cross-spawn@5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} @@ -1161,6 +1300,10 @@ packages: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -1257,6 +1400,10 @@ packages: resolution: {integrity: sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==} engines: {node: '>=18'} + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + globby@11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} @@ -1551,6 +1698,10 @@ packages: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -1559,10 +1710,6 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - minimatch@10.0.1: - resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} - engines: {node: 20 || >=22} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -1585,8 +1732,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -1751,8 +1898,8 @@ packages: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} - postcss@8.5.1: - resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==} + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -1827,6 +1974,11 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.38.0: + resolution: {integrity: sha512-5SsIRtJy9bf1ErAOiFMFzl64Ex9X5V7bnJ+WlFMb+zmP459OSWCEG7b0ERZ+PEU7xPt4OG3RHbrp1LJlXxYTrw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + rrweb-cssom@0.7.1: resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} @@ -2064,8 +2216,14 @@ packages: peerDependencies: typescript: '>=4.8.4' - ts-declaration-location@1.0.5: - resolution: {integrity: sha512-WqmlO9IoeYwCqJ2E9kHMcY9GZhhfLYItC3VnHDlPOrg6nNdUWS4wn4hhDZUPt60m1EvtjPIZyprTjpI992Bgzw==} + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-declaration-location@1.0.7: + resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==} peerDependencies: typescript: '>=4.0.0' @@ -2152,6 +2310,37 @@ packages: terser: optional: true + vite@5.4.16: + resolution: {integrity: sha512-Y5gnfp4NemVfgOTDQAunSD4346fal44L9mszGGY/e+qxsRT5y1sMlS/8tiQ8AFAp+MFgYNSINdfEchJiPm41vQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + vitefu@0.2.5: resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} peerDependencies: @@ -2522,6 +2711,11 @@ snapshots: eslint: 9.9.1 eslint-visitor-keys: 3.4.3 + '@eslint-community/eslint-utils@4.5.1(eslint@9.9.1)': + dependencies: + eslint: 9.9.1 + eslint-visitor-keys: 3.4.3 + '@eslint-community/regexpp@4.12.1': {} '@eslint/config-array@0.18.0': @@ -2666,58 +2860,126 @@ snapshots: optionalDependencies: rollup: 4.22.4 + '@rollup/pluginutils@5.1.0(rollup@4.38.0)': + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 2.3.1 + optionalDependencies: + rollup: 4.38.0 + '@rollup/rollup-android-arm-eabi@4.22.4': optional: true + '@rollup/rollup-android-arm-eabi@4.38.0': + optional: true + '@rollup/rollup-android-arm64@4.22.4': optional: true + '@rollup/rollup-android-arm64@4.38.0': + optional: true + '@rollup/rollup-darwin-arm64@4.22.4': optional: true + '@rollup/rollup-darwin-arm64@4.38.0': + optional: true + '@rollup/rollup-darwin-x64@4.22.4': optional: true + '@rollup/rollup-darwin-x64@4.38.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.38.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.38.0': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.22.4': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.38.0': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.22.4': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.38.0': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.22.4': optional: true + '@rollup/rollup-linux-arm64-gnu@4.38.0': + optional: true + '@rollup/rollup-linux-arm64-musl@4.22.4': optional: true + '@rollup/rollup-linux-arm64-musl@4.38.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.38.0': + optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.38.0': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.22.4': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.38.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.38.0': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.22.4': optional: true + '@rollup/rollup-linux-s390x-gnu@4.38.0': + optional: true + '@rollup/rollup-linux-x64-gnu@4.22.4': optional: true + '@rollup/rollup-linux-x64-gnu@4.38.0': + optional: true + '@rollup/rollup-linux-x64-musl@4.22.4': optional: true + '@rollup/rollup-linux-x64-musl@4.38.0': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.22.4': optional: true + '@rollup/rollup-win32-arm64-msvc@4.38.0': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.22.4': optional: true + '@rollup/rollup-win32-ia32-msvc@4.38.0': + optional: true + '@rollup/rollup-win32-x64-msvc@4.22.4': optional: true + '@rollup/rollup-win32-x64-msvc@4.38.0': + optional: true + '@stylistic/eslint-plugin-js@1.8.0(eslint@9.9.1)': dependencies: '@types/eslint': 8.56.12 - acorn: 8.14.0 + acorn: 8.14.1 escape-string-regexp: 4.0.0 eslint: 9.9.1 eslint-visitor-keys: 3.4.3 @@ -2738,25 +3000,25 @@ snapshots: typescript: 5.5.4 typescript-eslint: 8.26.0(eslint@9.9.1)(typescript@5.5.4) - '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.4.0 svelte: link:packages/svelte - vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 svelte: link:packages/svelte - vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) - vitefu: 0.2.5(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + vite: 5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vitefu: 0.2.5(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) transitivePeerDependencies: - supports-color @@ -2771,13 +3033,15 @@ snapshots: '@types/eslint@8.56.12': dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.7 '@types/json-schema': 7.0.15 '@types/estree@1.0.5': {} '@types/estree@1.0.6': {} + '@types/estree@1.0.7': {} + '@types/json-schema@7.0.15': {} '@types/node@12.20.55': {} @@ -2824,6 +3088,11 @@ snapshots: '@typescript-eslint/types': 8.26.0 '@typescript-eslint/visitor-keys': 8.26.0 + '@typescript-eslint/scope-manager@8.29.0': + dependencies: + '@typescript-eslint/types': 8.29.0 + '@typescript-eslint/visitor-keys': 8.29.0 + '@typescript-eslint/type-utils@8.26.0(eslint@9.9.1)(typescript@5.5.4)': dependencies: '@typescript-eslint/typescript-estree': 8.26.0(typescript@5.5.4) @@ -2837,6 +3106,8 @@ snapshots: '@typescript-eslint/types@8.26.0': {} + '@typescript-eslint/types@8.29.0': {} + '@typescript-eslint/typescript-estree@8.26.0(typescript@5.5.4)': dependencies: '@typescript-eslint/types': 8.26.0 @@ -2851,6 +3122,20 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.29.0(typescript@5.5.4)': + dependencies: + '@typescript-eslint/types': 8.29.0 + '@typescript-eslint/visitor-keys': 8.29.0 + debug: 4.4.0 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 2.1.0(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.26.0(eslint@9.9.1)(typescript@5.5.4)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) @@ -2862,11 +3147,27 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.29.0(eslint@9.9.1)(typescript@5.5.4)': + dependencies: + '@eslint-community/eslint-utils': 4.5.1(eslint@9.9.1) + '@typescript-eslint/scope-manager': 8.29.0 + '@typescript-eslint/types': 8.29.0 + '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.5.4) + eslint: 9.9.1 + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@8.26.0': dependencies: '@typescript-eslint/types': 8.26.0 eslint-visitor-keys: 4.2.0 + '@typescript-eslint/visitor-keys@8.29.0': + dependencies: + '@typescript-eslint/types': 8.29.0 + eslint-visitor-keys: 4.2.0 + '@vitest/coverage-v8@2.0.5(vitest@2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: '@ampproject/remapping': 2.3.0 @@ -2929,8 +3230,14 @@ snapshots: dependencies: acorn: 8.14.0 + acorn-jsx@5.3.2(acorn@8.14.1): + dependencies: + acorn: 8.14.1 + acorn@8.14.0: {} + acorn@8.14.1: {} + agent-base@7.1.1: dependencies: debug: 4.4.0 @@ -3192,7 +3499,7 @@ snapshots: eslint-plugin-es-x@7.8.0(eslint@9.9.1): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) + '@eslint-community/eslint-utils': 4.5.1(eslint@9.9.1) '@eslint-community/regexpp': 4.12.1 eslint: 9.9.1 eslint-compat-utils: 0.5.1(eslint@9.9.1) @@ -3201,33 +3508,33 @@ snapshots: eslint-plugin-n@17.16.1(eslint@9.9.1)(typescript@5.5.4): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) - '@typescript-eslint/utils': 8.26.0(eslint@9.9.1)(typescript@5.5.4) + '@eslint-community/eslint-utils': 4.5.1(eslint@9.9.1) + '@typescript-eslint/utils': 8.29.0(eslint@9.9.1)(typescript@5.5.4) enhanced-resolve: 5.18.1 eslint: 9.9.1 eslint-plugin-es-x: 7.8.0(eslint@9.9.1) get-tsconfig: 4.10.0 - globals: 15.14.0 + globals: 15.15.0 ignore: 5.3.2 minimatch: 9.0.5 semver: 7.7.1 - ts-declaration-location: 1.0.5(typescript@5.5.4) + ts-declaration-location: 1.0.7(typescript@5.5.4) transitivePeerDependencies: - supports-color - typescript eslint-plugin-svelte@2.38.0(eslint@9.9.1)(svelte@packages+svelte): dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.9.1) + '@eslint-community/eslint-utils': 4.5.1(eslint@9.9.1) '@jridgewell/sourcemap-codec': 1.5.0 debug: 4.4.0 eslint: 9.9.1 eslint-compat-utils: 0.5.1(eslint@9.9.1) esutils: 2.0.3 known-css-properties: 0.30.0 - postcss: 8.5.1 - postcss-load-config: 3.1.4(postcss@8.5.1) - postcss-safe-parser: 6.0.0(postcss@8.5.1) + postcss: 8.5.3 + postcss-load-config: 3.1.4(postcss@8.5.3) + postcss-safe-parser: 6.0.0(postcss@8.5.3) postcss-selector-parser: 6.1.2 semver: 7.7.1 svelte-eslint-parser: 0.43.0(svelte@packages+svelte) @@ -3300,8 +3607,8 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} @@ -3348,6 +3655,14 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.5 + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} @@ -3447,6 +3762,8 @@ snapshots: globals@15.14.0: {} + globals@15.15.0: {} + globby@11.1.0: dependencies: array-union: 2.1.0 @@ -3740,16 +4057,17 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + mime-db@1.52.0: {} mime-types@2.1.35: dependencies: mime-db: 1.52.0 - minimatch@10.0.1: - dependencies: - brace-expansion: 2.0.1 - minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -3766,7 +4084,7 @@ snapshots: ms@2.1.3: {} - nanoid@3.3.8: {} + nanoid@3.3.11: {} natural-compare@1.4.0: {} @@ -3875,29 +4193,29 @@ snapshots: '@polka/url': 1.0.0-next.25 trouter: 4.0.0 - postcss-load-config@3.1.4(postcss@8.5.1): + postcss-load-config@3.1.4(postcss@8.5.3): dependencies: lilconfig: 2.1.0 yaml: 1.10.2 optionalDependencies: - postcss: 8.5.1 + postcss: 8.5.3 - postcss-safe-parser@6.0.0(postcss@8.5.1): + postcss-safe-parser@6.0.0(postcss@8.5.3): dependencies: - postcss: 8.5.1 + postcss: 8.5.3 - postcss-scss@4.0.9(postcss@8.5.1): + postcss-scss@4.0.9(postcss@8.5.3): dependencies: - postcss: 8.5.1 + postcss: 8.5.3 postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss@8.5.1: + postcss@8.5.3: dependencies: - nanoid: 3.3.8 + nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -3974,6 +4292,32 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.22.4 fsevents: 2.3.3 + rollup@4.38.0: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.38.0 + '@rollup/rollup-android-arm64': 4.38.0 + '@rollup/rollup-darwin-arm64': 4.38.0 + '@rollup/rollup-darwin-x64': 4.38.0 + '@rollup/rollup-freebsd-arm64': 4.38.0 + '@rollup/rollup-freebsd-x64': 4.38.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.38.0 + '@rollup/rollup-linux-arm-musleabihf': 4.38.0 + '@rollup/rollup-linux-arm64-gnu': 4.38.0 + '@rollup/rollup-linux-arm64-musl': 4.38.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.38.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.38.0 + '@rollup/rollup-linux-riscv64-gnu': 4.38.0 + '@rollup/rollup-linux-riscv64-musl': 4.38.0 + '@rollup/rollup-linux-s390x-gnu': 4.38.0 + '@rollup/rollup-linux-x64-gnu': 4.38.0 + '@rollup/rollup-linux-x64-musl': 4.38.0 + '@rollup/rollup-win32-arm64-msvc': 4.38.0 + '@rollup/rollup-win32-ia32-msvc': 4.38.0 + '@rollup/rollup-win32-x64-msvc': 4.38.0 + fsevents: 2.3.3 + rrweb-cssom@0.7.1: {} run-applescript@7.0.0: {} @@ -4092,8 +4436,8 @@ snapshots: eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - postcss: 8.5.1 - postcss-scss: 4.0.9(postcss@8.5.1) + postcss: 8.5.3 + postcss-scss: 4.0.9(postcss@8.5.3) optionalDependencies: svelte: link:packages/svelte @@ -4173,9 +4517,13 @@ snapshots: dependencies: typescript: 5.5.4 - ts-declaration-location@1.0.5(typescript@5.5.4): + ts-api-utils@2.1.0(typescript@5.5.4): + dependencies: + typescript: 5.5.4 + + ts-declaration-location@1.0.7(typescript@5.5.4): dependencies: - minimatch: 10.0.1 + picomatch: 4.0.2 typescript: 5.5.4 type-check@0.4.0: @@ -4214,7 +4562,7 @@ snapshots: debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 1.1.2 - vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - '@types/node' - less @@ -4226,10 +4574,10 @@ snapshots: - supports-color - terser - vite-plugin-inspect@0.8.4(rollup@4.22.4)(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vite-plugin-inspect@0.8.4(rollup@4.38.0)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): dependencies: '@antfu/utils': 0.7.8 - '@rollup/pluginutils': 5.1.0(rollup@4.22.4) + '@rollup/pluginutils': 5.1.0(rollup@4.38.0) debug: 4.4.0 error-stack-parser-es: 0.1.1 fs-extra: 11.2.0 @@ -4237,7 +4585,7 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 2.0.4 - vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - rollup - supports-color @@ -4245,7 +4593,7 @@ snapshots: vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: esbuild: 0.21.5 - postcss: 8.5.1 + postcss: 8.5.3 rollup: 4.22.4 optionalDependencies: '@types/node': 20.12.7 @@ -4254,9 +4602,21 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vitefu@0.2.5(vite@5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): + dependencies: + esbuild: 0.21.5 + postcss: 8.5.3 + rollup: 4.38.0 optionalDependencies: - vite: 5.4.14(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + '@types/node': 20.12.7 + fsevents: 2.3.3 + lightningcss: 1.23.0 + sass: 1.70.0 + terser: 5.27.0 + + vitefu@0.2.5(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + optionalDependencies: + vite: 5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vitest@2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: From ec91cacab2f10529a55ce8b6e8a2cdd21794a57e Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Mon, 31 Mar 2025 12:25:56 -0700 Subject: [PATCH 0300/1429] init (#15628) --- .changeset/pink-lamps-leave.md | 5 +++ .../3-transform/client/visitors/ClassBody.js | 35 ++++++------------- 2 files changed, 16 insertions(+), 24 deletions(-) create mode 100644 .changeset/pink-lamps-leave.md diff --git a/.changeset/pink-lamps-leave.md b/.changeset/pink-lamps-leave.md new file mode 100644 index 0000000000..7ca9d87362 --- /dev/null +++ b/.changeset/pink-lamps-leave.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: add setters to `$derived` class properties diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js index 5787b590a8..24c20d7f94 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js @@ -142,30 +142,17 @@ export function ClassBody(node, context) { // get foo() { return this.#foo; } body.push(b.method('get', definition.key, [], [b.return(b.call('$.get', member))])); - if (field.kind === 'state' || field.kind === 'raw_state') { - // set foo(value) { this.#foo = value; } - const value = b.id('value'); - - body.push( - b.method( - 'set', - definition.key, - [value], - [b.stmt(b.call('$.set', member, value, field.kind === 'state' && b.true))] - ) - ); - } - - if (dev && (field.kind === 'derived' || field.kind === 'derived_by')) { - body.push( - b.method( - 'set', - definition.key, - [b.id('_')], - [b.throw_error(`Cannot update a derived property ('${name}')`)] - ) - ); - } + // set foo(value) { this.#foo = value; } + const val = b.id('value'); + + body.push( + b.method( + 'set', + definition.key, + [val], + [b.stmt(b.call('$.set', member, val, field.kind === 'state' && b.true))] + ) + ); } continue; } From c822f9b0bf77abe105123a8809bb183312aa9b74 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 15:35:56 -0400 Subject: [PATCH 0301/1429] Version Packages (#15649) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/pink-lamps-leave.md | 5 ----- .changeset/rare-falcons-admire.md | 5 ----- .changeset/smooth-mugs-grin.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 .changeset/pink-lamps-leave.md delete mode 100644 .changeset/rare-falcons-admire.md delete mode 100644 .changeset/smooth-mugs-grin.md diff --git a/.changeset/pink-lamps-leave.md b/.changeset/pink-lamps-leave.md deleted file mode 100644 index 7ca9d87362..0000000000 --- a/.changeset/pink-lamps-leave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: add setters to `$derived` class properties diff --git a/.changeset/rare-falcons-admire.md b/.changeset/rare-falcons-admire.md deleted file mode 100644 index ad13dd1cbc..0000000000 --- a/.changeset/rare-falcons-admire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: silence assignment warning on more function bindings diff --git a/.changeset/smooth-mugs-grin.md b/.changeset/smooth-mugs-grin.md deleted file mode 100644 index 3302a63012..0000000000 --- a/.changeset/smooth-mugs-grin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: make sure CSS is preserved during SSR with bindings diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 699a094aa2..e3fb18e85d 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.25.5 + +### Patch Changes + +- fix: add setters to `$derived` class properties ([#15628](https://github.com/sveltejs/svelte/pull/15628)) + +- fix: silence assignment warning on more function bindings ([#15644](https://github.com/sveltejs/svelte/pull/15644)) + +- fix: make sure CSS is preserved during SSR with bindings ([#15645](https://github.com/sveltejs/svelte/pull/15645)) + ## 5.25.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 9c79dfc8bc..f70ae5cac6 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.25.4", + "version": "5.25.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b2cfbd6469..b07135b6fa 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.25.4'; +export const VERSION = '5.25.5'; export const PUBLIC_VERSION = '5'; From e035e2838faf26da476e73831fb0e555464bf1d1 Mon Sep 17 00:00:00 2001 From: 7nik <kfiiranet@gmail.com> Date: Mon, 31 Mar 2025 22:37:27 +0300 Subject: [PATCH 0302/1429] Fix: consider component and its snippets during css pruning (#15630) * fix: consider component and its snippets during css pruning * add changeset * fix: avoid iterator.map --------- Co-authored-by: 7nik <kifiranet@gmail.com> --- .changeset/nervous-humans-flash.md | 5 +++ .../phases/2-analyze/css/css-prune.js | 26 +++++++++---- .../_config.js | 24 +++--------- .../expected.css | 4 +- .../input.svelte | 4 +- .../Child.svelte | 5 +++ .../siblings-combinator-component/_config.js | 20 ++++++++++ .../expected.css | 8 ++++ .../input.svelte | 37 +++++++++++++++++++ .../siblings-combinator-slot/_config.js | 10 +---- .../siblings-combinator-slot/expected.css | 2 +- .../siblings-combinator-slot/input.svelte | 2 +- 12 files changed, 108 insertions(+), 39 deletions(-) create mode 100644 .changeset/nervous-humans-flash.md create mode 100644 packages/svelte/tests/css/samples/siblings-combinator-component/Child.svelte create mode 100644 packages/svelte/tests/css/samples/siblings-combinator-component/_config.js create mode 100644 packages/svelte/tests/css/samples/siblings-combinator-component/expected.css create mode 100644 packages/svelte/tests/css/samples/siblings-combinator-component/input.svelte diff --git a/.changeset/nervous-humans-flash.md b/.changeset/nervous-humans-flash.md new file mode 100644 index 0000000000..753bb91e3f --- /dev/null +++ b/.changeset/nervous-humans-flash.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: better consider component and its snippets during css pruning diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index 070ec7cd34..0646c6341a 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -251,7 +251,11 @@ function apply_combinator(relative_selector, rest_selectors, rule, node, directi let sibling_matched = false; for (const possible_sibling of siblings.keys()) { - if (possible_sibling.type === 'RenderTag' || possible_sibling.type === 'SlotElement') { + if ( + possible_sibling.type === 'RenderTag' || + possible_sibling.type === 'SlotElement' || + possible_sibling.type === 'Component' + ) { // `{@render foo()}<p>foo</p>` with `:global(.x) + p` is a match if (rest_selectors.length === 1 && rest_selectors[0].metadata.is_global) { sibling_matched = true; @@ -814,10 +818,10 @@ function get_element_parent(node) { * @param {Direction} direction * @param {boolean} adjacent_only * @param {Set<Compiler.AST.SnippetBlock>} seen - * @returns {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag, NodeExistsValue>} + * @returns {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag | Compiler.AST.Component, NodeExistsValue>} */ function get_possible_element_siblings(node, direction, adjacent_only, seen = new Set()) { - /** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag, NodeExistsValue>} */ + /** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement | Compiler.AST.SlotElement | Compiler.AST.RenderTag | Compiler.AST.Component, NodeExistsValue>} */ const result = new Map(); const path = node.metadata.path; @@ -847,14 +851,18 @@ function get_possible_element_siblings(node, direction, adjacent_only, seen = ne } // Special case: slots, render tags and svelte:element tags could resolve to no siblings, // so we want to continue until we find a definite sibling even with the adjacent-only combinator - } else if (is_block(node)) { - if (node.type === 'SlotElement') { + } else if (is_block(node) || node.type === 'Component') { + if (node.type === 'SlotElement' || node.type === 'Component') { result.set(node, NODE_PROBABLY_EXISTS); } const possible_last_child = get_possible_nested_siblings(node, direction, adjacent_only); add_to_map(possible_last_child, result); - if (adjacent_only && has_definite_elements(possible_last_child)) { + if ( + adjacent_only && + node.type !== 'Component' && + has_definite_elements(possible_last_child) + ) { return result; } } else if (node.type === 'SvelteElement') { @@ -907,7 +915,7 @@ function get_possible_element_siblings(node, direction, adjacent_only, seen = ne } /** - * @param {Compiler.AST.EachBlock | Compiler.AST.IfBlock | Compiler.AST.AwaitBlock | Compiler.AST.KeyBlock | Compiler.AST.SlotElement | Compiler.AST.SnippetBlock} node + * @param {Compiler.AST.EachBlock | Compiler.AST.IfBlock | Compiler.AST.AwaitBlock | Compiler.AST.KeyBlock | Compiler.AST.SlotElement | Compiler.AST.SnippetBlock | Compiler.AST.Component} node * @param {Direction} direction * @param {boolean} adjacent_only * @param {Set<Compiler.AST.SnippetBlock>} seen @@ -942,6 +950,10 @@ function get_possible_nested_siblings(node, direction, adjacent_only, seen = new seen.add(node); fragments.push(node.body); break; + + case 'Component': + fragments.push(node.fragment, ...[...node.metadata.snippets].map((s) => s.body)); + break; } /** @type {Map<Compiler.AST.RegularElement | Compiler.AST.SvelteElement, NodeExistsValue>} NodeMap */ diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/_config.js b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/_config.js index 97e470d1c3..7644865495 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/_config.js +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/_config.js @@ -5,32 +5,20 @@ export default test({ { code: 'css_unused_selector', message: 'Unused CSS selector ".b ~ .c"', - start: { character: 137, column: 1, line: 11 }, - end: { character: 144, column: 8, line: 11 } + start: { character: 191, column: 1, line: 13 }, + end: { character: 198, column: 8, line: 13 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ".c ~ .f"', - start: { character: 162, column: 1, line: 12 }, - end: { character: 169, column: 8, line: 12 } - }, - { - code: 'css_unused_selector', - message: 'Unused CSS selector ".f ~ .g"', - start: { character: 187, column: 1, line: 13 }, - end: { character: 194, column: 8, line: 13 } + start: { character: 216, column: 1, line: 14 }, + end: { character: 223, column: 8, line: 14 } }, { code: 'css_unused_selector', message: 'Unused CSS selector ".b ~ .f"', - start: { character: 212, column: 1, line: 14 }, - end: { character: 219, column: 8, line: 14 } - }, - { - code: 'css_unused_selector', - message: 'Unused CSS selector ".b ~ .g"', - start: { character: 237, column: 1, line: 15 }, - end: { character: 244, column: 8, line: 15 } + start: { character: 241, column: 1, line: 15 }, + end: { character: 248, column: 8, line: 15 } } ] }); diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/expected.css b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/expected.css index 67a19d10c9..53fca3ae9e 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/expected.css +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/expected.css @@ -2,10 +2,10 @@ .d.svelte-xyz ~ .e:where(.svelte-xyz) { color: green; } .a.svelte-xyz ~ .g:where(.svelte-xyz) { color: green; } .a.svelte-xyz ~ .b:where(.svelte-xyz) { color: green; } + .f.svelte-xyz ~ .g:where(.svelte-xyz) { color: green; } + .b.svelte-xyz ~ .g:where(.svelte-xyz) { color: green; } /* no match */ /* (unused) .b ~ .c { color: red; }*/ /* (unused) .c ~ .f { color: red; }*/ - /* (unused) .f ~ .g { color: red; }*/ /* (unused) .b ~ .f { color: red; }*/ - /* (unused) .b ~ .g { color: red; }*/ diff --git a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte index 2e2846fa87..52264d3a5a 100644 --- a/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte +++ b/packages/svelte/tests/css/samples/general-siblings-combinator-slot/input.svelte @@ -6,13 +6,13 @@ .d ~ .e { color: green; } .a ~ .g { color: green; } .a ~ .b { color: green; } + .f ~ .g { color: green; } + .b ~ .g { color: green; } /* no match */ .b ~ .c { color: red; } .c ~ .f { color: red; } - .f ~ .g { color: red; } .b ~ .f { color: red; } - .b ~ .g { color: red; } </style> <div class="a"></div> diff --git a/packages/svelte/tests/css/samples/siblings-combinator-component/Child.svelte b/packages/svelte/tests/css/samples/siblings-combinator-component/Child.svelte new file mode 100644 index 0000000000..1df9f35e50 --- /dev/null +++ b/packages/svelte/tests/css/samples/siblings-combinator-component/Child.svelte @@ -0,0 +1,5 @@ +<script> + let { foo } = $props(); +</script> + +{@render foo()} diff --git a/packages/svelte/tests/css/samples/siblings-combinator-component/_config.js b/packages/svelte/tests/css/samples/siblings-combinator-component/_config.js new file mode 100644 index 0000000000..837fa20ae1 --- /dev/null +++ b/packages/svelte/tests/css/samples/siblings-combinator-component/_config.js @@ -0,0 +1,20 @@ +import { test } from '../../test'; + +export default test({ + warnings: [ + { + code: 'css_unused_selector', + message: 'Unused CSS selector "n + m"', + end: { + character: 468, + column: 6, + line: 36 + }, + start: { + character: 463, + column: 1, + line: 36 + } + } + ] +}); diff --git a/packages/svelte/tests/css/samples/siblings-combinator-component/expected.css b/packages/svelte/tests/css/samples/siblings-combinator-component/expected.css new file mode 100644 index 0000000000..d2657ccd21 --- /dev/null +++ b/packages/svelte/tests/css/samples/siblings-combinator-component/expected.css @@ -0,0 +1,8 @@ + x.svelte-xyz + y:where(.svelte-xyz) { color: green; } + x.svelte-xyz + v:where(.svelte-xyz) { color: green; } + x.svelte-xyz + z:where(.svelte-xyz) { color: green; } + y.svelte-xyz + z:where(.svelte-xyz) { color: green; } + v.svelte-xyz + z:where(.svelte-xyz) { color: green; } + .component + z.svelte-xyz { color: green; } + + /* (unused) n + m { color: red; }*/ diff --git a/packages/svelte/tests/css/samples/siblings-combinator-component/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-component/input.svelte new file mode 100644 index 0000000000..8d80acffb3 --- /dev/null +++ b/packages/svelte/tests/css/samples/siblings-combinator-component/input.svelte @@ -0,0 +1,37 @@ +<script> + import Child from './Child.svelte'; +</script> + +<div> + <x></x> + <Child> + <y></y> + {#snippet foo()} + <v></v> + {/snippet} + </Child> + <z></z> + + <Child> + <span> + <n></n> + </span> + {#snippet foo()} + <span> + <n></n> + </span> + {/snippet} + </Child> + <m></m> +</div> + +<style> + x + y { color: green; } + x + v { color: green; } + x + z { color: green; } + y + z { color: green; } + v + z { color: green; } + :global(.component) + z { color: green; } + + n + m { color: red; } +</style> diff --git a/packages/svelte/tests/css/samples/siblings-combinator-slot/_config.js b/packages/svelte/tests/css/samples/siblings-combinator-slot/_config.js index 2786baeff8..8a8f561d01 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-slot/_config.js +++ b/packages/svelte/tests/css/samples/siblings-combinator-slot/_config.js @@ -5,14 +5,8 @@ export default test({ { code: 'css_unused_selector', message: 'Unused CSS selector ".b + .c"', - start: { character: 110, column: 1, line: 10 }, - end: { character: 117, column: 8, line: 10 } - }, - { - code: 'css_unused_selector', - message: 'Unused CSS selector ".c + .f"', - start: { character: 135, column: 1, line: 11 }, - end: { character: 142, column: 8, line: 11 } + start: { character: 137, column: 1, line: 11 }, + end: { character: 144, column: 8, line: 11 } } ] }); diff --git a/packages/svelte/tests/css/samples/siblings-combinator-slot/expected.css b/packages/svelte/tests/css/samples/siblings-combinator-slot/expected.css index 643f6cf13f..85cbb77e65 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-slot/expected.css +++ b/packages/svelte/tests/css/samples/siblings-combinator-slot/expected.css @@ -1,7 +1,7 @@ .d.svelte-xyz + .e:where(.svelte-xyz) { color: green; } .a.svelte-xyz + .b:where(.svelte-xyz) { color: green; } + .c.svelte-xyz + .f:where(.svelte-xyz) { color: green; } /* no match */ /* (unused) .b + .c { color: red; }*/ - /* (unused) .c + .f { color: red; }*/ diff --git a/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte b/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte index 1b543f97b7..57e1df1507 100644 --- a/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte +++ b/packages/svelte/tests/css/samples/siblings-combinator-slot/input.svelte @@ -5,10 +5,10 @@ <style> .d + .e { color: green; } .a + .b { color: green; } + .c + .f { color: green; } /* no match */ .b + .c { color: red; } - .c + .f { color: red; } </style> <div class="a"></div> From 3682371fb602badc48dc4c006288177d398033c7 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Mon, 31 Mar 2025 16:07:19 -0400 Subject: [PATCH 0303/1429] chore: doc fix (#15651) --- documentation/docs/98-reference/.generated/client-errors.md | 2 ++ packages/svelte/messages/client-errors/errors.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index 901c49822c..fd9419176d 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -151,6 +151,8 @@ This error occurs when state is updated while evaluating a `$derived`. You might This is forbidden because it introduces instability: if `<p>{count} is even: {even}</p>` is updated before `odd` is recalculated, `even` will be stale. In most cases the solution is to make everything derived: ```js +let count = 0; +// ---cut--- let even = $derived(count % 2 === 0); let odd = $derived(!even); ``` diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index 572930843e..ca06122cb5 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -107,6 +107,8 @@ This error occurs when state is updated while evaluating a `$derived`. You might This is forbidden because it introduces instability: if `<p>{count} is even: {even}</p>` is updated before `odd` is recalculated, `even` will be stale. In most cases the solution is to make everything derived: ```js +let count = 0; +// ---cut--- let even = $derived(count % 2 === 0); let odd = $derived(!even); ``` From 959f593487d749f92fc924964bbe87190b3da4f0 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:16:13 +0200 Subject: [PATCH 0304/1429] fix: ignore generic type arguments while creating AST (#15659) * fix: ignore generic type arguments while creating AST bumps esrap and fixes a resulting error: because esrap now uses a more compliant TSESTree AST, it has `typeArguments` instead of `typeParameters` in some places - those were not deleted while stripping types, which caused an error. * fix $$render_inner declaration --- .changeset/happy-snakes-pump.md | 5 +++++ packages/svelte/package.json | 2 +- .../compiler/phases/1-parse/remove_typescript_nodes.js | 1 + .../phases/3-transform/server/transform-server.js | 10 ++++------ .../tests/runtime-runes/samples/typescript/main.svelte | 3 ++- .../_expected/server/index.svelte.js | 2 +- pnpm-lock.yaml | 10 +++++----- 7 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 .changeset/happy-snakes-pump.md diff --git a/.changeset/happy-snakes-pump.md b/.changeset/happy-snakes-pump.md new file mode 100644 index 0000000000..a5cb3dc303 --- /dev/null +++ b/.changeset/happy-snakes-pump.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ignore generic type arguments while creating AST diff --git a/packages/svelte/package.json b/packages/svelte/package.json index f70ae5cac6..1434bb9f06 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -156,7 +156,7 @@ "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", - "esrap": "^1.4.3", + "esrap": "^1.4.6", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", diff --git a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js index 37dc0e17a1..4ff6a782b4 100644 --- a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js +++ b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js @@ -24,6 +24,7 @@ const visitors = { // until that day comes, we just delete them so they don't confuse esrap delete n.typeAnnotation; delete n.typeParameters; + delete n.typeArguments; delete n.returnType; delete n.accessibility; }, diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index df3d831d3c..f746e90fe2 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -186,12 +186,10 @@ export function server_component(analysis, options) { ...snippets, b.let('$$settled', b.true), b.let('$$inner_payload'), - b.stmt( - b.function( - b.id('$$render_inner'), - [b.id('$$payload')], - b.block(/** @type {Statement[]} */ (rest)) - ) + b.function_declaration( + b.id('$$render_inner'), + [b.id('$$payload')], + b.block(/** @type {Statement[]} */ (rest)) ), b.do_while( b.unary('!', b.id('$$settled')), diff --git a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte index d2a9da5439..d1b6452df4 100644 --- a/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/typescript/main.svelte @@ -8,7 +8,7 @@ console.log(this); } - function foo(): string { + function foo<T>(): string { return ""!; } @@ -46,6 +46,7 @@ const TypedFoo = Foo<true>; const typeAssertion = <boolean>true; + const x = foo<true>(); </script> <script> diff --git a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js index c091179c41..cadae2cf15 100644 --- a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js @@ -23,7 +23,7 @@ export default function Bind_component_snippet($$payload) { }); $$payload.out += `<!----> value: ${$.escape(value)}`; - }; + } do { $$settled = true; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eaf407e304..e1f276d23f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -87,8 +87,8 @@ importers: specifier: ^1.2.1 version: 1.2.1 esrap: - specifier: ^1.4.3 - version: 1.4.3 + specifier: ^1.4.6 + version: 1.4.6 is-reference: specifier: ^3.0.3 version: 3.0.3 @@ -1261,8 +1261,8 @@ packages: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} - esrap@1.4.3: - resolution: {integrity: sha512-Xddc1RsoFJ4z9nR7W7BFaEPIp4UXoeQ0+077UdWLxbafMQFyU79sQJMk7kxNgRwQ9/aVgaKacCHC2pUACGwmYw==} + esrap@1.4.6: + resolution: {integrity: sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw==} esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} @@ -3617,7 +3617,7 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@1.4.3: + esrap@1.4.6: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 From 5b9053d11178975eeeb93c187f5b01d8ff625244 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:21:29 +0200 Subject: [PATCH 0305/1429] Version Packages (#15650) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/happy-snakes-pump.md | 5 ----- .changeset/nervous-humans-flash.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/happy-snakes-pump.md delete mode 100644 .changeset/nervous-humans-flash.md diff --git a/.changeset/happy-snakes-pump.md b/.changeset/happy-snakes-pump.md deleted file mode 100644 index a5cb3dc303..0000000000 --- a/.changeset/happy-snakes-pump.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ignore generic type arguments while creating AST diff --git a/.changeset/nervous-humans-flash.md b/.changeset/nervous-humans-flash.md deleted file mode 100644 index 753bb91e3f..0000000000 --- a/.changeset/nervous-humans-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: better consider component and its snippets during css pruning diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index e3fb18e85d..15dc5e2234 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.25.6 + +### Patch Changes + +- fix: ignore generic type arguments while creating AST ([#15659](https://github.com/sveltejs/svelte/pull/15659)) + +- fix: better consider component and its snippets during css pruning ([#15630](https://github.com/sveltejs/svelte/pull/15630)) + ## 5.25.5 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 1434bb9f06..eeb2211151 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.25.5", + "version": "5.25.6", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b07135b6fa..8bcf91ce39 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.25.5'; +export const VERSION = '5.25.6'; export const PUBLIC_VERSION = '5'; From 80557bbc1c8a94c43a95bfbdc51d37764d2065ea Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Tue, 1 Apr 2025 15:59:44 +0100 Subject: [PATCH 0306/1429] fix: ensure clearing of old values happens independent of root flushes (#15664) * fix: ensure clearing of old values happens indepedent of root flushes * lint * test --- .changeset/kind-elephants-behave.md | 5 +++++ packages/svelte/src/internal/client/runtime.js | 2 +- .../samples/onmount-prop-access/Component.svelte | 12 ++++++++++++ .../samples/onmount-prop-access/_config.js | 7 +++++++ .../samples/onmount-prop-access/main.svelte | 14 ++++++++++++++ 5 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 .changeset/kind-elephants-behave.md create mode 100644 packages/svelte/tests/runtime-runes/samples/onmount-prop-access/Component.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/onmount-prop-access/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/onmount-prop-access/main.svelte diff --git a/.changeset/kind-elephants-behave.md b/.changeset/kind-elephants-behave.md new file mode 100644 index 0000000000..f52dc1fd36 --- /dev/null +++ b/.changeset/kind-elephants-behave.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure clearing of old values happens independent of root flushes diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index a5d26412a4..a69181da98 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -692,6 +692,7 @@ function flush_queued_root_effects() { var collected_effects = process_effects(root_effects[i]); flush_queued_effects(collected_effects); } + old_values.clear(); } } finally { is_flushing = false; @@ -701,7 +702,6 @@ function flush_queued_root_effects() { if (DEV) { dev_effect_stack = []; } - old_values.clear(); } } diff --git a/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/Component.svelte b/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/Component.svelte new file mode 100644 index 0000000000..b5da702fa7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/Component.svelte @@ -0,0 +1,12 @@ +<script> + import { onMount } from 'svelte' + + let thing = $state(0) + + onMount(() => { + thing = 1; + return () => { + console.log(thing); + } + }) +</script> diff --git a/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/_config.js b/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/_config.js new file mode 100644 index 0000000000..0d24e265d3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + async test({ assert, logs }) { + assert.deepEqual(logs, [1]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/main.svelte b/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/main.svelte new file mode 100644 index 0000000000..92746760a4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/onmount-prop-access/main.svelte @@ -0,0 +1,14 @@ +<script> + import { onMount } from "svelte" + import Component from './Component.svelte'; + + let key = $state(0); + + onMount(() => { + key = 1; + }) +</script> + +{#key key} + <Component /> +{/key} From c544a5935131817123ffa6b405260f53b21ba6b3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 16:56:35 +0100 Subject: [PATCH 0307/1429] Version Packages (#15665) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/kind-elephants-behave.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/kind-elephants-behave.md diff --git a/.changeset/kind-elephants-behave.md b/.changeset/kind-elephants-behave.md deleted file mode 100644 index f52dc1fd36..0000000000 --- a/.changeset/kind-elephants-behave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure clearing of old values happens independent of root flushes diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 15dc5e2234..3ac52f5dc7 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.25.7 + +### Patch Changes + +- fix: ensure clearing of old values happens independent of root flushes ([#15664](https://github.com/sveltejs/svelte/pull/15664)) + ## 5.25.6 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index eeb2211151..8abad8a39b 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.25.6", + "version": "5.25.7", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 8bcf91ce39..2c39c2357f 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.25.6'; +export const VERSION = '5.25.7'; export const PUBLIC_VERSION = '5'; From 3d0bc3414920811a7e3fcf065d48b8e15b51bcdf Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Sun, 6 Apr 2025 12:32:03 +0100 Subject: [PATCH 0308/1429] fix: address untracked_writes memory leak (#15694) * fix-untracked-writes-leak * fix * fix * Update packages/svelte/src/internal/client/runtime.js Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- .changeset/pretty-dingos-unite.md | 5 +++++ packages/svelte/src/internal/client/runtime.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/pretty-dingos-unite.md diff --git a/.changeset/pretty-dingos-unite.md b/.changeset/pretty-dingos-unite.md new file mode 100644 index 0000000000..5703244670 --- /dev/null +++ b/.changeset/pretty-dingos-unite.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: address untracked_writes memory leak diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index a69181da98..c1c91f3551 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -476,7 +476,7 @@ export function update_reaction(reaction) { // we need to increment the read version to ensure that // any dependencies in this reaction aren't marked with // the same version - if (previous_reaction !== null) { + if (previous_reaction !== reaction) { read_version++; if (untracked_writes !== null) { From 7b850d3fba8a3edb1f7c028cb80efeae7932c7c2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 15:30:11 +0100 Subject: [PATCH 0309/1429] Version Packages (#15696) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/pretty-dingos-unite.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/pretty-dingos-unite.md diff --git a/.changeset/pretty-dingos-unite.md b/.changeset/pretty-dingos-unite.md deleted file mode 100644 index 5703244670..0000000000 --- a/.changeset/pretty-dingos-unite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: address untracked_writes memory leak diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 3ac52f5dc7..361de202b1 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.25.8 + +### Patch Changes + +- fix: address untracked_writes memory leak ([#15694](https://github.com/sveltejs/svelte/pull/15694)) + ## 5.25.7 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 8abad8a39b..8a225a798d 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.25.7", + "version": "5.25.8", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 2c39c2357f..2c8140e365 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.25.7'; +export const VERSION = '5.25.8'; export const PUBLIC_VERSION = '5'; From ab4e71a8099d1b7fb699c6ee5027e12313526812 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 15:47:27 -0400 Subject: [PATCH 0310/1429] chore(deps-dev): bump vite from 5.4.16 to 5.4.17 (#15692) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.16 to 5.4.17. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v5.4.17/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.4.17/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 5.4.17 dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- playgrounds/sandbox/package.json | 2 +- pnpm-lock.yaml | 212 +++++++++++++++---------------- 2 files changed, 107 insertions(+), 107 deletions(-) diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index cb44905309..d392349b60 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -18,7 +18,7 @@ "polka": "^1.0.0-next.25", "svelte": "workspace:*", "tinyglobby": "^0.2.12", - "vite": "^5.4.16", + "vite": "^5.4.17", "vite-plugin-inspect": "^0.8.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e1f276d23f..f0e66d1681 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,7 +152,7 @@ importers: devDependencies: '@sveltejs/vite-plugin-svelte': specifier: ^4.0.0-next.6 - version: 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) polka: specifier: ^1.0.0-next.25 version: 1.0.0-next.25 @@ -163,11 +163,11 @@ importers: specifier: ^0.2.12 version: 0.2.12 vite: - specifier: ^5.4.16 - version: 5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + specifier: ^5.4.17 + version: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vite-plugin-inspect: specifier: ^0.8.4 - version: 0.8.4(rollup@4.38.0)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 0.8.4(rollup@4.39.0)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) packages: @@ -551,8 +551,8 @@ packages: cpu: [arm] os: [android] - '@rollup/rollup-android-arm-eabi@4.38.0': - resolution: {integrity: sha512-ldomqc4/jDZu/xpYU+aRxo3V4mGCV9HeTgUBANI3oIQMOL+SsxB+S2lxMpkFp5UamSS3XuTMQVbsS24R4J4Qjg==} + '@rollup/rollup-android-arm-eabi@4.39.0': + resolution: {integrity: sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==} cpu: [arm] os: [android] @@ -561,8 +561,8 @@ packages: cpu: [arm64] os: [android] - '@rollup/rollup-android-arm64@4.38.0': - resolution: {integrity: sha512-VUsgcy4GhhT7rokwzYQP+aV9XnSLkkhlEJ0St8pbasuWO/vwphhZQxYEKUP3ayeCYLhk6gEtacRpYP/cj3GjyQ==} + '@rollup/rollup-android-arm64@4.39.0': + resolution: {integrity: sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==} cpu: [arm64] os: [android] @@ -571,8 +571,8 @@ packages: cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-arm64@4.38.0': - resolution: {integrity: sha512-buA17AYXlW9Rn091sWMq1xGUvWQFOH4N1rqUxGJtEQzhChxWjldGCCup7r/wUnaI6Au8sKXpoh0xg58a7cgcpg==} + '@rollup/rollup-darwin-arm64@4.39.0': + resolution: {integrity: sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==} cpu: [arm64] os: [darwin] @@ -581,18 +581,18 @@ packages: cpu: [x64] os: [darwin] - '@rollup/rollup-darwin-x64@4.38.0': - resolution: {integrity: sha512-Mgcmc78AjunP1SKXl624vVBOF2bzwNWFPMP4fpOu05vS0amnLcX8gHIge7q/lDAHy3T2HeR0TqrriZDQS2Woeg==} + '@rollup/rollup-darwin-x64@4.39.0': + resolution: {integrity: sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.38.0': - resolution: {integrity: sha512-zzJACgjLbQTsscxWqvrEQAEh28hqhebpRz5q/uUd1T7VTwUNZ4VIXQt5hE7ncs0GrF+s7d3S4on4TiXUY8KoQA==} + '@rollup/rollup-freebsd-arm64@4.39.0': + resolution: {integrity: sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.38.0': - resolution: {integrity: sha512-hCY/KAeYMCyDpEE4pTETam0XZS4/5GXzlLgpi5f0IaPExw9kuB+PDTOTLuPtM10TlRG0U9OSmXJ+Wq9J39LvAg==} + '@rollup/rollup-freebsd-x64@4.39.0': + resolution: {integrity: sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==} cpu: [x64] os: [freebsd] @@ -601,8 +601,8 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-gnueabihf@4.38.0': - resolution: {integrity: sha512-mimPH43mHl4JdOTD7bUMFhBdrg6f9HzMTOEnzRmXbOZqjijCw8LA5z8uL6LCjxSa67H2xiLFvvO67PT05PRKGg==} + '@rollup/rollup-linux-arm-gnueabihf@4.39.0': + resolution: {integrity: sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==} cpu: [arm] os: [linux] @@ -611,8 +611,8 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.38.0': - resolution: {integrity: sha512-tPiJtiOoNuIH8XGG8sWoMMkAMm98PUwlriOFCCbZGc9WCax+GLeVRhmaxjJtz6WxrPKACgrwoZ5ia/uapq3ZVg==} + '@rollup/rollup-linux-arm-musleabihf@4.39.0': + resolution: {integrity: sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==} cpu: [arm] os: [linux] @@ -621,8 +621,8 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.38.0': - resolution: {integrity: sha512-wZco59rIVuB0tjQS0CSHTTUcEde+pXQWugZVxWaQFdQQ1VYub/sTrNdY76D1MKdN2NB48JDuGABP6o6fqos8mA==} + '@rollup/rollup-linux-arm64-gnu@4.39.0': + resolution: {integrity: sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==} cpu: [arm64] os: [linux] @@ -631,13 +631,13 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.38.0': - resolution: {integrity: sha512-fQgqwKmW0REM4LomQ+87PP8w8xvU9LZfeLBKybeli+0yHT7VKILINzFEuggvnV9M3x1Ed4gUBmGUzCo/ikmFbQ==} + '@rollup/rollup-linux-arm64-musl@4.39.0': + resolution: {integrity: sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.38.0': - resolution: {integrity: sha512-hz5oqQLXTB3SbXpfkKHKXLdIp02/w3M+ajp8p4yWOWwQRtHWiEOCKtc9U+YXahrwdk+3qHdFMDWR5k+4dIlddg==} + '@rollup/rollup-linux-loongarch64-gnu@4.39.0': + resolution: {integrity: sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==} cpu: [loong64] os: [linux] @@ -646,8 +646,8 @@ packages: cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.38.0': - resolution: {integrity: sha512-NXqygK/dTSibQ+0pzxsL3r4Xl8oPqVoWbZV9niqOnIHV/J92fe65pOir0xjkUZDRSPyFRvu+4YOpJF9BZHQImw==} + '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': + resolution: {integrity: sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==} cpu: [ppc64] os: [linux] @@ -656,13 +656,13 @@ packages: cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.38.0': - resolution: {integrity: sha512-GEAIabR1uFyvf/jW/5jfu8gjM06/4kZ1W+j1nWTSSB3w6moZEBm7iBtzwQ3a1Pxos2F7Gz+58aVEnZHU295QTg==} + '@rollup/rollup-linux-riscv64-gnu@4.39.0': + resolution: {integrity: sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.38.0': - resolution: {integrity: sha512-9EYTX+Gus2EGPbfs+fh7l95wVADtSQyYw4DfSBcYdUEAmP2lqSZY0Y17yX/3m5VKGGJ4UmIH5LHLkMJft3bYoA==} + '@rollup/rollup-linux-riscv64-musl@4.39.0': + resolution: {integrity: sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==} cpu: [riscv64] os: [linux] @@ -671,8 +671,8 @@ packages: cpu: [s390x] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.38.0': - resolution: {integrity: sha512-Mpp6+Z5VhB9VDk7RwZXoG2qMdERm3Jw07RNlXHE0bOnEeX+l7Fy4bg+NxfyN15ruuY3/7Vrbpm75J9QHFqj5+Q==} + '@rollup/rollup-linux-s390x-gnu@4.39.0': + resolution: {integrity: sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==} cpu: [s390x] os: [linux] @@ -681,8 +681,8 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.38.0': - resolution: {integrity: sha512-vPvNgFlZRAgO7rwncMeE0+8c4Hmc+qixnp00/Uv3ht2x7KYrJ6ERVd3/R0nUtlE6/hu7/HiiNHJ/rP6knRFt1w==} + '@rollup/rollup-linux-x64-gnu@4.39.0': + resolution: {integrity: sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==} cpu: [x64] os: [linux] @@ -691,8 +691,8 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.38.0': - resolution: {integrity: sha512-q5Zv+goWvQUGCaL7fU8NuTw8aydIL/C9abAVGCzRReuj5h30TPx4LumBtAidrVOtXnlB+RZkBtExMsfqkMfb8g==} + '@rollup/rollup-linux-x64-musl@4.39.0': + resolution: {integrity: sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==} cpu: [x64] os: [linux] @@ -701,8 +701,8 @@ packages: cpu: [arm64] os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.38.0': - resolution: {integrity: sha512-u/Jbm1BU89Vftqyqbmxdq14nBaQjQX1HhmsdBWqSdGClNaKwhjsg5TpW+5Ibs1mb8Es9wJiMdl86BcmtUVXNZg==} + '@rollup/rollup-win32-arm64-msvc@4.39.0': + resolution: {integrity: sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==} cpu: [arm64] os: [win32] @@ -711,8 +711,8 @@ packages: cpu: [ia32] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.38.0': - resolution: {integrity: sha512-mqu4PzTrlpNHHbu5qleGvXJoGgHpChBlrBx/mEhTPpnAL1ZAYFlvHD7rLK839LLKQzqEQMFJfGrrOHItN4ZQqA==} + '@rollup/rollup-win32-ia32-msvc@4.39.0': + resolution: {integrity: sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==} cpu: [ia32] os: [win32] @@ -721,8 +721,8 @@ packages: cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.38.0': - resolution: {integrity: sha512-jjqy3uWlecfB98Psxb5cD6Fny9Fupv9LrDSPTQZUROqjvZmcCqNu4UMl7qqhlUUGpwiAkotj6GYu4SZdcr/nLw==} + '@rollup/rollup-win32-x64-msvc@4.39.0': + resolution: {integrity: sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==} cpu: [x64] os: [win32] @@ -1974,8 +1974,8 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rollup@4.38.0: - resolution: {integrity: sha512-5SsIRtJy9bf1ErAOiFMFzl64Ex9X5V7bnJ+WlFMb+zmP459OSWCEG7b0ERZ+PEU7xPt4OG3RHbrp1LJlXxYTrw==} + rollup@4.39.0: + resolution: {integrity: sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2310,8 +2310,8 @@ packages: terser: optional: true - vite@5.4.16: - resolution: {integrity: sha512-Y5gnfp4NemVfgOTDQAunSD4346fal44L9mszGGY/e+qxsRT5y1sMlS/8tiQ8AFAp+MFgYNSINdfEchJiPm41vQ==} + vite@5.4.17: + resolution: {integrity: sha512-5+VqZryDj4wgCs55o9Lp+p8GE78TLVg0lasCH5xFZ4jacZjtqZa6JUw9/p0WeAojaOfncSM6v77InkFPGnvPvg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -2860,120 +2860,120 @@ snapshots: optionalDependencies: rollup: 4.22.4 - '@rollup/pluginutils@5.1.0(rollup@4.38.0)': + '@rollup/pluginutils@5.1.0(rollup@4.39.0)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 2.3.1 optionalDependencies: - rollup: 4.38.0 + rollup: 4.39.0 '@rollup/rollup-android-arm-eabi@4.22.4': optional: true - '@rollup/rollup-android-arm-eabi@4.38.0': + '@rollup/rollup-android-arm-eabi@4.39.0': optional: true '@rollup/rollup-android-arm64@4.22.4': optional: true - '@rollup/rollup-android-arm64@4.38.0': + '@rollup/rollup-android-arm64@4.39.0': optional: true '@rollup/rollup-darwin-arm64@4.22.4': optional: true - '@rollup/rollup-darwin-arm64@4.38.0': + '@rollup/rollup-darwin-arm64@4.39.0': optional: true '@rollup/rollup-darwin-x64@4.22.4': optional: true - '@rollup/rollup-darwin-x64@4.38.0': + '@rollup/rollup-darwin-x64@4.39.0': optional: true - '@rollup/rollup-freebsd-arm64@4.38.0': + '@rollup/rollup-freebsd-arm64@4.39.0': optional: true - '@rollup/rollup-freebsd-x64@4.38.0': + '@rollup/rollup-freebsd-x64@4.39.0': optional: true '@rollup/rollup-linux-arm-gnueabihf@4.22.4': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.38.0': + '@rollup/rollup-linux-arm-gnueabihf@4.39.0': optional: true '@rollup/rollup-linux-arm-musleabihf@4.22.4': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.38.0': + '@rollup/rollup-linux-arm-musleabihf@4.39.0': optional: true '@rollup/rollup-linux-arm64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-arm64-gnu@4.38.0': + '@rollup/rollup-linux-arm64-gnu@4.39.0': optional: true '@rollup/rollup-linux-arm64-musl@4.22.4': optional: true - '@rollup/rollup-linux-arm64-musl@4.38.0': + '@rollup/rollup-linux-arm64-musl@4.39.0': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.38.0': + '@rollup/rollup-linux-loongarch64-gnu@4.39.0': optional: true '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.38.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': optional: true '@rollup/rollup-linux-riscv64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.38.0': + '@rollup/rollup-linux-riscv64-gnu@4.39.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.38.0': + '@rollup/rollup-linux-riscv64-musl@4.39.0': optional: true '@rollup/rollup-linux-s390x-gnu@4.22.4': optional: true - '@rollup/rollup-linux-s390x-gnu@4.38.0': + '@rollup/rollup-linux-s390x-gnu@4.39.0': optional: true '@rollup/rollup-linux-x64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-x64-gnu@4.38.0': + '@rollup/rollup-linux-x64-gnu@4.39.0': optional: true '@rollup/rollup-linux-x64-musl@4.22.4': optional: true - '@rollup/rollup-linux-x64-musl@4.38.0': + '@rollup/rollup-linux-x64-musl@4.39.0': optional: true '@rollup/rollup-win32-arm64-msvc@4.22.4': optional: true - '@rollup/rollup-win32-arm64-msvc@4.38.0': + '@rollup/rollup-win32-arm64-msvc@4.39.0': optional: true '@rollup/rollup-win32-ia32-msvc@4.22.4': optional: true - '@rollup/rollup-win32-ia32-msvc@4.38.0': + '@rollup/rollup-win32-ia32-msvc@4.39.0': optional: true '@rollup/rollup-win32-x64-msvc@4.22.4': optional: true - '@rollup/rollup-win32-x64-msvc@4.38.0': + '@rollup/rollup-win32-x64-msvc@4.39.0': optional: true '@stylistic/eslint-plugin-js@1.8.0(eslint@9.9.1)': @@ -3000,25 +3000,25 @@ snapshots: typescript: 5.5.4 typescript-eslint: 8.26.0(eslint@9.9.1)(typescript@5.5.4) - '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.4.0 svelte: link:packages/svelte - vite: 5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 svelte: link:packages/svelte - vite: 5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) - vitefu: 0.2.5(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + vite: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vitefu: 0.2.5(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) transitivePeerDependencies: - supports-color @@ -4292,30 +4292,30 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.22.4 fsevents: 2.3.3 - rollup@4.38.0: + rollup@4.39.0: dependencies: '@types/estree': 1.0.7 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.38.0 - '@rollup/rollup-android-arm64': 4.38.0 - '@rollup/rollup-darwin-arm64': 4.38.0 - '@rollup/rollup-darwin-x64': 4.38.0 - '@rollup/rollup-freebsd-arm64': 4.38.0 - '@rollup/rollup-freebsd-x64': 4.38.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.38.0 - '@rollup/rollup-linux-arm-musleabihf': 4.38.0 - '@rollup/rollup-linux-arm64-gnu': 4.38.0 - '@rollup/rollup-linux-arm64-musl': 4.38.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.38.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.38.0 - '@rollup/rollup-linux-riscv64-gnu': 4.38.0 - '@rollup/rollup-linux-riscv64-musl': 4.38.0 - '@rollup/rollup-linux-s390x-gnu': 4.38.0 - '@rollup/rollup-linux-x64-gnu': 4.38.0 - '@rollup/rollup-linux-x64-musl': 4.38.0 - '@rollup/rollup-win32-arm64-msvc': 4.38.0 - '@rollup/rollup-win32-ia32-msvc': 4.38.0 - '@rollup/rollup-win32-x64-msvc': 4.38.0 + '@rollup/rollup-android-arm-eabi': 4.39.0 + '@rollup/rollup-android-arm64': 4.39.0 + '@rollup/rollup-darwin-arm64': 4.39.0 + '@rollup/rollup-darwin-x64': 4.39.0 + '@rollup/rollup-freebsd-arm64': 4.39.0 + '@rollup/rollup-freebsd-x64': 4.39.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.39.0 + '@rollup/rollup-linux-arm-musleabihf': 4.39.0 + '@rollup/rollup-linux-arm64-gnu': 4.39.0 + '@rollup/rollup-linux-arm64-musl': 4.39.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.39.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.39.0 + '@rollup/rollup-linux-riscv64-gnu': 4.39.0 + '@rollup/rollup-linux-riscv64-musl': 4.39.0 + '@rollup/rollup-linux-s390x-gnu': 4.39.0 + '@rollup/rollup-linux-x64-gnu': 4.39.0 + '@rollup/rollup-linux-x64-musl': 4.39.0 + '@rollup/rollup-win32-arm64-msvc': 4.39.0 + '@rollup/rollup-win32-ia32-msvc': 4.39.0 + '@rollup/rollup-win32-x64-msvc': 4.39.0 fsevents: 2.3.3 rrweb-cssom@0.7.1: {} @@ -4562,7 +4562,7 @@ snapshots: debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 1.1.2 - vite: 5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - '@types/node' - less @@ -4574,10 +4574,10 @@ snapshots: - supports-color - terser - vite-plugin-inspect@0.8.4(rollup@4.38.0)(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vite-plugin-inspect@0.8.4(rollup@4.39.0)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): dependencies: '@antfu/utils': 0.7.8 - '@rollup/pluginutils': 5.1.0(rollup@4.38.0) + '@rollup/pluginutils': 5.1.0(rollup@4.39.0) debug: 4.4.0 error-stack-parser-es: 0.1.1 fs-extra: 11.2.0 @@ -4585,7 +4585,7 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 2.0.4 - vite: 5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - rollup - supports-color @@ -4602,11 +4602,11 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): + vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: esbuild: 0.21.5 postcss: 8.5.3 - rollup: 4.38.0 + rollup: 4.39.0 optionalDependencies: '@types/node': 20.12.7 fsevents: 2.3.3 @@ -4614,9 +4614,9 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vitefu@0.2.5(vite@5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vitefu@0.2.5(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): optionalDependencies: - vite: 5.4.16(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vitest@2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: From 8e21c41c27fd062fed2b9d53ab3f19ac1705142f Mon Sep 17 00:00:00 2001 From: James Scott-Brown <James.Scott-Brown@london.gov.uk> Date: Mon, 7 Apr 2025 21:01:48 +0100 Subject: [PATCH 0311/1429] Link from $host directive docs to custom elements docs (#15686) --- documentation/docs/02-runes/08-$host.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/02-runes/08-$host.md b/documentation/docs/02-runes/08-$host.md index 7b5e041e5e..ba6f0a5b5b 100644 --- a/documentation/docs/02-runes/08-$host.md +++ b/documentation/docs/02-runes/08-$host.md @@ -2,7 +2,7 @@ title: $host --- -When compiling a component as a custom element, the `$host` rune provides access to the host element, allowing you to (for example) dispatch custom events ([demo](/playground/untitled#H4sIAAAAAAAAE41Ry2rDMBD8FSECtqkTt1fHFpSSL-ix7sFRNkTEXglrnTYY_3uRlDgxTaEHIfYxs7szA9-rBizPPwZOZwM89wmecqxbF70as7InaMjltrWFR3mpkQDJ8pwXVnbKkKiwItUa3RGLVtk7gTHQXRDR2lXda4CY1D0SK9nCUk0QPyfrCovsRoNFe17aQOAwGncgO2gBqRzihJXiQrEs2csYOhQ-7HgKHaLIbpRhhBG-I2eD_8ciM4KnnOCbeE5dD2P6h0Dz0-Yi_arNhPLJXBtSGi2TvSXdbpqwdsXvjuYsC1veabvvUTog2ylrapKH2G2XsMFLS4uDthQnq2t1cwKkGOGLvYU5PvaQxLsxOkPmsm97Io1Mo2yUPF6VnOZFkw1RMoopKLKAE_9gmGxyDFMwMcwN-Bx_ABXQWmOtAgAA)): +When compiling a component as a [custom element](custom-elements), the `$host` rune provides access to the host element, allowing you to (for example) dispatch custom events ([demo](/playground/untitled#H4sIAAAAAAAAE41Ry2rDMBD8FSECtqkTt1fHFpSSL-ix7sFRNkTEXglrnTYY_3uRlDgxTaEHIfYxs7szA9-rBizPPwZOZwM89wmecqxbF70as7InaMjltrWFR3mpkQDJ8pwXVnbKkKiwItUa3RGLVtk7gTHQXRDR2lXda4CY1D0SK9nCUk0QPyfrCovsRoNFe17aQOAwGncgO2gBqRzihJXiQrEs2csYOhQ-7HgKHaLIbpRhhBG-I2eD_8ciM4KnnOCbeE5dD2P6h0Dz0-Yi_arNhPLJXBtSGi2TvSXdbpqwdsXvjuYsC1veabvvUTog2ylrapKH2G2XsMFLS4uDthQnq2t1cwKkGOGLvYU5PvaQxLsxOkPmsm97Io1Mo2yUPF6VnOZFkw1RMoopKLKAE_9gmGxyDFMwMcwN-Bx_ABXQWmOtAgAA)): <!-- prettier-ignore --> ```svelte From 6a668c4daf553211a2315f6ef0bfd84c8ef344f0 Mon Sep 17 00:00:00 2001 From: Simon Lund <41208671+simon-lund@users.noreply.github.com> Date: Mon, 7 Apr 2025 22:33:23 +0200 Subject: [PATCH 0312/1429] docs:Update 02-context.md (#15700) * Update 02-context.md use `const` for key variable and assign it a symbol instead of empty of object. * Update documentation/docs/06-runtime/02-context.md --------- Co-authored-by: Rich Harris <hello@rich-harris.dev> --- documentation/docs/06-runtime/02-context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/06-runtime/02-context.md b/documentation/docs/06-runtime/02-context.md index b698323a04..4204bcfe6d 100644 --- a/documentation/docs/06-runtime/02-context.md +++ b/documentation/docs/06-runtime/02-context.md @@ -94,7 +94,7 @@ interface User {} // ---cut--- import { getContext, setContext } from 'svelte'; -let key = {}; +const key = {}; /** @param {User} user */ export function setUserContext(user) { From caf62ee6872fc0c9944aea98585597d2d1712e21 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Tue, 8 Apr 2025 07:20:36 -0400 Subject: [PATCH 0313/1429] fix: allow `$.state` and `$.derived` to be treeshaken (#15702) --- .changeset/clever-news-enjoy.md | 5 +++++ packages/svelte/src/internal/client/reactivity/deriveds.js | 1 + packages/svelte/src/internal/client/reactivity/sources.js | 1 + 3 files changed, 7 insertions(+) create mode 100644 .changeset/clever-news-enjoy.md diff --git a/.changeset/clever-news-enjoy.md b/.changeset/clever-news-enjoy.md new file mode 100644 index 0000000000..2ff3dcbe56 --- /dev/null +++ b/.changeset/clever-news-enjoy.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow `$.state` and `$.derived` to be treeshaken diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index cd7bbba02f..86171c2b2d 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -67,6 +67,7 @@ export function derived(fn) { * @param {() => V} fn * @returns {Derived<V>} */ +/*#__NO_SIDE_EFFECTS__*/ export function user_derived(fn) { const d = derived(fn); diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index e4834902fe..ceab59b37c 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -78,6 +78,7 @@ export function source(v, stack) { * @param {V} v * @param {Error | null} [stack] */ +/*#__NO_SIDE_EFFECTS__*/ export function state(v, stack) { const s = source(v, stack); From 98d14ece6688280f98b185cc9e69fbb309e1afa8 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 8 Apr 2025 22:41:54 +0200 Subject: [PATCH 0314/1429] fix: rework binding ownership validation (#15678) * remove old validation * fix: rework binding ownership validation Previously we were doing stack-based retrieval of the owner, which while catching more cases was costly (performance-wise) and prone to errors (as shown by many issues over the months). This drastically simplifies the ownership validation - we now only do simple static analysis to check which props are mutated and wrap them with runtime checks to see if a binding was established. Besides making the implementation simpler and more performant, this also follows an insight we had over the months: Most people don't really know what to do with this warning when it's shown beyond very simple cases. Either it's not actionable because they don't really know how to fix it or they question if they should at all (in some cases rightfully so). Now that the warning is only shown in simple and easy-to-reason-about cases, it has a much better signal-to-noise-ratio and will hopefully guide people in the right direction early on (learn from the obvious cases to not write spaghetti code in more complex cases). closes #15532 closes #15210 closes #14893 closes #13607 closes #13139 closes #11861 * remove some now obsolete tests * fix * better warnings now that we have more info * fix * hoist * we only care about mutation, not reassignment * tidy * handle prop aliases * mutation validation is only tangentially linked to context requirement * no need for two vars, one will do * update warning, include mutation location * tweak --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/sweet-ants-care.md | 5 + .../.generated/client-warnings.md | 8 +- .../messages/client-warnings/warnings.md | 6 +- .../src/compiler/phases/2-analyze/index.js | 1 + .../3-transform/client/transform-client.js | 6 + .../phases/3-transform/client/utils.js | 33 ++- .../client/visitors/AssignmentExpression.js | 7 +- .../3-transform/client/visitors/ClassBody.js | 27 -- .../client/visitors/UpdateExpression.js | 6 +- .../client/visitors/shared/component.js | 34 ++- .../client/visitors/shared/utils.js | 63 ++++- .../svelte/src/compiler/phases/types.d.ts | 1 + .../svelte/src/internal/client/constants.js | 1 - .../svelte/src/internal/client/context.js | 10 - .../src/internal/client/dev/ownership.js | 247 ++++-------------- packages/svelte/src/internal/client/index.js | 10 +- packages/svelte/src/internal/client/proxy.js | 81 +----- .../src/internal/client/reactivity/sources.js | 2 +- .../svelte/src/internal/client/types.d.ts | 8 - .../svelte/src/internal/client/warnings.js | 19 +- packages/svelte/src/utils.js | 6 +- .../non-local-mutation-discouraged/_config.js | 2 +- .../non-local-mutation-global-2/_config.js | 11 - .../non-local-mutation-global-2/child.svelte | 18 -- .../non-local-mutation-global-2/main.svelte | 5 - .../_config.js | 6 +- .../main.svelte | 9 + .../sub.svelte | 0 .../_config.js | 33 +-- .../main.svelte | 11 +- .../state.svelte.js | 15 +- .../sub.svelte | 0 .../Child.svelte | 0 .../_config.js | 33 +-- .../main.svelte | 14 +- .../_config.js | 37 --- .../main.svelte | 11 - .../state.svelte.js | 13 - .../sub.svelte | 8 - .../_config.js | 24 -- .../main.svelte | 8 - .../state.svelte.js | 14 - .../Child.svelte | 9 - .../_config.js | 37 --- .../main.svelte | 17 -- .../_config.js | 24 -- .../main.svelte | 13 - .../Counter.svelte | 7 - .../main.svelte | 9 - .../state.svelte.js | 3 - .../_config.js | 2 +- .../_config.js | 2 +- .../_config.js | 1 - .../CounterBinding.svelte | 7 - .../CounterContext.svelte | 13 - .../_config.js | 34 --- .../main.svelte | 46 ---- 57 files changed, 271 insertions(+), 806 deletions(-) create mode 100644 .changeset/sweet-ants-care.md delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-global-2/_config.js delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-global-2/child.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-global-2/main.svelte rename packages/svelte/tests/runtime-runes/samples/{non-local-mutation-inherited-owner => non-local-mutation-inherited-owner-1}/_config.js (74%) create mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-1/main.svelte rename packages/svelte/tests/runtime-runes/samples/{non-local-mutation-inherited-owner-3 => non-local-mutation-inherited-owner-1}/sub.svelte (100%) rename packages/svelte/tests/runtime-runes/samples/{non-local-mutation-inherited-owner-5 => non-local-mutation-inherited-owner-2}/sub.svelte (100%) rename packages/svelte/tests/runtime-runes/samples/{non-local-mutation-inherited-owner-7 => non-local-mutation-inherited-owner-3}/Child.svelte (100%) delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/_config.js delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/main.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/state.svelte.js delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/sub.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/_config.js delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/main.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/state.svelte.js delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/Child.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/_config.js delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/main.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/_config.js delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/main.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/Counter.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/main.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/state.svelte.js delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterBinding.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterContext.svelte delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/_config.js delete mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/main.svelte diff --git a/.changeset/sweet-ants-care.md b/.changeset/sweet-ants-care.md new file mode 100644 index 0000000000..b4805626ab --- /dev/null +++ b/.changeset/sweet-ants-care.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: rework binding ownership validation diff --git a/documentation/docs/98-reference/.generated/client-warnings.md b/documentation/docs/98-reference/.generated/client-warnings.md index 284e9a7c3e..77d1df4cdd 100644 --- a/documentation/docs/98-reference/.generated/client-warnings.md +++ b/documentation/docs/98-reference/.generated/client-warnings.md @@ -161,7 +161,7 @@ Tried to unmount a component that was not mounted ### ownership_invalid_binding ``` -%parent% passed a value to %child% with `bind:`, but the value is owned by %owner%. Consider creating a binding between %owner% and %parent% +%parent% passed property `%prop%` to %child% with `bind:`, but its parent component %owner% did not declare `%prop%` as a binding. Consider creating a binding between %owner% and %parent% (e.g. `bind:%prop%={...}` instead of `%prop%={...}`) ``` Consider three components `GrandParent`, `Parent` and `Child`. If you do `<GrandParent bind:value>`, inside `GrandParent` pass on the variable via `<Parent {value} />` (note the missing `bind:`) and then do `<Child bind:value>` inside `Parent`, this warning is thrown. @@ -171,11 +171,7 @@ To fix it, `bind:` to the value instead of just passing a property (i.e. in this ### ownership_invalid_mutation ``` -Mutating a value outside the component that created it is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead -``` - -``` -%component% mutated a value owned by %owner%. This is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead +Mutating unbound props (`%name%`, at %location%) is strongly discouraged. Consider using `bind:%prop%={...}` in %parent% (or using a callback) instead ``` Consider the following code: diff --git a/packages/svelte/messages/client-warnings/warnings.md b/packages/svelte/messages/client-warnings/warnings.md index 943cf6f01f..f8e9ebd8a0 100644 --- a/packages/svelte/messages/client-warnings/warnings.md +++ b/packages/svelte/messages/client-warnings/warnings.md @@ -132,7 +132,7 @@ During development, this error is often preceeded by a `console.error` detailing ## ownership_invalid_binding -> %parent% passed a value to %child% with `bind:`, but the value is owned by %owner%. Consider creating a binding between %owner% and %parent% +> %parent% passed property `%prop%` to %child% with `bind:`, but its parent component %owner% did not declare `%prop%` as a binding. Consider creating a binding between %owner% and %parent% (e.g. `bind:%prop%={...}` instead of `%prop%={...}`) Consider three components `GrandParent`, `Parent` and `Child`. If you do `<GrandParent bind:value>`, inside `GrandParent` pass on the variable via `<Parent {value} />` (note the missing `bind:`) and then do `<Child bind:value>` inside `Parent`, this warning is thrown. @@ -140,9 +140,7 @@ To fix it, `bind:` to the value instead of just passing a property (i.e. in this ## ownership_invalid_mutation -> Mutating a value outside the component that created it is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead - -> %component% mutated a value owned by %owner%. This is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead +> Mutating unbound props (`%name%`, at %location%) is strongly discouraged. Consider using `bind:%prop%={...}` in %parent% (or using a callback) instead Consider the following code: diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 1f636c32df..c62fb03e8f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -432,6 +432,7 @@ export function analyze_component(root, source, options) { uses_component_bindings: false, uses_render_tags: false, needs_context: false, + needs_mutation_validation: false, needs_props: false, event_directive_node: null, uses_event_attributes: false, diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 0bdfbae746..38fcee8d6f 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -393,6 +393,12 @@ export function client_component(analysis, options) { ); } + if (analysis.needs_mutation_validation) { + component_block.body.unshift( + b.var('$$ownership_validator', b.call('$.create_ownership_validator', b.id('$$props'))) + ); + } + const should_inject_context = dev || analysis.needs_context || diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index 28e3fabb19..a37ecd31cc 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -1,10 +1,10 @@ -/** @import { ArrowFunctionExpression, Expression, FunctionDeclaration, FunctionExpression, Identifier, Pattern, PrivateIdentifier, Statement } from 'estree' */ -/** @import { AST, Binding } from '#compiler' */ +/** @import { ArrowFunctionExpression, AssignmentExpression, Expression, FunctionDeclaration, FunctionExpression, Identifier, Node, Pattern, UpdateExpression } from 'estree' */ +/** @import { Binding } from '#compiler' */ /** @import { ClientTransformState, ComponentClientTransformState, ComponentContext } from './types.js' */ /** @import { Analysis } from '../../types.js' */ /** @import { Scope } from '../../scope.js' */ import * as b from '../../../utils/builders.js'; -import { extract_identifiers, is_simple_expression } from '../../../utils/ast.js'; +import { is_simple_expression } from '../../../utils/ast.js'; import { PROPS_IS_LAZY_INITIAL, PROPS_IS_IMMUTABLE, @@ -13,7 +13,8 @@ import { PROPS_IS_BINDABLE } from '../../../../constants.js'; import { dev } from '../../../state.js'; -import { get_value } from './visitors/shared/declarations.js'; +import { walk } from 'zimmerframe'; +import { validate_mutation } from './visitors/shared/utils.js'; /** * @param {Binding} binding @@ -110,6 +111,30 @@ function get_hoisted_params(node, context) { } } } + + if (dev) { + // this is a little hacky, but necessary for ownership validation + // to work inside hoisted event handlers + + /** + * @param {AssignmentExpression | UpdateExpression} node + * @param {{ next: () => void, stop: () => void }} context + */ + function visit(node, { next, stop }) { + if (validate_mutation(node, /** @type {any} */ (context), node) !== node) { + params.push(b.id('$$ownership_validator')); + stop(); + } else { + next(); + } + } + + walk(/** @type {Node} */ (node), null, { + AssignmentExpression: visit, + UpdateExpression: visit + }); + } + return params; } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js index 850cd83b15..4baa1c8e6c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js @@ -7,9 +7,10 @@ import { get_attribute_expression, is_event_attribute } from '../../../../utils/ast.js'; -import { dev, is_ignored, locate_node } from '../../../../state.js'; +import { dev, locate_node } from '../../../../state.js'; import { should_proxy } from '../utils.js'; import { visit_assignment_expression } from '../../shared/assignments.js'; +import { validate_mutation } from './shared/utils.js'; /** * @param {AssignmentExpression} node @@ -20,9 +21,7 @@ export function AssignmentExpression(node, context) { visit_assignment_expression(node, context, build_assignment) ?? context.next() ); - return is_ignored(node, 'ownership_invalid_mutation') - ? b.call('$.skip_ownership_validation', b.thunk(expression)) - : expression; + return validate_mutation(node, context, expression); } /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js index 24c20d7f94..efc3c95c3c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js @@ -161,33 +161,6 @@ export function ClassBody(node, context) { body.push(/** @type {MethodDefinition} **/ (context.visit(definition, child_state))); } - if (dev && public_state.size > 0) { - // add an `[$.ADD_OWNER]` method so that a class with state fields can widen ownership - body.push( - b.method( - 'method', - b.id('$.ADD_OWNER'), - [b.id('owner')], - [ - b.stmt( - b.call( - '$.add_owner_to_class', - b.this, - b.id('owner'), - b.array( - Array.from(public_state).map(([name]) => - b.thunk(b.call('$.get', b.member(b.this, b.private_id(name)))) - ) - ), - is_ignored(node, 'ownership_invalid_binding') && b.true - ) - ) - ], - true - ) - ); - } - return { ...node, body }; } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/UpdateExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/UpdateExpression.js index 13c1b4bc51..63c03b0eb6 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/UpdateExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/UpdateExpression.js @@ -1,8 +1,8 @@ /** @import { AssignmentExpression, Expression, UpdateExpression } from 'estree' */ /** @import { Context } from '../types' */ -import { is_ignored } from '../../../../state.js'; import { object } from '../../../../utils/ast.js'; import * as b from '../../../../utils/builders.js'; +import { validate_mutation } from './shared/utils.js'; /** * @param {UpdateExpression} node @@ -51,7 +51,5 @@ export function UpdateExpression(node, context) { ); } - return is_ignored(node, 'ownership_invalid_mutation') - ? b.call('$.skip_ownership_validation', b.thunk(update)) - : update; + return validate_mutation(node, context, update); } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js index 2bae4486dc..2ea68e206e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js @@ -179,19 +179,29 @@ export function build_component(node, component_name, context, anchor = context. } else if (attribute.type === 'BindDirective') { const expression = /** @type {Expression} */ (context.visit(attribute.expression)); - if (dev && attribute.name !== 'this') { - binding_initializers.push( - b.stmt( - b.call( - b.id('$.add_owner_effect'), - expression.type === 'SequenceExpression' - ? expression.expressions[0] - : b.thunk(expression), - b.id(component_name), - is_ignored(node, 'ownership_invalid_binding') && b.true + if ( + dev && + attribute.name !== 'this' && + !is_ignored(node, 'ownership_invalid_binding') && + // bind:x={() => x.y, y => x.y = y} will be handled by the assignment expression binding validation + attribute.expression.type !== 'SequenceExpression' + ) { + const left = object(attribute.expression); + const binding = left && context.state.scope.get(left.name); + + if (binding?.kind === 'bindable_prop' || binding?.kind === 'prop') { + context.state.analysis.needs_mutation_validation = true; + binding_initializers.push( + b.stmt( + b.call( + '$$ownership_validator.binding', + b.literal(binding.node.name), + b.id(component_name), + b.thunk(expression) + ) ) - ) - ); + ); + } } if (expression.type === 'SequenceExpression') { diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index df6308d631..af6e56f70c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -1,13 +1,13 @@ -/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, SequenceExpression, Statement, Super } from 'estree' */ +/** @import { AssignmentExpression, Expression, ExpressionStatement, Identifier, MemberExpression, SequenceExpression, Literal, Super, UpdateExpression } from 'estree' */ /** @import { AST, ExpressionMetadata } from '#compiler' */ -/** @import { ComponentClientTransformState } from '../../types' */ +/** @import { ComponentClientTransformState, Context } from '../../types' */ import { walk } from 'zimmerframe'; import { object } from '../../../../../utils/ast.js'; import * as b from '../../../../../utils/builders.js'; import { sanitize_template_string } from '../../../../../utils/sanitize_template_string.js'; import { regex_is_valid_identifier } from '../../../../patterns.js'; import is_reference from 'is-reference'; -import { locator } from '../../../../../state.js'; +import { dev, is_ignored, locator } from '../../../../../state.js'; import { create_derived } from '../../utils.js'; /** @@ -295,3 +295,60 @@ export function validate_binding(state, binding, expression) { ) ); } + +/** + * In dev mode validate mutations to props + * @param {AssignmentExpression | UpdateExpression} node + * @param {Context} context + * @param {Expression} expression + */ +export function validate_mutation(node, context, expression) { + let left = /** @type {Expression | Super} */ ( + node.type === 'AssignmentExpression' ? node.left : node.argument + ); + + if (!dev || left.type !== 'MemberExpression' || is_ignored(node, 'ownership_invalid_mutation')) { + return expression; + } + + const name = object(left); + if (!name) return expression; + + const binding = context.state.scope.get(name.name); + if (binding?.kind !== 'prop' && binding?.kind !== 'bindable_prop') return expression; + + const state = /** @type {ComponentClientTransformState} */ (context.state); + state.analysis.needs_mutation_validation = true; + + /** @type {Array<Identifier | Literal>} */ + const path = []; + + while (left.type === 'MemberExpression') { + if (left.property.type === 'Literal') { + path.unshift(left.property); + } else if (left.property.type === 'Identifier') { + if (left.computed) { + path.unshift(left.property); + } else { + path.unshift(b.literal(left.property.name)); + } + } else { + return expression; + } + + left = left.object; + } + + path.unshift(b.literal(name.name)); + + const loc = locator(/** @type {number} */ (left.start)); + + return b.call( + '$$ownership_validator.mutation', + b.literal(binding.prop_alias), + b.array(path), + expression, + loc && b.literal(loc.line), + loc && b.literal(loc.column) + ); +} diff --git a/packages/svelte/src/compiler/phases/types.d.ts b/packages/svelte/src/compiler/phases/types.d.ts index abe2b115de..f09b881303 100644 --- a/packages/svelte/src/compiler/phases/types.d.ts +++ b/packages/svelte/src/compiler/phases/types.d.ts @@ -53,6 +53,7 @@ export interface ComponentAnalysis extends Analysis { uses_component_bindings: boolean; uses_render_tags: boolean; needs_context: boolean; + needs_mutation_validation: boolean; needs_props: boolean; /** Set to the first event directive (on:x) found on a DOM element in the code */ event_directive_node: AST.OnDirective | null; diff --git a/packages/svelte/src/internal/client/constants.js b/packages/svelte/src/internal/client/constants.js index 21377c1cc8..7e5196c606 100644 --- a/packages/svelte/src/internal/client/constants.js +++ b/packages/svelte/src/internal/client/constants.js @@ -23,6 +23,5 @@ export const EFFECT_HAS_DERIVED = 1 << 20; export const EFFECT_IS_UPDATING = 1 << 21; export const STATE_SYMBOL = Symbol('$state'); -export const STATE_SYMBOL_METADATA = Symbol('$state metadata'); export const LEGACY_PROPS = Symbol('legacy props'); export const LOADING_ATTR_SYMBOL = Symbol(''); diff --git a/packages/svelte/src/internal/client/context.js b/packages/svelte/src/internal/client/context.js index bfca9d5e6a..7a2fdd0edb 100644 --- a/packages/svelte/src/internal/client/context.js +++ b/packages/svelte/src/internal/client/context.js @@ -1,7 +1,6 @@ /** @import { ComponentContext } from '#client' */ import { DEV } from 'esm-env'; -import { add_owner } from './dev/ownership.js'; import { lifecycle_outside_component } from '../shared/errors.js'; import { source } from './reactivity/sources.js'; import { @@ -67,15 +66,6 @@ export function getContext(key) { */ export function setContext(key, context) { const context_map = get_or_init_context_map('setContext'); - - if (DEV) { - // When state is put into context, we treat as if it's global from now on. - // We do for performance reasons (it's for example very expensive to call - // getContext on a big object many times when part of a list component) - // and danger of false positives. - untrack(() => add_owner(context, null, true)); - } - context_map.set(key, context); return context; } diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index 62119b36db..6c40a744df 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -1,12 +1,11 @@ -/** @import { ProxyMetadata } from '#client' */ /** @typedef {{ file: string, line: number, column: number }} Location */ -import { STATE_SYMBOL_METADATA } from '../constants.js'; -import { render_effect, user_pre_effect } from '../reactivity/effects.js'; -import { dev_current_component_function } from '../context.js'; -import { get_prototype_of } from '../../shared/utils.js'; +import { get_descriptor } from '../../shared/utils.js'; +import { LEGACY_PROPS, STATE_SYMBOL } from '../constants.js'; +import { FILENAME } from '../../../constants.js'; +import { component_context } from '../context.js'; import * as w from '../warnings.js'; -import { FILENAME, UNINITIALIZED } from '../../../constants.js'; +import { sanitize_location } from '../../../utils.js'; /** @type {Record<string, Array<{ start: Location, end: Location, component: Function }>>} */ const boundaries = {}; @@ -71,8 +70,6 @@ export function get_component() { return null; } -export const ADD_OWNER = Symbol('ADD_OWNER'); - /** * Together with `mark_module_end`, this function establishes the boundaries of a `.svelte` file, * such that subsequent calls to `get_component` can tell us which component is responsible @@ -108,197 +105,69 @@ export function mark_module_end(component) { } /** - * @param {any} object - * @param {any | null} owner - * @param {boolean} [global] - * @param {boolean} [skip_warning] + * Sets up a validator that + * - traverses the path of a prop to find out if it is allowed to be mutated + * - checks that the binding chain is not interrupted + * @param {Record<string, any>} props */ -export function add_owner(object, owner, global = false, skip_warning = false) { - if (object && !global) { - const component = dev_current_component_function; - const metadata = object[STATE_SYMBOL_METADATA]; - if (metadata && !has_owner(metadata, component)) { - let original = get_owner(metadata); - - if (owner && owner[FILENAME] !== component[FILENAME] && !skip_warning) { - w.ownership_invalid_binding(component[FILENAME], owner[FILENAME], original[FILENAME]); +export function create_ownership_validator(props) { + const component = component_context?.function; + const parent = component_context?.p?.function; + + return { + /** + * @param {string} prop + * @param {any[]} path + * @param {any} result + * @param {number} line + * @param {number} column + */ + mutation: (prop, path, result, line, column) => { + const name = path[0]; + if (is_bound(props, name) || !parent) { + return result; } - } - } - add_owner_to_object(object, owner, new Set()); -} - -/** - * @param {() => unknown} get_object - * @param {any} Component - * @param {boolean} [skip_warning] - */ -export function add_owner_effect(get_object, Component, skip_warning = false) { - user_pre_effect(() => { - add_owner(get_object(), Component, false, skip_warning); - }); -} - -/** - * @param {any} _this - * @param {Function} owner - * @param {Array<() => any>} getters - * @param {boolean} skip_warning - */ -export function add_owner_to_class(_this, owner, getters, skip_warning) { - _this[ADD_OWNER].current ||= getters.map(() => UNINITIALIZED); - - for (let i = 0; i < getters.length; i += 1) { - const current = getters[i](); - // For performance reasons we only re-add the owner if the state has changed - if (current !== _this[ADD_OWNER][i]) { - _this[ADD_OWNER].current[i] = current; - add_owner(current, owner, false, skip_warning); - } - } -} - -/** - * @param {ProxyMetadata | null} from - * @param {ProxyMetadata} to - */ -export function widen_ownership(from, to) { - if (to.owners === null) { - return; - } - - while (from) { - if (from.owners === null) { - to.owners = null; - break; - } - - for (const owner of from.owners) { - to.owners.add(owner); - } - - from = from.parent; - } -} - -/** - * @param {any} object - * @param {Function | null} owner If `null`, then the object is globally owned and will not be checked - * @param {Set<any>} seen - */ -function add_owner_to_object(object, owner, seen) { - const metadata = /** @type {ProxyMetadata} */ (object?.[STATE_SYMBOL_METADATA]); + let value = props[name]; - if (metadata) { - // this is a state proxy, add owner directly, if not globally shared - if ('owners' in metadata && metadata.owners != null) { - if (owner) { - metadata.owners.add(owner); - } else { - metadata.owners = null; + for (let i = 1; i < path.length - 1; i++) { + if (!value?.[STATE_SYMBOL]) { + return result; + } + value = value[path[i]]; } - } - } else if (object && typeof object === 'object') { - if (seen.has(object)) return; - seen.add(object); - if (ADD_OWNER in object && object[ADD_OWNER]) { - // this is a class with state fields. we put this in a render effect - // so that if state is replaced (e.g. `instance.name = { first, last }`) - // the new state is also co-owned by the caller of `getContext` - render_effect(() => { - object[ADD_OWNER](owner); - }); - } else { - var proto = get_prototype_of(object); - if (proto === Object.prototype) { - // recurse until we find a state proxy - for (const key in object) { - if (Object.getOwnPropertyDescriptor(object, key)?.get) { - // Similar to the class case; the getter could update with a new state - let current = UNINITIALIZED; - render_effect(() => { - const next = object[key]; - if (current !== next) { - current = next; - add_owner_to_object(next, owner, seen); - } - }); - } else { - add_owner_to_object(object[key], owner, seen); - } - } - } else if (proto === Array.prototype) { - // recurse until we find a state proxy - for (let i = 0; i < object.length; i += 1) { - add_owner_to_object(object[i], owner, seen); - } + const location = sanitize_location(`${component[FILENAME]}:${line}:${column}`); + + w.ownership_invalid_mutation(name, location, prop, parent[FILENAME]); + + return result; + }, + /** + * @param {any} key + * @param {any} child_component + * @param {() => any} value + */ + binding: (key, child_component, value) => { + if (!is_bound(props, key) && parent && value()?.[STATE_SYMBOL]) { + w.ownership_invalid_binding( + component[FILENAME], + key, + child_component[FILENAME], + parent[FILENAME] + ); } } - } + }; } /** - * @param {ProxyMetadata} metadata - * @param {Function} component - * @returns {boolean} + * @param {Record<string, any>} props + * @param {string} prop_name */ -function has_owner(metadata, component) { - if (metadata.owners === null) { - return true; - } - - return ( - metadata.owners.has(component) || - // This helps avoid false positives when using HMR, where the component function is replaced - (FILENAME in component && - [...metadata.owners].some( - (owner) => /** @type {any} */ (owner)[FILENAME] === component[FILENAME] - )) || - (metadata.parent !== null && has_owner(metadata.parent, component)) - ); -} - -/** - * @param {ProxyMetadata} metadata - * @returns {any} - */ -function get_owner(metadata) { - return ( - metadata?.owners?.values().next().value ?? - get_owner(/** @type {ProxyMetadata} */ (metadata.parent)) - ); -} - -let skip = false; - -/** - * @param {() => any} fn - */ -export function skip_ownership_validation(fn) { - skip = true; - fn(); - skip = false; -} - -/** - * @param {ProxyMetadata} metadata - */ -export function check_ownership(metadata) { - if (skip) return; - - const component = get_component(); - - if (component && !has_owner(metadata, component)) { - let original = get_owner(metadata); - - // @ts-expect-error - if (original[FILENAME] !== component[FILENAME]) { - // @ts-expect-error - w.ownership_invalid_mutation(component[FILENAME], original[FILENAME]); - } else { - w.ownership_invalid_mutation(); - } - } +function is_bound(props, prop_name) { + // Can be the case when someone does `mount(Component, props)` with `let props = $state({...})` + // or `createClassComponent(Component, props)` + const is_entry_props = STATE_SYMBOL in props || LEGACY_PROPS in props; + return !!get_descriptor(props, prop_name)?.set || (is_entry_props && prop_name in props); } diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index a5f93e8b17..7eed8a744a 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -4,15 +4,7 @@ export { assign, assign_and, assign_or, assign_nullish } from './dev/assign.js'; export { cleanup_styles } from './dev/css.js'; export { add_locations } from './dev/elements.js'; export { hmr } from './dev/hmr.js'; -export { - ADD_OWNER, - add_owner, - mark_module_start, - mark_module_end, - add_owner_effect, - add_owner_to_class, - skip_ownership_validation -} from './dev/ownership.js'; +export { mark_module_start, mark_module_end, create_ownership_validator } from './dev/ownership.js'; export { check_target, legacy_api } from './dev/legacy.js'; export { trace } from './dev/tracing.js'; export { inspect } from './dev/inspect.js'; diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index fab271c916..5e0aa3dbc3 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -1,7 +1,6 @@ -/** @import { ProxyMetadata, Source } from '#client' */ +/** @import { Source } from '#client' */ import { DEV } from 'esm-env'; import { get, active_effect, active_reaction, set_active_reaction } from './runtime.js'; -import { component_context } from './context.js'; import { array_prototype, get_descriptor, @@ -9,24 +8,19 @@ import { is_array, object_prototype } from '../shared/utils.js'; -import { check_ownership, widen_ownership } from './dev/ownership.js'; import { state as source, set } from './reactivity/sources.js'; -import { STATE_SYMBOL, STATE_SYMBOL_METADATA } from './constants.js'; +import { STATE_SYMBOL } from './constants.js'; import { UNINITIALIZED } from '../../constants.js'; import * as e from './errors.js'; import { get_stack } from './dev/tracing.js'; import { tracing_mode_flag } from '../flags/index.js'; -/** @type {ProxyMetadata | null} */ -var parent_metadata = null; - /** * @template T * @param {T} value - * @param {Source<T>} [prev] dev mode only * @returns {T} */ -export function proxy(value, prev) { +export function proxy(value) { // if non-proxyable, or is already a proxy, return `value` if (typeof value !== 'object' || value === null || STATE_SYMBOL in value) { return value; @@ -55,16 +49,7 @@ export function proxy(value, prev) { set_active_reaction(reaction); /** @type {T} */ - var result; - - if (DEV) { - var previous_metadata = parent_metadata; - parent_metadata = metadata; - result = fn(); - parent_metadata = previous_metadata; - } else { - result = fn(); - } + var result = fn(); set_active_reaction(previous_reaction); return result; @@ -76,31 +61,6 @@ export function proxy(value, prev) { sources.set('length', source(/** @type {any[]} */ (value).length, stack)); } - /** @type {ProxyMetadata} */ - var metadata; - - if (DEV) { - metadata = { - parent: parent_metadata, - owners: null - }; - - if (prev) { - // Reuse owners from previous state; necessary because reassignment is not guaranteed to have correct component context. - // If no previous proxy exists we play it safe and assume ownerless state - // @ts-expect-error - const prev_owners = prev.v?.[STATE_SYMBOL_METADATA]?.owners; - metadata.owners = prev_owners ? new Set(prev_owners) : null; - } else { - metadata.owners = - parent_metadata === null - ? component_context !== null - ? new Set([component_context.function]) - : null - : new Set(); - } - } - return new Proxy(/** @type {any} */ (value), { defineProperty(_, prop, descriptor) { if ( @@ -160,10 +120,6 @@ export function proxy(value, prev) { }, get(target, prop, receiver) { - if (DEV && prop === STATE_SYMBOL_METADATA) { - return metadata; - } - if (prop === STATE_SYMBOL) { return value; } @@ -179,22 +135,6 @@ export function proxy(value, prev) { if (s !== undefined) { var v = get(s); - - // In case of something like `foo = bar.map(...)`, foo would have ownership - // of the array itself, while the individual items would have ownership - // of the component that created bar. That means if we later do `foo[0].baz = 42`, - // we could get a false-positive ownership violation, since the two proxies - // are not connected to each other via the parent metadata relationship. - // For this reason, we need to widen the ownership of the children - // upon access when we detect they are not connected. - if (DEV) { - /** @type {ProxyMetadata | undefined} */ - var prop_metadata = v?.[STATE_SYMBOL_METADATA]; - if (prop_metadata && prop_metadata?.parent !== metadata) { - widen_ownership(metadata, prop_metadata); - } - } - return v === UNINITIALIZED ? undefined : v; } @@ -225,10 +165,6 @@ export function proxy(value, prev) { }, has(target, prop) { - if (DEV && prop === STATE_SYMBOL_METADATA) { - return true; - } - if (prop === STATE_SYMBOL) { return true; } @@ -295,15 +231,6 @@ export function proxy(value, prev) { ); } - if (DEV) { - /** @type {ProxyMetadata | undefined} */ - var prop_metadata = value?.[STATE_SYMBOL_METADATA]; - if (prop_metadata && prop_metadata?.parent !== metadata) { - widen_ownership(metadata, prop_metadata); - } - check_ownership(metadata); - } - var descriptor = Reflect.getOwnPropertyDescriptor(target, prop); // Set the new value before updating any signals so that any listeners get the new value diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index ceab59b37c..2361762519 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -140,7 +140,7 @@ export function set(source, value, should_proxy = false) { e.state_unsafe_mutation(); } - let new_value = should_proxy ? proxy(value, source) : value; + let new_value = should_proxy ? proxy(value) : value; return internal_set(source, new_value); } diff --git a/packages/svelte/src/internal/client/types.d.ts b/packages/svelte/src/internal/client/types.d.ts index 0c260a0a9f..b46bdf2013 100644 --- a/packages/svelte/src/internal/client/types.d.ts +++ b/packages/svelte/src/internal/client/types.d.ts @@ -179,14 +179,6 @@ export type TaskCallback = (now: number) => boolean | void; export type TaskEntry = { c: TaskCallback; f: () => void }; -/** Dev-only */ -export interface ProxyMetadata { - /** The components that 'own' this state, if any. `null` means no owners, i.e. everyone can mutate this state. */ - owners: null | Set<Function>; - /** The parent metadata object */ - parent: null | ProxyMetadata; -} - export type ProxyStateObject<T = Record<string | symbol, any>> = T & { [STATE_SYMBOL]: T; }; diff --git a/packages/svelte/src/internal/client/warnings.js b/packages/svelte/src/internal/client/warnings.js index 250c6eca2f..c84b487e28 100644 --- a/packages/svelte/src/internal/client/warnings.js +++ b/packages/svelte/src/internal/client/warnings.js @@ -129,27 +129,30 @@ export function lifecycle_double_unmount() { } /** - * %parent% passed a value to %child% with `bind:`, but the value is owned by %owner%. Consider creating a binding between %owner% and %parent% + * %parent% passed property `%prop%` to %child% with `bind:`, but its parent component %owner% did not declare `%prop%` as a binding. Consider creating a binding between %owner% and %parent% (e.g. `bind:%prop%={...}` instead of `%prop%={...}`) * @param {string} parent + * @param {string} prop * @param {string} child * @param {string} owner */ -export function ownership_invalid_binding(parent, child, owner) { +export function ownership_invalid_binding(parent, prop, child, owner) { if (DEV) { - console.warn(`%c[svelte] ownership_invalid_binding\n%c${parent} passed a value to ${child} with \`bind:\`, but the value is owned by ${owner}. Consider creating a binding between ${owner} and ${parent}\nhttps://svelte.dev/e/ownership_invalid_binding`, bold, normal); + console.warn(`%c[svelte] ownership_invalid_binding\n%c${parent} passed property \`${prop}\` to ${child} with \`bind:\`, but its parent component ${owner} did not declare \`${prop}\` as a binding. Consider creating a binding between ${owner} and ${parent} (e.g. \`bind:${prop}={...}\` instead of \`${prop}={...}\`)\nhttps://svelte.dev/e/ownership_invalid_binding`, bold, normal); } else { console.warn(`https://svelte.dev/e/ownership_invalid_binding`); } } /** - * %component% mutated a value owned by %owner%. This is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead - * @param {string | undefined | null} [component] - * @param {string | undefined | null} [owner] + * Mutating unbound props (`%name%`, at %location%) is strongly discouraged. Consider using `bind:%prop%={...}` in %parent% (or using a callback) instead + * @param {string} name + * @param {string} location + * @param {string} prop + * @param {string} parent */ -export function ownership_invalid_mutation(component, owner) { +export function ownership_invalid_mutation(name, location, prop, parent) { if (DEV) { - console.warn(`%c[svelte] ownership_invalid_mutation\n%c${component ? `${component} mutated a value owned by ${owner}. This is strongly discouraged. Consider passing values to child components with \`bind:\`, or use a callback instead` : 'Mutating a value outside the component that created it is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead'}\nhttps://svelte.dev/e/ownership_invalid_mutation`, bold, normal); + console.warn(`%c[svelte] ownership_invalid_mutation\n%cMutating unbound props (\`${name}\`, at ${location}) is strongly discouraged. Consider using \`bind:${prop}={...}\` in ${parent} (or using a callback) instead\nhttps://svelte.dev/e/ownership_invalid_mutation`, bold, normal); } else { console.warn(`https://svelte.dev/e/ownership_invalid_mutation`); } diff --git a/packages/svelte/src/utils.js b/packages/svelte/src/utils.js index d4d106d56d..ada318e85a 100644 --- a/packages/svelte/src/utils.js +++ b/packages/svelte/src/utils.js @@ -465,8 +465,10 @@ export function is_raw_text_element(name) { /** * Prevent devtools trying to make `location` a clickable link by inserting a zero-width space - * @param {string | undefined} location + * @template {string | undefined} T + * @param {T} location + * @returns {T}; */ export function sanitize_location(location) { - return location?.replace(/\//g, '/\u200b'); + return /** @type {T} */ (location?.replace(/\//g, '/\u200b')); } diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-discouraged/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-discouraged/_config.js index 62c6961242..8452661026 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-discouraged/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-discouraged/_config.js @@ -10,7 +10,7 @@ export default test({ test({ assert, target, warnings }) { const warning = - 'Counter.svelte mutated a value owned by main.svelte. This is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead'; + 'Mutating unbound props (`object`, at Counter.svelte:5:23) is strongly discouraged. Consider using `bind:object={...}` in main.svelte (or using a callback) instead'; const [btn1, btn2] = target.querySelectorAll('button'); btn1.click(); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-global-2/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-global-2/_config.js deleted file mode 100644 index b4864154c3..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-global-2/_config.js +++ /dev/null @@ -1,11 +0,0 @@ -import { test } from '../../test'; - -export default test({ - compileOptions: { - dev: true - }, - - async test({ assert, warnings }) { - assert.deepEqual(warnings, []); - } -}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-global-2/child.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-global-2/child.svelte deleted file mode 100644 index 13de753647..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-global-2/child.svelte +++ /dev/null @@ -1,18 +0,0 @@ -<script module> - let toast1 = $state(); - let toast2 = $state({}); - - export async function show_toast() { - toast1 = { - message: 'foo', - show: true - }; - toast1.show = false; - - toast2 = { - message: 'foo', - show: true - }; - toast2.show = false; - } -</script> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-global-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-global-2/main.svelte deleted file mode 100644 index 8a6922e9e2..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-global-2/main.svelte +++ /dev/null @@ -1,5 +0,0 @@ -<script> - import { show_toast } from "./child.svelte"; - - show_toast(); -</script> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-1/_config.js similarity index 74% rename from packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/_config.js rename to packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-1/_config.js index c07b9ce129..96b18d1854 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-1/_config.js @@ -8,7 +8,7 @@ let warn; let warnings = []; export default test({ - html: `<button>clicks: 0</button>`, + html: `<button>[]</button>`, compileOptions: { dev: true @@ -34,8 +34,8 @@ export default test({ btn?.click(); }); - assert.htmlEqual(target.innerHTML, `<button>clicks: 1</button>`); + assert.htmlEqual(target.innerHTML, `<button>[foo]</button>`); - assert.deepEqual(warnings, []); + assert.deepEqual(warnings, [], 'expected getContext to have widened ownership'); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-1/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-1/main.svelte new file mode 100644 index 0000000000..2dd7cab141 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-1/main.svelte @@ -0,0 +1,9 @@ +<script> + import { setContext } from 'svelte'; + import Sub from './sub.svelte'; + + let list = $state([]); + setContext('list', list); +</script> + +<Sub /> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/sub.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-1/sub.svelte similarity index 100% rename from packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/sub.svelte rename to packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-1/sub.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/_config.js index c07b9ce129..66f1726a2a 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/_config.js @@ -1,41 +1,24 @@ import { flushSync } from 'svelte'; import { test } from '../../test'; -/** @type {typeof console.warn} */ -let warn; - -/** @type {any[]} */ -let warnings = []; - export default test({ - html: `<button>clicks: 0</button>`, - compileOptions: { dev: true }, - before_test: () => { - warn = console.warn; - - console.warn = (...args) => { - warnings.push(...args); - }; - }, + test({ assert, target, warnings }) { + const [btn1, btn2] = target.querySelectorAll('button'); - after_test: () => { - console.warn = warn; - warnings = []; - }, + flushSync(() => { + btn1.click(); + }); - test({ assert, target }) { - const btn = target.querySelector('button'); + assert.deepEqual(warnings.length, 0); flushSync(() => { - btn?.click(); + btn2.click(); }); - assert.htmlEqual(target.innerHTML, `<button>clicks: 1</button>`); - - assert.deepEqual(warnings, []); + assert.deepEqual(warnings.length, 1); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/main.svelte index ad450a937e..0be7e434e4 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/main.svelte @@ -1,9 +1,8 @@ -<script> - import { global } from './state.svelte.js'; +<script lang="ts"> + import Sub from './sub.svelte'; + import { create_my_state } from './state.svelte'; - global.a = { b: 0 }; + const myState = create_my_state(); </script> -<button onclick={() => global.a.b += 1}> - clicks: {global.a.b} -</button> +<Sub count={myState.my_state} inc={myState.inc} /> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/state.svelte.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/state.svelte.js index 3e7a68cf97..2906b9bce5 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/state.svelte.js +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/state.svelte.js @@ -1 +1,14 @@ -export let global = $state({}); +export function create_my_state() { + const my_state = $state({ + a: 0 + }); + + function inc() { + my_state.a++; + } + + return { + my_state, + inc + }; +} diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/sub.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/sub.svelte similarity index 100% rename from packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/sub.svelte rename to packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-2/sub.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/Child.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/Child.svelte similarity index 100% rename from packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/Child.svelte rename to packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/Child.svelte diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/_config.js index 96b18d1854..ab7327ab8b 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/_config.js @@ -1,41 +1,24 @@ import { flushSync } from 'svelte'; import { test } from '../../test'; -/** @type {typeof console.warn} */ -let warn; - -/** @type {any[]} */ -let warnings = []; - export default test({ - html: `<button>[]</button>`, - compileOptions: { dev: true }, - before_test: () => { - warn = console.warn; - - console.warn = (...args) => { - warnings.push(...args); - }; - }, + async test({ assert, target, warnings }) { + const [btn1, btn2] = target.querySelectorAll('button'); - after_test: () => { - console.warn = warn; - warnings = []; - }, + flushSync(() => { + btn1.click(); + }); - test({ assert, target }) { - const btn = target.querySelector('button'); + assert.deepEqual(warnings.length, 0); flushSync(() => { - btn?.click(); + btn2.click(); }); - assert.htmlEqual(target.innerHTML, `<button>[foo]</button>`); - - assert.deepEqual(warnings, [], 'expected getContext to have widened ownership'); + assert.deepEqual(warnings.length, 1); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/main.svelte index 2dd7cab141..8e8343790b 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-3/main.svelte @@ -1,9 +1,13 @@ <script> - import { setContext } from 'svelte'; - import Sub from './sub.svelte'; + import Child from './Child.svelte'; - let list = $state([]); - setContext('list', list); + let items = $state([{ id: "test", name: "this is a test"}, { id:"test2", name: "this is a second test"}]); + let found = $state(); + + function onclick() { + found = items.find(c => c.id === 'test2'); + } </script> -<Sub /> +<button {onclick}>First click here</button> +<Child item={found} /> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/_config.js deleted file mode 100644 index aeb3740dfe..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/_config.js +++ /dev/null @@ -1,37 +0,0 @@ -import { flushSync } from 'svelte'; -import { test } from '../../test'; - -/** @type {typeof console.warn} */ -let warn; - -/** @type {any[]} */ -let warnings = []; - -export default test({ - compileOptions: { - dev: true - }, - - before_test: () => { - warn = console.warn; - - console.warn = (...args) => { - warnings.push(...args); - }; - }, - - after_test: () => { - console.warn = warn; - warnings = []; - }, - - test({ assert, target }) { - const btn = target.querySelector('button'); - - flushSync(() => { - btn?.click(); - }); - - assert.deepEqual(warnings, []); - } -}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/main.svelte deleted file mode 100644 index 2d40c13949..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/main.svelte +++ /dev/null @@ -1,11 +0,0 @@ -<script> - import { global } from './state.svelte.js'; - import Sub from './sub.svelte'; -</script> - -<Sub /> -<!-- it's important _NOT_ to read global.a.b in the template, - else the proxy would set up the structure with its own component context already --> -<button onclick={() => global.increment_a_b()}> - click me -</button> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/state.svelte.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/state.svelte.js deleted file mode 100644 index 4079059171..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/state.svelte.js +++ /dev/null @@ -1,13 +0,0 @@ -class Global { - state = $state({}); - - add_a(a) { - this.state.a = a; - } - - increment_a_b() { - this.state.a.b++; - } -} - -export const global = new Global(); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/sub.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/sub.svelte deleted file mode 100644 index 044904aa18..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-4/sub.svelte +++ /dev/null @@ -1,8 +0,0 @@ -<script> - import { global } from "./state.svelte"; - - // The real world use case would be someone manipulating state locally, for example form state, - // and then push the values to a global state for everyone else to see / possibly mutate. - const local_soon_global = $state({ b: 0 }); - global.add_a(local_soon_global); -</script> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/_config.js deleted file mode 100644 index 66f1726a2a..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/_config.js +++ /dev/null @@ -1,24 +0,0 @@ -import { flushSync } from 'svelte'; -import { test } from '../../test'; - -export default test({ - compileOptions: { - dev: true - }, - - test({ assert, target, warnings }) { - const [btn1, btn2] = target.querySelectorAll('button'); - - flushSync(() => { - btn1.click(); - }); - - assert.deepEqual(warnings.length, 0); - - flushSync(() => { - btn2.click(); - }); - - assert.deepEqual(warnings.length, 1); - } -}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/main.svelte deleted file mode 100644 index 0be7e434e4..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/main.svelte +++ /dev/null @@ -1,8 +0,0 @@ -<script lang="ts"> - import Sub from './sub.svelte'; - import { create_my_state } from './state.svelte'; - - const myState = create_my_state(); -</script> - -<Sub count={myState.my_state} inc={myState.inc} /> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/state.svelte.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/state.svelte.js deleted file mode 100644 index 2906b9bce5..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-5/state.svelte.js +++ /dev/null @@ -1,14 +0,0 @@ -export function create_my_state() { - const my_state = $state({ - a: 0 - }); - - function inc() { - my_state.a++; - } - - return { - my_state, - inc - }; -} diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/Child.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/Child.svelte deleted file mode 100644 index aa31fd7606..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/Child.svelte +++ /dev/null @@ -1,9 +0,0 @@ -<script> - import { getContext } from 'svelte'; - - const foo = getContext('foo') -</script> - -<button onclick={() => { - foo.person.name.last = 'T'; -}}>mutate</button> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/_config.js deleted file mode 100644 index cc9ea715f0..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/_config.js +++ /dev/null @@ -1,37 +0,0 @@ -import { flushSync } from 'svelte'; -import { test } from '../../test'; - -/** @type {typeof console.warn} */ -let warn; - -/** @type {any[]} */ -let warnings = []; - -export default test({ - compileOptions: { - dev: true - }, - - before_test: () => { - warn = console.warn; - - console.warn = (...args) => { - warnings.push(...args); - }; - }, - - after_test: () => { - console.warn = warn; - warnings = []; - }, - - async test({ assert, target }) { - const btn = target.querySelector('button'); - - flushSync(() => { - btn?.click(); - }); - - assert.deepEqual(warnings.length, 0); - } -}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/main.svelte deleted file mode 100644 index 92d7dbd2db..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-6/main.svelte +++ /dev/null @@ -1,17 +0,0 @@ -<script> - import { setContext } from 'svelte'; - import Child from './Child.svelte'; - - class Person { - name = $state({ - first: 'Mr', - last: 'Bean' - }); - } - - setContext('foo', { - person: new Person() - }); -</script> - -<Child /> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/_config.js deleted file mode 100644 index ab7327ab8b..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/_config.js +++ /dev/null @@ -1,24 +0,0 @@ -import { flushSync } from 'svelte'; -import { test } from '../../test'; - -export default test({ - compileOptions: { - dev: true - }, - - async test({ assert, target, warnings }) { - const [btn1, btn2] = target.querySelectorAll('button'); - - flushSync(() => { - btn1.click(); - }); - - assert.deepEqual(warnings.length, 0); - - flushSync(() => { - btn2.click(); - }); - - assert.deepEqual(warnings.length, 1); - } -}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/main.svelte deleted file mode 100644 index 8e8343790b..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner-7/main.svelte +++ /dev/null @@ -1,13 +0,0 @@ -<script> - import Child from './Child.svelte'; - - let items = $state([{ id: "test", name: "this is a test"}, { id:"test2", name: "this is a second test"}]); - let found = $state(); - - function onclick() { - found = items.find(c => c.id === 'test2'); - } -</script> - -<button {onclick}>First click here</button> -<Child item={found} /> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/Counter.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/Counter.svelte deleted file mode 100644 index ffe6ef75c4..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/Counter.svelte +++ /dev/null @@ -1,7 +0,0 @@ -<script> - import { global } from './state.svelte.js'; -</script> - -<button onclick={() => global.object.count += 1}> - clicks: {global.object.count} -</button> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/main.svelte deleted file mode 100644 index 5f1c7461f6..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/main.svelte +++ /dev/null @@ -1,9 +0,0 @@ -<script> - import Counter from './Counter.svelte'; - import { global } from './state.svelte.js'; - - let object = $state({ count: 0 }); - global.object = object; -</script> - -<Counter /> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/state.svelte.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/state.svelte.js deleted file mode 100644 index 6881c2faf6..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-inherited-owner/state.svelte.js +++ /dev/null @@ -1,3 +0,0 @@ -export let global = $state({ - object: { count: -1 } -}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-2/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-2/_config.js index 87474a05cc..39fa80c55a 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-2/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-2/_config.js @@ -8,6 +8,6 @@ export default test({ }, warnings: [ - 'Intermediate.svelte passed a value to Counter.svelte with `bind:`, but the value is owned by main.svelte. Consider creating a binding between main.svelte and Intermediate.svelte' + 'Intermediate.svelte passed property `object` to Counter.svelte with `bind:`, but its parent component main.svelte did not declare `object` as a binding. Consider creating a binding between main.svelte and Intermediate.svelte (e.g. `bind:object={...}` instead of `object={...}`)' ] }); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-3/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-3/_config.js index 66e5184380..7b8cc676d5 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-3/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-3/_config.js @@ -33,7 +33,7 @@ export default test({ assert.htmlEqual(target.innerHTML, `<button>clicks: 1</button><button>clicks: 1</button>`); assert.deepEqual(warnings, [ - 'Counter.svelte mutated a value owned by main.svelte. This is strongly discouraged. Consider passing values to child components with `bind:`, or use a callback instead' + 'Mutating unbound props (`notshared`, at Counter.svelte:10:23) is strongly discouraged. Consider using `bind:notshared={...}` in main.svelte (or using a callback) instead' ]); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-7/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-7/_config.js index e766a946d0..bd2ecc28b6 100644 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-7/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-7/_config.js @@ -1,7 +1,6 @@ import { flushSync } from 'svelte'; import { ok, test } from '../../test'; -// Tests that proxies widen ownership correctly even if not directly connected to each other export default test({ compileOptions: { dev: true diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterBinding.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterBinding.svelte deleted file mode 100644 index d6da559fb1..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterBinding.svelte +++ /dev/null @@ -1,7 +0,0 @@ -<script> - let { linked3 = $bindable(), linked4 = $bindable() } = $props(); -</script> - -<p>Binding</p> -<button onclick={() => linked3.count++}>Increment Linked 1 ({linked3.count})</button> -<button onclick={() => linked4.count++}>Increment Linked 2 ({linked4.count})</button> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterContext.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterContext.svelte deleted file mode 100644 index b935f0a472..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/CounterContext.svelte +++ /dev/null @@ -1,13 +0,0 @@ -<script> - import { getContext } from 'svelte'; - const linked1 = getContext('linked1'); - const linked2 = getContext('linked2'); -</script> - -<p>Context</p> -<button onclick={() => linked1.linked.current.count++} - >Increment Linked 1 ({linked1.linked.current.count})</button -> -<button onclick={() => linked2.linked.current.count++} - >Increment Linked 2 ({linked2.linked.current.count})</button -> diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/_config.js deleted file mode 100644 index d6d12d01cd..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/_config.js +++ /dev/null @@ -1,34 +0,0 @@ -import { flushSync } from 'svelte'; -import { test } from '../../test'; - -// Tests that ownership is widened with $derived (on class or on its own) that contains $state -export default test({ - compileOptions: { - dev: true - }, - - test({ assert, target, warnings }) { - const [root, counter_context1, counter_context2, counter_binding1, counter_binding2] = - target.querySelectorAll('button'); - - counter_context1.click(); - counter_context2.click(); - counter_binding1.click(); - counter_binding2.click(); - flushSync(); - - assert.equal(warnings.length, 0); - - root.click(); - flushSync(); - counter_context1.click(); - counter_context2.click(); - counter_binding1.click(); - counter_binding2.click(); - flushSync(); - - assert.equal(warnings.length, 0); - }, - - warnings: [] -}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/main.svelte deleted file mode 100644 index aaade26e16..0000000000 --- a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-with-binding-8/main.svelte +++ /dev/null @@ -1,46 +0,0 @@ -<script> - import CounterBinding from './CounterBinding.svelte'; - import CounterContext from './CounterContext.svelte'; - import { setContext } from 'svelte'; - - let counter = $state({ count: 0 }); - - class Linked { - #getter; - linked = $derived.by(() => { - const state = $state({ current: $state.snapshot(this.#getter()) }); - return state; - }); - - constructor(fn) { - this.#getter = fn; - } - } - - const linked1 = $derived.by(() => { - const state = $state({ current: $state.snapshot(counter) }); - return state; - }); - const linked2 = new Linked(() => counter); - - setContext('linked1', { - get linked() { - return linked1; - } - }); - setContext('linked2', linked2); - - const linked3 = $derived.by(() => { - const state = $state({ current: $state.snapshot(counter) }); - return state; - }); - const linked4 = new Linked(() => counter); -</script> - -<p>Parent</p> -<button onclick={() => counter.count++}> - Increment Original ({counter.count}) -</button> - -<CounterContext /> -<CounterBinding bind:linked3={linked3.current} bind:linked4={linked4.linked.current} /> From 7694818f9cf70cf802058c2a49571aaa69956d8a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 17:09:22 -0400 Subject: [PATCH 0315/1429] Version Packages (#15705) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/clever-news-enjoy.md | 5 ----- .changeset/sweet-ants-care.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/clever-news-enjoy.md delete mode 100644 .changeset/sweet-ants-care.md diff --git a/.changeset/clever-news-enjoy.md b/.changeset/clever-news-enjoy.md deleted file mode 100644 index 2ff3dcbe56..0000000000 --- a/.changeset/clever-news-enjoy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: allow `$.state` and `$.derived` to be treeshaken diff --git a/.changeset/sweet-ants-care.md b/.changeset/sweet-ants-care.md deleted file mode 100644 index b4805626ab..0000000000 --- a/.changeset/sweet-ants-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: rework binding ownership validation diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 361de202b1..eb76e9a9e9 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.25.9 + +### Patch Changes + +- fix: allow `$.state` and `$.derived` to be treeshaken ([#15702](https://github.com/sveltejs/svelte/pull/15702)) + +- fix: rework binding ownership validation ([#15678](https://github.com/sveltejs/svelte/pull/15678)) + ## 5.25.8 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 8a225a798d..3fb843b3a2 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.25.8", + "version": "5.25.9", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 2c8140e365..13a69857a7 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.25.8'; +export const VERSION = '5.25.9'; export const PUBLIC_VERSION = '5'; From 708f541ad8ed2e9426cedb03e0bc1ad5d84cb787 Mon Sep 17 00:00:00 2001 From: 7nik <kfiiranet@gmail.com> Date: Wed, 9 Apr 2025 00:39:11 +0300 Subject: [PATCH 0316/1429] fix: better scope `:global()` with nesting selector `&` (#15671) Co-authored-by: 7nik <kifiranet@gmail.com> --- .changeset/stupid-vans-draw.md | 5 +++++ .../phases/2-analyze/css/css-prune.js | 20 +++++++++++++++---- .../samples/global-with-nesting/expected.css | 7 ++++++- .../samples/global-with-nesting/input.svelte | 7 ++++++- 4 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 .changeset/stupid-vans-draw.md diff --git a/.changeset/stupid-vans-draw.md b/.changeset/stupid-vans-draw.md new file mode 100644 index 0000000000..24892f1e8f --- /dev/null +++ b/.changeset/stupid-vans-draw.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: better scope `:global()` with nesting selector `&` diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js index 0646c6341a..fbe6ca1cd3 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-prune.js @@ -1,6 +1,11 @@ /** @import * as Compiler from '#compiler' */ import { walk } from 'zimmerframe'; -import { get_parent_rules, get_possible_values, is_outer_global } from './utils.js'; +import { + get_parent_rules, + get_possible_values, + is_outer_global, + is_unscoped_pseudo_class +} from './utils.js'; import { regex_ends_with_whitespace, regex_starts_with_whitespace } from '../../patterns.js'; import { get_attribute_chunks, is_text_attribute } from '../../../utils/ast.js'; @@ -286,20 +291,26 @@ function apply_combinator(relative_selector, rest_selectors, rule, node, directi * a global selector * @param {Compiler.AST.CSS.RelativeSelector} selector * @param {Compiler.AST.CSS.Rule} rule + * @returns {boolean} */ function is_global(selector, rule) { if (selector.metadata.is_global || selector.metadata.is_global_like) { return true; } + let explicitly_global = false; + for (const s of selector.selectors) { /** @type {Compiler.AST.CSS.SelectorList | null} */ let selector_list = null; + let can_be_global = false; let owner = rule; if (s.type === 'PseudoClassSelector') { if ((s.name === 'is' || s.name === 'where') && s.args) { selector_list = s.args; + } else { + can_be_global = is_unscoped_pseudo_class(s); } } @@ -308,18 +319,19 @@ function is_global(selector, rule) { selector_list = owner.prelude; } - const has_global_selectors = selector_list?.children.some((complex_selector) => { + const has_global_selectors = !!selector_list?.children.some((complex_selector) => { return complex_selector.children.every((relative_selector) => is_global(relative_selector, owner) ); }); + explicitly_global ||= has_global_selectors; - if (!has_global_selectors) { + if (!has_global_selectors && !can_be_global) { return false; } } - return true; + return explicitly_global || selector.selectors.length === 0; } const regex_backslash_and_following_character = /\\(.)/g; diff --git a/packages/svelte/tests/css/samples/global-with-nesting/expected.css b/packages/svelte/tests/css/samples/global-with-nesting/expected.css index dcb8a0e481..1863c57d85 100644 --- a/packages/svelte/tests/css/samples/global-with-nesting/expected.css +++ b/packages/svelte/tests/css/samples/global-with-nesting/expected.css @@ -1,5 +1,10 @@ div.svelte-xyz { &.class{ - color: red; + color: green; + } + } + * { + &:hover .class.svelte-xyz { + color: green; } } \ No newline at end of file diff --git a/packages/svelte/tests/css/samples/global-with-nesting/input.svelte b/packages/svelte/tests/css/samples/global-with-nesting/input.svelte index 0c73ed7a78..2c1d2b5ebd 100644 --- a/packages/svelte/tests/css/samples/global-with-nesting/input.svelte +++ b/packages/svelte/tests/css/samples/global-with-nesting/input.svelte @@ -1,7 +1,12 @@ <style> div { &:global(.class){ - color: red; + color: green; + } + } + :global(*) { + &:hover .class { + color: green; } } </style> From 966ccfbe7451b7c00fc63ee43ea65bd7f286b5cf Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Tue, 8 Apr 2025 23:41:53 +0200 Subject: [PATCH 0317/1429] fix: set deriveds as `CLEAN` if they are assigned to (#15592) * fix: set deriveds as `CLEAN` if they are assigned to * chore: remove only * chore: remove unnecessary typecast * fix: set unowned as `MAYBE_DIRTY` instead of `CLEAN` * fix: visit the derived function when to update the dependencies even when it's reassigned * fix: use `execute_derived` instead of `update_reaction` * fix: execute deriveds eagerly when they are set if DIRTY --- .changeset/nervous-kids-shake.md | 5 +++ .../internal/client/reactivity/deriveds.js | 2 +- .../src/internal/client/reactivity/sources.js | 12 +++++-- .../svelte/src/internal/client/runtime.js | 2 +- packages/svelte/tests/signals/test.ts | 32 +++++++++++++++++++ 5 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 .changeset/nervous-kids-shake.md diff --git a/.changeset/nervous-kids-shake.md b/.changeset/nervous-kids-shake.md new file mode 100644 index 0000000000..3fc6429797 --- /dev/null +++ b/.changeset/nervous-kids-shake.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: set deriveds as `CLEAN` if they are assigned to diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 86171c2b2d..c9a8f7674a 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -131,7 +131,7 @@ function get_derived_parent_effect(derived) { * @param {Derived} derived * @returns {T} */ -function execute_derived(derived) { +export function execute_derived(derived) { var value; var prev_active_effect = active_effect; diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 2361762519..cae49c1832 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -28,14 +28,14 @@ import { UNOWNED, MAYBE_DIRTY, BLOCK_EFFECT, - ROOT_EFFECT, - EFFECT_IS_UPDATING + ROOT_EFFECT } from '../constants.js'; import * as e from '../errors.js'; import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js'; import { get_stack } from '../dev/tracing.js'; import { component_context, is_runes } from '../context.js'; import { proxy } from '../proxy.js'; +import { execute_derived } from './deriveds.js'; export let inspect_effects = new Set(); export const old_values = new Map(); @@ -172,6 +172,14 @@ export function internal_set(source, value) { } } + if ((source.f & DERIVED) !== 0) { + // if we are assigning to a dirty derived we set it to clean/maybe dirty but we also eagerly execute it to track the dependencies + if ((source.f & DIRTY) !== 0) { + execute_derived(/** @type {Derived} */ (source)); + } + set_signal_status(source, (source.f & UNOWNED) === 0 ? CLEAN : MAYBE_DIRTY); + } + mark_reactions(source, DIRTY); // It's possible that the current reaction might not have up-to-date dependencies diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index c1c91f3551..a7662be617 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -27,7 +27,7 @@ import { } from './constants.js'; import { flush_tasks } from './dom/task.js'; import { internal_set, old_values } from './reactivity/sources.js'; -import { destroy_derived_effects, update_derived } from './reactivity/deriveds.js'; +import { destroy_derived_effects, execute_derived, update_derived } from './reactivity/deriveds.js'; import * as e from './errors.js'; import { FILENAME } from '../../constants.js'; import { tracing_mode_flag } from '../flags/index.js'; diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index 3977caae36..3a427e9392 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -1080,6 +1080,38 @@ describe('signals', () => { }; }); + test("deriveds set after they are DIRTY doesn't get updated on get", () => { + return () => { + const a = state(0); + const b = derived(() => $.get(a)); + + set(b, 1); + assert.equal($.get(b), 1); + + set(a, 2); + assert.equal($.get(b), 2); + set(b, 3); + + assert.equal($.get(b), 3); + }; + }); + + test("unowned deriveds set after they are DIRTY doesn't get updated on get", () => { + return () => { + const a = state(0); + const b = derived(() => $.get(a)); + const c = derived(() => $.get(b)); + + set(b, 1); + assert.equal($.get(c), 1); + + set(a, 2); + + assert.equal($.get(b), 2); + assert.equal($.get(c), 2); + }; + }); + test('deriveds containing effects work correctly when used with untrack', () => { return () => { let a = render_effect(() => {}); From e9a16c4b4209d104e0bef7cf28df51f830de1254 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Tue, 8 Apr 2025 18:53:18 -0400 Subject: [PATCH 0318/1429] chore: revert corepack override (#15197) * Revert "try this (#15196)" This reverts commit f878736f3825e1832fa7344306358e877e20bd7f. * upgrade node * upgrade pnpm --------- Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- .github/workflows/pkg.pr.new.yml | 5 +---- package.json | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml index 90d219faae..b2b521dc6f 100644 --- a/.github/workflows/pkg.pr.new.yml +++ b/.github/workflows/pkg.pr.new.yml @@ -11,13 +11,10 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: install corepack - run: npm i -g corepack@0.31.0 - - run: corepack enable - uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 22.x cache: pnpm - name: Install dependencies diff --git a/package.json b/package.json index ad69bfc9ca..70e85438f0 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "private": true, "type": "module", "license": "MIT", - "packageManager": "pnpm@9.4.0", + "packageManager": "pnpm@10.4.0", "engines": { "pnpm": ">=9.0.0" }, From 0ca1f4a37ec008092fc1798e374c6108308addef Mon Sep 17 00:00:00 2001 From: Min Idzelis <min123@gmail.com> Date: Tue, 8 Apr 2025 21:26:54 -0400 Subject: [PATCH 0319/1429] docs: raise importance of global vs local transitions (#15479) * Doc: Raise importance of global vs local transitions * switch order --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- documentation/docs/03-template-syntax/13-transition.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/docs/03-template-syntax/13-transition.md b/documentation/docs/03-template-syntax/13-transition.md index 51c11e8b34..c51175c272 100644 --- a/documentation/docs/03-template-syntax/13-transition.md +++ b/documentation/docs/03-template-syntax/13-transition.md @@ -22,10 +22,6 @@ The `transition:` directive indicates a _bidirectional_ transition, which means {/if} ``` -## Built-in transitions - -A selection of built-in transitions can be imported from the [`svelte/transition`](svelte-transition) module. - ## Local vs global Transitions are local by default. Local transitions only play when the block they belong to is created or destroyed, _not_ when parent blocks are created or destroyed. @@ -40,6 +36,10 @@ Transitions are local by default. Local transitions only play when the block the {/if} ``` +## Built-in transitions + +A selection of built-in transitions can be imported from the [`svelte/transition`](svelte-transition) module. + ## Transition parameters Transitions can have parameters. From 0ff3d7452092511a55a63a7639a8e8798fea9fed Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Tue, 8 Apr 2025 19:40:05 -0700 Subject: [PATCH 0320/1429] docs: update `$effect` examples (#15463) * docs: update effect examples * revert * Update documentation/docs/02-runes/04-$effect.md * update example * revert * update effect root example --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- documentation/docs/02-runes/04-$effect.md | 55 ++++++++++++++--------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/documentation/docs/02-runes/04-$effect.md b/documentation/docs/02-runes/04-$effect.md index ae1a2146c9..46ea9b81e9 100644 --- a/documentation/docs/02-runes/04-$effect.md +++ b/documentation/docs/02-runes/04-$effect.md @@ -25,7 +25,7 @@ You can create an effect with the `$effect` rune ([demo](/playground/untitled#H4 }); </script> -<canvas bind:this={canvas} width="100" height="100" /> +<canvas bind:this={canvas} width="100" height="100"></canvas> ``` When Svelte runs an effect function, it tracks which pieces of state (and derived state) are accessed (unless accessed inside [`untrack`](svelte#untrack)), and re-runs the function when that state later changes. @@ -135,19 +135,33 @@ An effect only reruns when the object it reads changes, not when a property insi An effect only depends on the values that it read the last time it ran. This has interesting implications for effects that have conditional code. -For instance, if `a` is `true` in the code snippet below, the code inside the `if` block will run and `b` will be evaluated. As such, changes to either `a` or `b` [will cause the effect to re-run](/playground/untitled#H4sIAAAAAAAAE3VQzWrDMAx-FdUU4kBp71li6EPstOxge0ox8-QQK2PD-N1nLy2F0Z2Evj9_chKkP1B04pnYscc3cRCT8xhF95IEf8-Vq0DBr8rzPB_jJ3qumNERH-E2ECNxiRF9tIubWY00lgcYNAywj6wZJS8rtk83wjwgCrXHaULLUrYwKEgVGrnkx-Dx6MNFNstK5OjSbFGbwE0gdXuT_zGYrjmAuco515Hr1p_uXak3K3MgCGS9s-9D2grU-judlQYXIencnzad-tdR79qZrMyvw9wd5Z8Yv1h09dz8mn8AkM7Pfo0BAAA=). +For instance, if `condition` is `true` in the code snippet below, the code inside the `if` block will run and `color` will be evaluated. As such, changes to either `condition` or `color` [will cause the effect to re-run](/playground/untitled#H4sIAAAAAAAAE21RQW6DMBD8ytaNBJHaJFLViwNIVZ8RcnBgXVk1xsILTYT4e20TQg89IOPZ2fHM7siMaJBx9tmaWpFqjQNlAKXEihx7YVJpdIyfRkY3G4gB8Pi97cPanRtQU8AuwuF_eNUaQuPlOMtc1SlLRWlKUo1tOwJflUikQHZtA0klzCDc64Imx0ANn8bInV1CDhtHgjClrsftcSXotluLybOUb3g4JJHhOZs5WZpuIS9gjNqkJKQP5e2ClrR4SMdZ13E4xZ8zTPOTJU2A2uE_PQ9COCI926_hTVarIU4hu_REPlBrKq2q73ycrf1N-vS4TMUsulaVg3EtR8H9rFgsg8uUsT1B2F9eshigZHBRpuaD0D3mY8Qm2BfB5N2YyRzdNEYVDy0Ja-WsFjcOUuP1HvFLWA6H3XuHTUSmmDV2--0TXonxsKbp7G9C6R__NONS-MFNvxj_d6mBAgAA). -Conversely, if `a` is `false`, `b` will not be evaluated, and the effect will _only_ re-run when `a` changes. +Conversely, if `condition` is `false`, `color` will not be evaluated, and the effect will _only_ re-run again when `condition` changes. ```ts -let a = false; -let b = false; +// @filename: ambient.d.ts +declare module 'canvas-confetti' { + interface ConfettiOptions { + colors: string[]; + } + + function confetti(opts?: ConfettiOptions): void; + export default confetti; +} + +// @filename: index.js // ---cut--- -$effect(() => { - console.log('running'); +import confetti from 'canvas-confetti'; - if (a) { - console.log('b:', b); +let condition = $state(true); +let color = $state('#ff3e00'); + +$effect(() => { + if (condition) { + confetti({ colors: [color] }); + } else { + confetti(); } }); ``` @@ -211,20 +225,19 @@ It is used to implement abstractions like [`createSubscriber`](/docs/svelte/svel The `$effect.root` rune is an advanced feature that creates a non-tracked scope that doesn't auto-cleanup. This is useful for nested effects that you want to manually control. This rune also allows for the creation of effects outside of the component initialisation phase. -```svelte -<script> - let count = $state(0); +```js +const destroy = $effect.root(() => { + $effect(() => { + // setup + }); - const cleanup = $effect.root(() => { - $effect(() => { - console.log(count); - }); + return () => { + // cleanup + }; +}); - return () => { - console.log('effect root cleanup'); - }; - }); -</script> +// later... +destroy(); ``` ## When not to use `$effect` From 93110a32469779794d2c022a7605e958ce75cb34 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 9 Apr 2025 06:30:53 -0400 Subject: [PATCH 0321/1429] docs: explain restriction on exporting reassigned state (#15713) --- .../01-introduction/04-svelte-js-files.md | 2 +- documentation/docs/02-runes/02-$state.md | 80 +++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/documentation/docs/01-introduction/04-svelte-js-files.md b/documentation/docs/01-introduction/04-svelte-js-files.md index 0e05484299..1d3e3dd61a 100644 --- a/documentation/docs/01-introduction/04-svelte-js-files.md +++ b/documentation/docs/01-introduction/04-svelte-js-files.md @@ -4,7 +4,7 @@ title: .svelte.js and .svelte.ts files Besides `.svelte` files, Svelte also operates on `.svelte.js` and `.svelte.ts` files. -These behave like any other `.js` or `.ts` module, except that you can use runes. This is useful for creating reusable reactive logic, or sharing reactive state across your app. +These behave like any other `.js` or `.ts` module, except that you can use runes. This is useful for creating reusable reactive logic, or sharing reactive state across your app (though note that you [cannot export reassigned state]($state#Passing-state-across-modules)). > [!LEGACY] > This is a concept that didn't exist prior to Svelte 5 diff --git a/documentation/docs/02-runes/02-$state.md b/documentation/docs/02-runes/02-$state.md index 49e17cd08f..16630a977b 100644 --- a/documentation/docs/02-runes/02-$state.md +++ b/documentation/docs/02-runes/02-$state.md @@ -250,3 +250,83 @@ console.log(total.value); // 7 ``` ...though if you find yourself writing code like that, consider using [classes](#Classes) instead. + +## Passing state across modules + +You can declare state in `.svelte.js` and `.svelte.ts` files, but you can only _export_ that state if it's not directly reassigned. In other words you can't do this: + +```js +/// file: state.svelte.js +export let count = $state(0); + +export function increment() { + count += 1; +} +``` + +That's because every reference to `count` is transformed by the Svelte compiler — the code above is roughly equivalent to this: + +```js +/// file: state.svelte.js (compiler output) +// @filename: index.ts +interface Signal<T> { + value: T; +} + +interface Svelte { + state<T>(value?: T): Signal<T>; + get<T>(source: Signal<T>): T; + set<T>(source: Signal<T>, value: T): void; +} +declare const $: Svelte; +// ---cut--- +export let count = $.state(0); + +export function increment() { + $.set(count, $.get(count) + 1); +} +``` + +> [!NOTE] You can see the code Svelte generates by clicking the 'JS Output' tab in the [playground](/playground). + +Since the compiler only operates on one file at a time, if another file imports `count` Svelte doesn't know that it needs to wrap each reference in `$.get` and `$.set`: + +```js +// @filename: state.svelte.js +export let count = 0; + +// @filename: index.js +// ---cut--- +import { count } from './state.svelte.js'; + +console.log(typeof count); // 'object', not 'number' +``` + +This leaves you with two options for sharing state between modules — either don't reassign it... + +```js +// This is allowed — since we're updating +// `counter.count` rather than `counter`, +// Svelte doesn't wrap it in `$.state` +export const counter = $state({ + count: 0 +}); + +export function increment() { + counter.count += 1; +} +``` + +...or don't directly export it: + +```js +let count = $state(0); + +export function getCount() { + return count; +} + +export function increment() { + count += 1; +} +``` From c23f15134e85c17e85ad326fcb50bbc4a1cdffa4 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Wed, 9 Apr 2025 12:51:21 -0400 Subject: [PATCH 0322/1429] chore: remove stack-based module boundaries (#15711) --- .../98-reference/.generated/client-errors.md | 2 +- .../svelte/messages/client-errors/errors.md | 2 +- .../3-transform/client/transform-client.js | 3 - .../svelte/src/internal/client/dev/legacy.js | 5 +- .../src/internal/client/dev/ownership.js | 97 ------------------- packages/svelte/src/internal/client/errors.js | 7 +- packages/svelte/src/internal/client/index.js | 2 +- 7 files changed, 7 insertions(+), 111 deletions(-) diff --git a/documentation/docs/98-reference/.generated/client-errors.md b/documentation/docs/98-reference/.generated/client-errors.md index fd9419176d..32348bb781 100644 --- a/documentation/docs/98-reference/.generated/client-errors.md +++ b/documentation/docs/98-reference/.generated/client-errors.md @@ -21,7 +21,7 @@ A component is attempting to bind to a non-bindable property `%key%` belonging t ### component_api_changed ``` -%parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5 +Calling `%method%` on a component instance (of %component%) is no longer valid in Svelte 5 ``` See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more information. diff --git a/packages/svelte/messages/client-errors/errors.md b/packages/svelte/messages/client-errors/errors.md index ca06122cb5..c4e68f8fee 100644 --- a/packages/svelte/messages/client-errors/errors.md +++ b/packages/svelte/messages/client-errors/errors.md @@ -12,7 +12,7 @@ ## component_api_changed -> %parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5 +> Calling `%method%` on a component instance (of %component%) is no longer valid in Svelte 5 See the [migration guide](/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more information. diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 38fcee8d6f..098b3ecae8 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -536,9 +536,6 @@ export function client_component(analysis, options) { b.assignment('=', b.member(b.id(analysis.name), '$.FILENAME', true), b.literal(filename)) ) ); - - body.unshift(b.stmt(b.call(b.id('$.mark_module_start')))); - body.push(b.stmt(b.call(b.id('$.mark_module_end'), b.id(analysis.name)))); } if (!analysis.runes) { diff --git a/packages/svelte/src/internal/client/dev/legacy.js b/packages/svelte/src/internal/client/dev/legacy.js index 138213c551..02428dc824 100644 --- a/packages/svelte/src/internal/client/dev/legacy.js +++ b/packages/svelte/src/internal/client/dev/legacy.js @@ -1,7 +1,6 @@ import * as e from '../errors.js'; import { component_context } from '../context.js'; import { FILENAME } from '../../../constants.js'; -import { get_component } from './ownership.js'; /** @param {Function & { [FILENAME]: string }} target */ export function check_target(target) { @@ -15,9 +14,7 @@ export function legacy_api() { /** @param {string} method */ function error(method) { - // @ts-expect-error - const parent = get_component()?.[FILENAME] ?? 'Something'; - e.component_api_changed(parent, method, component[FILENAME]); + e.component_api_changed(method, component[FILENAME]); } return { diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index 6c40a744df..e28a40dd77 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -7,103 +7,6 @@ import { component_context } from '../context.js'; import * as w from '../warnings.js'; import { sanitize_location } from '../../../utils.js'; -/** @type {Record<string, Array<{ start: Location, end: Location, component: Function }>>} */ -const boundaries = {}; - -const chrome_pattern = /at (?:.+ \()?(.+):(\d+):(\d+)\)?$/; -const firefox_pattern = /@(.+):(\d+):(\d+)$/; - -function get_stack() { - const stack = new Error().stack; - if (!stack) return null; - - const entries = []; - - for (const line of stack.split('\n')) { - let match = chrome_pattern.exec(line) ?? firefox_pattern.exec(line); - - if (match) { - entries.push({ - file: match[1], - line: +match[2], - column: +match[3] - }); - } - } - - return entries; -} - -/** - * Determines which `.svelte` component is responsible for a given state change - * @returns {Function | null} - */ -export function get_component() { - // first 4 lines are svelte internals; adjust this number if we change the internal call stack - const stack = get_stack()?.slice(4); - if (!stack) return null; - - for (let i = 0; i < stack.length; i++) { - const entry = stack[i]; - const modules = boundaries[entry.file]; - if (!modules) { - // If the first entry is not a component, that means the modification very likely happened - // within a .svelte.js file, possibly triggered by a component. Since these files are not part - // of the bondaries/component context heuristic, we need to bail in this case, else we would - // have false positives when the .svelte.ts file provides a state creator function, encapsulating - // the state and its mutations, and is being called from a component other than the one who - // called the state creator function. - if (i === 0) return null; - continue; - } - - for (const module of modules) { - if (module.end == null) { - return null; - } - if (module.start.line < entry.line && module.end.line > entry.line) { - return module.component; - } - } - } - - return null; -} - -/** - * Together with `mark_module_end`, this function establishes the boundaries of a `.svelte` file, - * such that subsequent calls to `get_component` can tell us which component is responsible - * for a given state change - */ -export function mark_module_start() { - const start = get_stack()?.[2]; - - if (start) { - (boundaries[start.file] ??= []).push({ - start, - // @ts-expect-error - end: null, - // @ts-expect-error we add the component at the end, since HMR will overwrite the function - component: null - }); - } -} - -/** - * @param {Function} component - */ -export function mark_module_end(component) { - const end = get_stack()?.[2]; - - if (end) { - const boundaries_file = boundaries[end.file]; - const boundary = boundaries_file[boundaries_file.length - 1]; - - boundary.end = end; - boundary.component = component; - } -} - /** * Sets up a validator that * - traverses the path of a prop to find out if it is allowed to be mutated diff --git a/packages/svelte/src/internal/client/errors.js b/packages/svelte/src/internal/client/errors.js index 8a5b5033a7..429dd99da9 100644 --- a/packages/svelte/src/internal/client/errors.js +++ b/packages/svelte/src/internal/client/errors.js @@ -54,15 +54,14 @@ export function bind_not_bindable(key, component, name) { } /** - * %parent% called `%method%` on an instance of %component%, which is no longer valid in Svelte 5 - * @param {string} parent + * Calling `%method%` on a component instance (of %component%) is no longer valid in Svelte 5 * @param {string} method * @param {string} component * @returns {never} */ -export function component_api_changed(parent, method, component) { +export function component_api_changed(method, component) { if (DEV) { - const error = new Error(`component_api_changed\n${parent} called \`${method}\` on an instance of ${component}, which is no longer valid in Svelte 5\nhttps://svelte.dev/e/component_api_changed`); + const error = new Error(`component_api_changed\nCalling \`${method}\` on a component instance (of ${component}) is no longer valid in Svelte 5\nhttps://svelte.dev/e/component_api_changed`); error.name = 'Svelte error'; throw error; diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 7eed8a744a..a865419c5f 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -4,7 +4,7 @@ export { assign, assign_and, assign_or, assign_nullish } from './dev/assign.js'; export { cleanup_styles } from './dev/css.js'; export { add_locations } from './dev/elements.js'; export { hmr } from './dev/hmr.js'; -export { mark_module_start, mark_module_end, create_ownership_validator } from './dev/ownership.js'; +export { create_ownership_validator } from './dev/ownership.js'; export { check_target, legacy_api } from './dev/legacy.js'; export { trace } from './dev/tracing.js'; export { inspect } from './dev/inspect.js'; From 475b5dbe83732fd031fa4f97aac712550385c700 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 14:09:51 -0400 Subject: [PATCH 0323/1429] Version Packages (#15712) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/nervous-kids-shake.md | 5 ----- .changeset/stupid-vans-draw.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/nervous-kids-shake.md delete mode 100644 .changeset/stupid-vans-draw.md diff --git a/.changeset/nervous-kids-shake.md b/.changeset/nervous-kids-shake.md deleted file mode 100644 index 3fc6429797..0000000000 --- a/.changeset/nervous-kids-shake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: set deriveds as `CLEAN` if they are assigned to diff --git a/.changeset/stupid-vans-draw.md b/.changeset/stupid-vans-draw.md deleted file mode 100644 index 24892f1e8f..0000000000 --- a/.changeset/stupid-vans-draw.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: better scope `:global()` with nesting selector `&` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index eb76e9a9e9..6f999f381e 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.25.10 + +### Patch Changes + +- fix: set deriveds as `CLEAN` if they are assigned to ([#15592](https://github.com/sveltejs/svelte/pull/15592)) + +- fix: better scope `:global()` with nesting selector `&` ([#15671](https://github.com/sveltejs/svelte/pull/15671)) + ## 5.25.9 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 3fb843b3a2..b9f434e688 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.25.9", + "version": "5.25.10", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 13a69857a7..2ea9890df9 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.25.9'; +export const VERSION = '5.25.10'; export const PUBLIC_VERSION = '5'; From 6c97a78049ccc706076e8b962d482c7a1fa27650 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Thu, 10 Apr 2025 13:29:40 +0200 Subject: [PATCH 0324/1429] fix: prevent ownership warnings if the fallback of a bindable is used (#15720) * fix: prevent ownership warnings if the fallback of a bindable is used * fix: filter out symbol from own keys * fix: don't create sources for `BINDABLE_FALLBACK_SYMBOL` * fix: use strategy suggested by actually competent person aka @dummdidumm * chore: rename function --- .changeset/wise-turkeys-yell.md | 5 ++++ .../src/internal/client/dev/ownership.js | 12 ++++++---- .../src/internal/client/reactivity/props.js | 2 +- .../Child.svelte | 5 ++++ .../Parent.svelte | 7 ++++++ .../_config.js | 11 +++++++++ .../main.svelte | 5 ++++ .../Parent.svelte | 8 +++++++ .../_config.js | 23 +++++++++++++++++++ .../main.svelte | 5 ++++ 10 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 .changeset/wise-turkeys-yell.md create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Parent.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/Parent.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/main.svelte diff --git a/.changeset/wise-turkeys-yell.md b/.changeset/wise-turkeys-yell.md new file mode 100644 index 0000000000..cd5e103de3 --- /dev/null +++ b/.changeset/wise-turkeys-yell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent ownership warnings if the fallback of a bindable is used diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index e28a40dd77..108c7adf92 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -27,7 +27,7 @@ export function create_ownership_validator(props) { */ mutation: (prop, path, result, line, column) => { const name = path[0]; - if (is_bound(props, name) || !parent) { + if (is_bound_or_unset(props, name) || !parent) { return result; } @@ -52,7 +52,7 @@ export function create_ownership_validator(props) { * @param {() => any} value */ binding: (key, child_component, value) => { - if (!is_bound(props, key) && parent && value()?.[STATE_SYMBOL]) { + if (!is_bound_or_unset(props, key) && parent && value()?.[STATE_SYMBOL]) { w.ownership_invalid_binding( component[FILENAME], key, @@ -68,9 +68,13 @@ export function create_ownership_validator(props) { * @param {Record<string, any>} props * @param {string} prop_name */ -function is_bound(props, prop_name) { +function is_bound_or_unset(props, prop_name) { // Can be the case when someone does `mount(Component, props)` with `let props = $state({...})` // or `createClassComponent(Component, props)` const is_entry_props = STATE_SYMBOL in props || LEGACY_PROPS in props; - return !!get_descriptor(props, prop_name)?.set || (is_entry_props && prop_name in props); + return ( + !!get_descriptor(props, prop_name)?.set || + (is_entry_props && prop_name in props) || + !(prop_name in props) + ); } diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index bd85b14df0..341d7c768a 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -7,7 +7,7 @@ import { PROPS_IS_RUNES, PROPS_IS_UPDATED } from '../../../constants.js'; -import { get_descriptor, is_function } from '../../shared/utils.js'; +import { define_property, get_descriptor, is_function } from '../../shared/utils.js'; import { mutable_source, set, source, update } from './sources.js'; import { derived, derived_safe_equal } from './deriveds.js'; import { get, captured_signals, untrack } from '../runtime.js'; diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Child.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Child.svelte new file mode 100644 index 0000000000..78b82caed9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Child.svelte @@ -0,0 +1,5 @@ +<script> + const { test = $bindable() } = $props(); +</script> + +{test} diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Parent.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Parent.svelte new file mode 100644 index 0000000000..7bfb17aa64 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/Parent.svelte @@ -0,0 +1,7 @@ +<script> + import Child from './Child.svelte'; + + let { test = $bindable({}) } = $props(); +</script> + +<Child bind:test /> diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/_config.js b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/_config.js new file mode 100644 index 0000000000..e93067eb9d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; + +export default test({ + mode: ['client'], + compileOptions: { + dev: true + }, + async test({ warnings, assert }) { + assert.deepEqual(warnings, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/main.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/main.svelte new file mode 100644 index 0000000000..282afb1771 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-binding-bindable-fallback/main.svelte @@ -0,0 +1,5 @@ +<script> + import Parent from './Parent.svelte'; +</script> + +<Parent /> diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/Parent.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/Parent.svelte new file mode 100644 index 0000000000..7d6b248da7 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/Parent.svelte @@ -0,0 +1,8 @@ +<script> + let { test = $bindable({}) } = $props(); +</script> + +<button onclick={()=>test = {}}></button> +<button onclick={()=>test.test = {}}></button> + +{test} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/_config.js b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/_config.js new file mode 100644 index 0000000000..9b4e3479ea --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/_config.js @@ -0,0 +1,23 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['client'], + compileOptions: { + dev: true + }, + async test({ warnings, assert, target }) { + const [btn, btn2] = target.querySelectorAll('button'); + flushSync(() => { + btn2.click(); + }); + assert.deepEqual(warnings, []); + flushSync(() => { + btn.click(); + }); + flushSync(() => { + btn2.click(); + }); + assert.deepEqual(warnings, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/main.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/main.svelte new file mode 100644 index 0000000000..282afb1771 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-bindable-fallback/main.svelte @@ -0,0 +1,5 @@ +<script> + import Parent from './Parent.svelte'; +</script> + +<Parent /> From 6d195f0350c5e46da0012525f92654c62257402e Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Thu, 10 Apr 2025 08:19:07 -0400 Subject: [PATCH 0325/1429] fix: handle hydration mismatches in await blocks (#15708) * failing test for #15704 * handle hydration mismatches in await blocks * DRY out * changeset * update test --- .changeset/wild-carrots-eat.md | 5 ++++ .../3-transform/server/visitors/AwaitBlock.js | 10 ++----- packages/svelte/src/constants.js | 1 + .../src/internal/client/dom/blocks/await.js | 29 +++++++++++++++++-- packages/svelte/src/internal/server/index.js | 7 +++-- .../await-hydrate-maybe-promise/_config.js | 23 +++++++++++++++ .../await-hydrate-maybe-promise/main.svelte | 25 ++++++++++++++++ .../_expected/server/index.svelte.js | 6 ++-- 8 files changed, 92 insertions(+), 14 deletions(-) create mode 100644 .changeset/wild-carrots-eat.md create mode 100644 packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/main.svelte diff --git a/.changeset/wild-carrots-eat.md b/.changeset/wild-carrots-eat.md new file mode 100644 index 0000000000..23b55f945c --- /dev/null +++ b/.changeset/wild-carrots-eat.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: handle hydration mismatches in await blocks diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js index 8fc82b8905..2aa534d257 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js @@ -2,7 +2,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ import * as b from '../../../../utils/builders.js'; -import { empty_comment } from './shared/utils.js'; +import { block_close } from './shared/utils.js'; /** * @param {AST.AwaitBlock} node @@ -10,10 +10,10 @@ import { empty_comment } from './shared/utils.js'; */ export function AwaitBlock(node, context) { context.state.template.push( - empty_comment, b.stmt( b.call( '$.await', + b.id('$$payload'), /** @type {Expression} */ (context.visit(node.expression)), b.thunk( node.pending ? /** @type {BlockStatement} */ (context.visit(node.pending)) : b.block([]) @@ -21,13 +21,9 @@ export function AwaitBlock(node, context) { b.arrow( node.value ? [/** @type {Pattern} */ (context.visit(node.value))] : [], node.then ? /** @type {BlockStatement} */ (context.visit(node.then)) : b.block([]) - ), - b.arrow( - node.error ? [/** @type {Pattern} */ (context.visit(node.error))] : [], - node.catch ? /** @type {BlockStatement} */ (context.visit(node.catch)) : b.block([]) ) ) ), - empty_comment + block_close ); } diff --git a/packages/svelte/src/constants.js b/packages/svelte/src/constants.js index 8861e440fc..6ea407d448 100644 --- a/packages/svelte/src/constants.js +++ b/packages/svelte/src/constants.js @@ -22,6 +22,7 @@ export const HYDRATION_START = '['; /** used to indicate that an `{:else}...` block was rendered */ export const HYDRATION_START_ELSE = '[!'; export const HYDRATION_END = ']'; +export const HYDRATION_AWAIT_THEN = '!'; export const HYDRATION_ERROR = {}; export const ELEMENT_IS_NAMESPACED = 1; diff --git a/packages/svelte/src/internal/client/dom/blocks/await.js b/packages/svelte/src/internal/client/dom/blocks/await.js index 2e3d229779..99bdc0000c 100644 --- a/packages/svelte/src/internal/client/dom/blocks/await.js +++ b/packages/svelte/src/internal/client/dom/blocks/await.js @@ -4,9 +4,16 @@ import { is_promise } from '../../../shared/utils.js'; import { block, branch, pause_effect, resume_effect } from '../../reactivity/effects.js'; import { internal_set, mutable_source, source } from '../../reactivity/sources.js'; import { flushSync, set_active_effect, set_active_reaction } from '../../runtime.js'; -import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; +import { + hydrate_next, + hydrate_node, + hydrating, + remove_nodes, + set_hydrate_node, + set_hydrating +} from '../hydration.js'; import { queue_micro_task } from '../task.js'; -import { UNINITIALIZED } from '../../../../constants.js'; +import { HYDRATION_START_ELSE, UNINITIALIZED } from '../../../../constants.js'; import { component_context, is_runes, @@ -113,6 +120,19 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) { var effect = block(() => { if (input === (input = get_input())) return; + /** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */ + // @ts-ignore coercing `anchor` to a `Comment` causes TypeScript and Prettier to fight + let mismatch = hydrating && is_promise(input) === (anchor.data === HYDRATION_START_ELSE); + + if (mismatch) { + // Hydration mismatch: remove everything inside the anchor and start fresh + anchor = remove_nodes(); + + set_hydrate_node(anchor); + set_hydrating(false); + mismatch = true; + } + if (is_promise(input)) { var promise = input; @@ -155,6 +175,11 @@ export function await_block(node, get_input, pending_fn, then_fn, catch_fn) { update(THEN, false); } + if (mismatch) { + // continue in hydration mode + set_hydrating(true); + } + // Set the input to something else, in order to disable the promise callbacks return () => (input = UNINITIALIZED); }); diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index bf36a595d8..ff34c07132 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -13,7 +13,7 @@ import { import { escape_html } from '../../escaping.js'; import { DEV } from 'esm-env'; import { current_component, pop, push } from './context.js'; -import { EMPTY_COMMENT, BLOCK_CLOSE, BLOCK_OPEN } from './hydration.js'; +import { EMPTY_COMMENT, BLOCK_CLOSE, BLOCK_OPEN, BLOCK_OPEN_ELSE } from './hydration.js'; import { validate_store } from '../shared/validate.js'; import { is_boolean_attribute, is_raw_text_element, is_void } from '../../utils.js'; import { reset_elements } from './dev.js'; @@ -474,18 +474,21 @@ export function bind_props(props_parent, props_now) { /** * @template V + * @param {Payload} payload * @param {Promise<V>} promise * @param {null | (() => void)} pending_fn * @param {(value: V) => void} then_fn * @returns {void} */ -function await_block(promise, pending_fn, then_fn) { +function await_block(payload, promise, pending_fn, then_fn) { if (is_promise(promise)) { + payload.out += BLOCK_OPEN; promise.then(null, noop); if (pending_fn !== null) { pending_fn(); } } else if (then_fn !== null) { + payload.out += BLOCK_OPEN_ELSE; then_fn(promise); } } diff --git a/packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/_config.js b/packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/_config.js new file mode 100644 index 0000000000..f81b41d41a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/_config.js @@ -0,0 +1,23 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + ssrHtml: '<button>fulfil</button><p>42</p><hr><p>loading...</p>', + html: '<button>fulfil</button><p>loading...</p><hr><p>42</p>', + + props: { + browser: true + }, + + server_props: { + browser: false + }, + + async test({ assert, target }) { + const button = target.querySelector('button'); + + flushSync(() => button?.click()); + await Promise.resolve(); + assert.htmlEqual(target.innerHTML, '<button>fulfil</button><p>42</p><hr><p>42</p>'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/main.svelte b/packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/main.svelte new file mode 100644 index 0000000000..d8d0cd4027 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/await-hydrate-maybe-promise/main.svelte @@ -0,0 +1,25 @@ +<script> + let { browser } = $props(); + + let fulfil; + let promise = new Promise((f) => (fulfil = f)); + + let a = browser ? promise : 42; + let b = browser ? 42 : promise; +</script> + +<button onclick={() => fulfil(42)}>fulfil</button> + +{#await a} + {#if true}<p>loading...</p>{/if} +{:then a} + <p>{a}</p> +{/await} + +<hr> + +{#await b} + {#if true}<p>loading...</p>{/if} +{:then b} + <p>{b}</p> +{/await} diff --git a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js index 012789a550..4b6e32d58e 100644 --- a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/server/index.svelte.js @@ -8,7 +8,7 @@ export default function Await_block_scope($$payload) { counter.count += 1; } - $$payload.out += `<button>clicks: ${$.escape(counter.count)}</button> <!---->`; - $.await(promise, () => {}, (counter) => {}, () => {}); - $$payload.out += `<!----> ${$.escape(counter.count)}`; + $$payload.out += `<button>clicks: ${$.escape(counter.count)}</button> `; + $.await($$payload, promise, () => {}, (counter) => {}); + $$payload.out += `<!--]--> ${$.escape(counter.count)}`; } \ No newline at end of file From c2c83b67f3470fa27afe998e079a48de47b7d9c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 15:39:50 -0400 Subject: [PATCH 0326/1429] Version Packages (#15726) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/wild-carrots-eat.md | 5 ----- .changeset/wise-turkeys-yell.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/wild-carrots-eat.md delete mode 100644 .changeset/wise-turkeys-yell.md diff --git a/.changeset/wild-carrots-eat.md b/.changeset/wild-carrots-eat.md deleted file mode 100644 index 23b55f945c..0000000000 --- a/.changeset/wild-carrots-eat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: handle hydration mismatches in await blocks diff --git a/.changeset/wise-turkeys-yell.md b/.changeset/wise-turkeys-yell.md deleted file mode 100644 index cd5e103de3..0000000000 --- a/.changeset/wise-turkeys-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: prevent ownership warnings if the fallback of a bindable is used diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 6f999f381e..3066c19bce 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.25.11 + +### Patch Changes + +- fix: handle hydration mismatches in await blocks ([#15708](https://github.com/sveltejs/svelte/pull/15708)) + +- fix: prevent ownership warnings if the fallback of a bindable is used ([#15720](https://github.com/sveltejs/svelte/pull/15720)) + ## 5.25.10 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index b9f434e688..187d3aaa2c 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.25.10", + "version": "5.25.11", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 2ea9890df9..cc5f8f775c 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.25.10'; +export const VERSION = '5.25.11'; export const PUBLIC_VERSION = '5'; From 9cafdd89d0e8f2fb0ce02e5b5112498218d4f2b5 Mon Sep 17 00:00:00 2001 From: Dominic Gannaway <trueadm@users.noreply.github.com> Date: Thu, 10 Apr 2025 22:18:38 +0100 Subject: [PATCH 0327/1429] fix: improve internal_set versioning mechanic (#15724) --- .changeset/pretty-planes-visit.md | 5 +++++ .../src/internal/client/reactivity/sources.js | 3 ++- .../samples/writable-derived-2/_config.js | 7 +++++++ .../samples/writable-derived-2/main.svelte | 9 +++++++++ .../samples/writable-derived-2/util.svelte.js | 17 +++++++++++++++++ 5 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 .changeset/pretty-planes-visit.md create mode 100644 packages/svelte/tests/runtime-runes/samples/writable-derived-2/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/writable-derived-2/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/writable-derived-2/util.svelte.js diff --git a/.changeset/pretty-planes-visit.md b/.changeset/pretty-planes-visit.md new file mode 100644 index 0000000000..d2ee0cae2e --- /dev/null +++ b/.changeset/pretty-planes-visit.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve internal_set versioning mechanic diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index cae49c1832..27e8fd824d 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -162,7 +162,6 @@ export function internal_set(source, value) { } source.v = value; - source.wv = increment_write_version(); if (DEV && tracing_mode_flag) { source.updated = get_stack('UpdatedAt'); @@ -180,6 +179,8 @@ export function internal_set(source, value) { set_signal_status(source, (source.f & UNOWNED) === 0 ? CLEAN : MAYBE_DIRTY); } + source.wv = increment_write_version(); + mark_reactions(source, DIRTY); // It's possible that the current reaction might not have up-to-date dependencies diff --git a/packages/svelte/tests/runtime-runes/samples/writable-derived-2/_config.js b/packages/svelte/tests/runtime-runes/samples/writable-derived-2/_config.js new file mode 100644 index 0000000000..fde3e7b1ea --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/writable-derived-2/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + html: `true true`, + + test({ assert, target, window }) {} +}); diff --git a/packages/svelte/tests/runtime-runes/samples/writable-derived-2/main.svelte b/packages/svelte/tests/runtime-runes/samples/writable-derived-2/main.svelte new file mode 100644 index 0000000000..741aa69125 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/writable-derived-2/main.svelte @@ -0,0 +1,9 @@ +<script> + import { expect2, createAppState } from "./util.svelte.js" + + const result = createAppState({ source: () => "wrong" }); + result.onChange("right"); + const expect1 = result.value === "right"; +</script> + +{expect1} {expect2} diff --git a/packages/svelte/tests/runtime-runes/samples/writable-derived-2/util.svelte.js b/packages/svelte/tests/runtime-runes/samples/writable-derived-2/util.svelte.js new file mode 100644 index 0000000000..8e862753ab --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/writable-derived-2/util.svelte.js @@ -0,0 +1,17 @@ +export const createAppState = (options) => { + const source = $derived(options.source()); + let value = $derived(source); + + return { + get value() { + return value; + }, + onChange(nextValue) { + value = nextValue; + } + }; +}; + +const result = createAppState({ source: () => 'wrong' }); +result.onChange('right'); +export const expect2 = result.value === 'right'; From 73acf6e7f48957d39d9e261083d652e89bbc94c3 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Thu, 10 Apr 2025 17:26:25 -0400 Subject: [PATCH 0328/1429] nicer sandbox output (#15730) --- playgrounds/sandbox/run.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playgrounds/sandbox/run.js b/playgrounds/sandbox/run.js index 8fa2c2a2da..3a62286b24 100644 --- a/playgrounds/sandbox/run.js +++ b/playgrounds/sandbox/run.js @@ -56,7 +56,7 @@ for (const generate of /** @type {const} */ (['client', 'server'])) { }); write( - `${cwd}/output/${file}.json`, + `${cwd}/output/ast/${file}.json`, JSON.stringify( ast, (key, value) => (typeof value === 'bigint' ? ['BigInt', value.toString()] : value), @@ -66,7 +66,7 @@ for (const generate of /** @type {const} */ (['client', 'server'])) { try { const migrated = migrate(source); - write(`${cwd}/output/${file}.migrated.svelte`, migrated.code); + write(`${cwd}/output/migrated/${file}`, migrated.code); } catch (e) { console.warn(`Error migrating ${file}`, e); } From 0d233e58cf4472cb922118ad76509500273f0d60 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Fri, 11 Apr 2025 00:17:22 +0200 Subject: [PATCH 0329/1429] fix: don't transform reassigned state in labeled statement in `$derived` (#15725) * fix: don't transform reassigned state in labeled statement in `$derived` * fix type so optional chaining is unnecessary * drive-by tidy up * drive-by tidy up --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/weak-doors-yell.md | 5 ++ packages/svelte/src/compiler/migrate/index.js | 72 +++++++++---------- .../input.svelte | 6 ++ .../output.svelte | 10 +++ 4 files changed, 57 insertions(+), 36 deletions(-) create mode 100644 .changeset/weak-doors-yell.md create mode 100644 packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/input.svelte create mode 100644 packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/output.svelte diff --git a/.changeset/weak-doors-yell.md b/.changeset/weak-doors-yell.md new file mode 100644 index 0000000000..1b21783435 --- /dev/null +++ b/.changeset/weak-doors-yell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't transform reassigned state in labeled statement in `$derived` diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 9d79d88b23..523389a25a 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -944,54 +944,53 @@ const instance_script = { node.body.type === 'ExpressionStatement' && node.body.expression.type === 'AssignmentExpression' ) { - const ids = extract_identifiers(node.body.expression.left); - const [, expression_ids] = extract_all_identifiers_from_expression( - node.body.expression.right - ); - const bindings = ids.map((id) => state.scope.get(id.name)); - const reassigned_bindings = bindings.filter((b) => b?.reassigned); + const { left, right } = node.body.expression; - if ( - node.body.expression.right.type !== 'Literal' && - !bindings.some((b) => b?.kind === 'store_sub') && - node.body.expression.left.type !== 'MemberExpression' - ) { - let { start, end } = /** @type {{ start: number, end: number }} */ ( - node.body.expression.right - ); + const ids = extract_identifiers(left); + const [, expression_ids] = extract_all_identifiers_from_expression(right); + const bindings = ids.map((id) => /** @type {Binding} */ (state.scope.get(id.name))); - check_rune_binding('derived'); + if (bindings.every((b) => b.kind === 'legacy_reactive')) { + if ( + right.type !== 'Literal' && + bindings.every((b) => b.kind !== 'store_sub') && + left.type !== 'MemberExpression' + ) { + let { start, end } = /** @type {{ start: number, end: number }} */ (right); - // $derived - state.str.update( - /** @type {number} */ (node.start), - /** @type {number} */ (node.body.expression.start), - 'let ' - ); + check_rune_binding('derived'); - if (node.body.expression.right.type === 'SequenceExpression') { - while (state.str.original[start] !== '(') start -= 1; - while (state.str.original[end - 1] !== ')') end += 1; - } + // $derived + state.str.update( + /** @type {number} */ (node.start), + /** @type {number} */ (node.body.expression.start), + 'let ' + ); + + if (right.type === 'SequenceExpression') { + while (state.str.original[start] !== '(') start -= 1; + while (state.str.original[end - 1] !== ')') end += 1; + } + + state.str.prependRight(start, `$derived(`); - state.str.prependRight(start, `$derived(`); + // in a case like `$: ({ a } = b())`, there's already a trailing parenthesis. + // otherwise, we need to add one + if (state.str.original[/** @type {number} */ (node.body.start)] !== '(') { + state.str.appendLeft(end, `)`); + } - // in a case like `$: ({ a } = b())`, there's already a trailing parenthesis. - // otherwise, we need to add one - if (state.str.original[/** @type {number} */ (node.body.start)] !== '(') { - state.str.appendLeft(end, `)`); + return; } - return; - } else { - for (const binding of reassigned_bindings) { - if (binding && (ids.includes(binding.node) || expression_ids.length === 0)) { + for (const binding of bindings) { + if (binding.reassigned && (ids.includes(binding.node) || expression_ids.length === 0)) { check_rune_binding('state'); const init = binding.kind === 'state' ? ' = $state()' : expression_ids.length === 0 - ? ` = $state(${state.str.original.substring(/** @type {number} */ (node.body.expression.right.start), node.body.expression.right.end)})` + ? ` = $state(${state.str.original.substring(/** @type {number} */ (right.start), right.end)})` : ''; // implicitly-declared variable which we need to make explicit state.str.prependLeft( @@ -1000,7 +999,8 @@ const instance_script = { ); } } - if (expression_ids.length === 0 && !bindings.some((b) => b?.kind === 'store_sub')) { + + if (expression_ids.length === 0 && bindings.every((b) => b.kind !== 'store_sub')) { state.str.remove(/** @type {number} */ (node.start), /** @type {number} */ (node.end)); return; } diff --git a/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/input.svelte b/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/input.svelte new file mode 100644 index 0000000000..0b5c13d889 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/input.svelte @@ -0,0 +1,6 @@ +<script> + let something = '123'; + + let foo = false; + $: foo = !!something; +</script> \ No newline at end of file diff --git a/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/output.svelte b/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/output.svelte new file mode 100644 index 0000000000..c2b36a6e30 --- /dev/null +++ b/packages/svelte/tests/migrate/samples/labeled-statement-reassign-state/output.svelte @@ -0,0 +1,10 @@ +<script> + import { run } from 'svelte/legacy'; + + let something = '123'; + + let foo = $state(false); + run(() => { + foo = !!something; + }); +</script> \ No newline at end of file From 0020e597e2137cdc8d6c913885978f0ccb966e3f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 18:39:23 -0400 Subject: [PATCH 0330/1429] Version Packages (#15729) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/pretty-planes-visit.md | 5 ----- .changeset/weak-doors-yell.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/pretty-planes-visit.md delete mode 100644 .changeset/weak-doors-yell.md diff --git a/.changeset/pretty-planes-visit.md b/.changeset/pretty-planes-visit.md deleted file mode 100644 index d2ee0cae2e..0000000000 --- a/.changeset/pretty-planes-visit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: improve internal_set versioning mechanic diff --git a/.changeset/weak-doors-yell.md b/.changeset/weak-doors-yell.md deleted file mode 100644 index 1b21783435..0000000000 --- a/.changeset/weak-doors-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: don't transform reassigned state in labeled statement in `$derived` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 3066c19bce..f27a6640bd 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.25.12 + +### Patch Changes + +- fix: improve internal_set versioning mechanic ([#15724](https://github.com/sveltejs/svelte/pull/15724)) + +- fix: don't transform reassigned state in labeled statement in `$derived` ([#15725](https://github.com/sveltejs/svelte/pull/15725)) + ## 5.25.11 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 187d3aaa2c..67f6186fc7 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.25.11", + "version": "5.25.12", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index cc5f8f775c..c554e9018c 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.25.11'; +export const VERSION = '5.25.12'; export const PUBLIC_VERSION = '5'; From ec1d85c89e345529b9108b770c68cf89b808f3b0 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Thu, 10 Apr 2025 16:50:05 -0700 Subject: [PATCH 0331/1429] fix: add snippet argument validation in dev (#15521) * init * fix * make `Payload` a class * doh * lint * tweak changeset * fix * only export things that should be available on $ * tweak message * fix --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/bright-jeans-compare.md | 5 ++ .../98-reference/.generated/shared-errors.md | 6 ++ .../svelte/messages/shared-errors/errors.md | 4 ++ .../client/visitors/SnippetBlock.js | 17 ++++- .../server/visitors/SnippetBlock.js | 5 +- .../src/internal/client/dev/validation.js | 15 +++++ packages/svelte/src/internal/client/index.js | 1 + .../src/internal/server/blocks/snippet.js | 2 +- packages/svelte/src/internal/server/dev.js | 13 +++- packages/svelte/src/internal/server/index.js | 57 ++--------------- .../svelte/src/internal/server/payload.js | 64 +++++++++++++++++++ .../svelte/src/internal/server/types.d.ts | 13 ---- packages/svelte/src/internal/shared/errors.js | 15 +++++ 13 files changed, 147 insertions(+), 70 deletions(-) create mode 100644 .changeset/bright-jeans-compare.md create mode 100644 packages/svelte/src/internal/client/dev/validation.js create mode 100644 packages/svelte/src/internal/server/payload.js diff --git a/.changeset/bright-jeans-compare.md b/.changeset/bright-jeans-compare.md new file mode 100644 index 0000000000..eaec658e03 --- /dev/null +++ b/.changeset/bright-jeans-compare.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: add snippet argument validation in dev diff --git a/documentation/docs/98-reference/.generated/shared-errors.md b/documentation/docs/98-reference/.generated/shared-errors.md index 0102aafcbc..4c81d7b894 100644 --- a/documentation/docs/98-reference/.generated/shared-errors.md +++ b/documentation/docs/98-reference/.generated/shared-errors.md @@ -30,6 +30,12 @@ This error would be thrown in a setup like this: Here, `List.svelte` is using `{@render children(item)` which means it expects `Parent.svelte` to use snippets. Instead, `Parent.svelte` uses the deprecated `let:` directive. This combination of APIs is incompatible, hence the error. +### invalid_snippet_arguments + +``` +A snippet function was passed invalid arguments. Snippets should only be instantiated via `{@render ...}` +``` + ### lifecycle_outside_component ``` diff --git a/packages/svelte/messages/shared-errors/errors.md b/packages/svelte/messages/shared-errors/errors.md index 8b4c61303a..20f3d193d9 100644 --- a/packages/svelte/messages/shared-errors/errors.md +++ b/packages/svelte/messages/shared-errors/errors.md @@ -26,6 +26,10 @@ This error would be thrown in a setup like this: Here, `List.svelte` is using `{@render children(item)` which means it expects `Parent.svelte` to use snippets. Instead, `Parent.svelte` uses the deprecated `let:` directive. This combination of APIs is incompatible, hence the error. +## invalid_snippet_arguments + +> A snippet function was passed invalid arguments. Snippets should only be instantiated via `{@render ...}` + ## lifecycle_outside_component > `%name%(...)` can only be used during component initialisation diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js index 7a0d6981b5..7eb043aa5d 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js @@ -1,4 +1,4 @@ -/** @import { BlockStatement, Expression, Identifier, Pattern, Statement } from 'estree' */ +/** @import { AssignmentPattern, BlockStatement, Expression, Identifier, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ import { dev } from '../../../../state.js'; @@ -12,7 +12,7 @@ import { get_value } from './shared/declarations.js'; */ export function SnippetBlock(node, context) { // TODO hoist where possible - /** @type {Pattern[]} */ + /** @type {(Identifier | AssignmentPattern)[]} */ const args = [b.id('$$anchor')]; /** @type {BlockStatement} */ @@ -66,7 +66,18 @@ export function SnippetBlock(node, context) { } } } - + if (dev) { + declarations.unshift( + b.stmt( + b.call( + '$.validate_snippet_args', + .../** @type {Identifier[]} */ ( + args.map((arg) => (arg?.type === 'Identifier' ? arg : arg?.left)) + ) + ) + ) + ); + } body = b.block([ ...declarations, .../** @type {BlockStatement} */ (context.visit(node.body, child_state)).body diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js index eb83917927..cae3e7d79c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js @@ -1,6 +1,7 @@ /** @import { BlockStatement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ +import { dev } from '../../../../state.js'; import * as b from '../../../../utils/builders.js'; /** @@ -13,7 +14,9 @@ export function SnippetBlock(node, context) { [b.id('$$payload'), ...node.parameters], /** @type {BlockStatement} */ (context.visit(node.body)) ); - + if (dev) { + fn.body.body.unshift(b.stmt(b.call('$.validate_snippet_args', b.id('$$payload')))); + } // @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone fn.___snippet = true; diff --git a/packages/svelte/src/internal/client/dev/validation.js b/packages/svelte/src/internal/client/dev/validation.js new file mode 100644 index 0000000000..e41e4c4628 --- /dev/null +++ b/packages/svelte/src/internal/client/dev/validation.js @@ -0,0 +1,15 @@ +import { invalid_snippet_arguments } from '../../shared/errors.js'; +/** + * @param {Node} anchor + * @param {...(()=>any)[]} args + */ +export function validate_snippet_args(anchor, ...args) { + if (typeof anchor !== 'object' || !(anchor instanceof Node)) { + invalid_snippet_arguments(); + } + for (let arg of args) { + if (typeof arg !== 'function') { + invalid_snippet_arguments(); + } + } +} diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index a865419c5f..e977bf3b0f 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -8,6 +8,7 @@ export { create_ownership_validator } from './dev/ownership.js'; export { check_target, legacy_api } from './dev/legacy.js'; export { trace } from './dev/tracing.js'; export { inspect } from './dev/inspect.js'; +export { validate_snippet_args } from './dev/validation.js'; export { await_block as await } from './dom/blocks/await.js'; export { if_block as if } from './dom/blocks/if.js'; export { key_block as key } from './dom/blocks/key.js'; diff --git a/packages/svelte/src/internal/server/blocks/snippet.js b/packages/svelte/src/internal/server/blocks/snippet.js index 3c5e860790..9e96ae3430 100644 --- a/packages/svelte/src/internal/server/blocks/snippet.js +++ b/packages/svelte/src/internal/server/blocks/snippet.js @@ -1,5 +1,5 @@ /** @import { Snippet } from 'svelte' */ -/** @import { Payload } from '#server' */ +/** @import { Payload } from '../payload' */ /** @import { Getters } from '#shared' */ /** diff --git a/packages/svelte/src/internal/server/dev.js b/packages/svelte/src/internal/server/dev.js index ecf4e67429..34849196b7 100644 --- a/packages/svelte/src/internal/server/dev.js +++ b/packages/svelte/src/internal/server/dev.js @@ -1,10 +1,12 @@ -/** @import { Component, Payload } from '#server' */ +/** @import { Component } from '#server' */ import { FILENAME } from '../../constants.js'; import { is_tag_valid_with_ancestor, is_tag_valid_with_parent } from '../../html-tree-validation.js'; import { current_component } from './context.js'; +import { invalid_snippet_arguments } from '../shared/errors.js'; +import { Payload } from './payload.js'; /** * @typedef {{ @@ -98,3 +100,12 @@ export function push_element(payload, tag, line, column) { export function pop_element() { parent = /** @type {Element} */ (parent).parent; } + +/** + * @param {Payload} payload + */ +export function validate_snippet_args(payload) { + if (typeof payload !== 'object' || !(payload instanceof Payload)) { + invalid_snippet_arguments(); + } +} diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index ff34c07132..d711778a44 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -1,5 +1,5 @@ /** @import { ComponentType, SvelteComponent } from 'svelte' */ -/** @import { Component, Payload, RenderOutput } from '#server' */ +/** @import { Component, RenderOutput } from '#server' */ /** @import { Store } from '#shared' */ export { FILENAME, HMR } from '../../constants.js'; import { attr, clsx, to_class, to_style } from '../shared/attributes.js'; @@ -17,43 +17,13 @@ import { EMPTY_COMMENT, BLOCK_CLOSE, BLOCK_OPEN, BLOCK_OPEN_ELSE } from './hydra import { validate_store } from '../shared/validate.js'; import { is_boolean_attribute, is_raw_text_element, is_void } from '../../utils.js'; import { reset_elements } from './dev.js'; +import { Payload } from './payload.js'; // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 // https://infra.spec.whatwg.org/#noncharacter const INVALID_ATTR_NAME_CHAR_REGEX = /[\s'">/=\u{FDD0}-\u{FDEF}\u{FFFE}\u{FFFF}\u{1FFFE}\u{1FFFF}\u{2FFFE}\u{2FFFF}\u{3FFFE}\u{3FFFF}\u{4FFFE}\u{4FFFF}\u{5FFFE}\u{5FFFF}\u{6FFFE}\u{6FFFF}\u{7FFFE}\u{7FFFF}\u{8FFFE}\u{8FFFF}\u{9FFFE}\u{9FFFF}\u{AFFFE}\u{AFFFF}\u{BFFFE}\u{BFFFF}\u{CFFFE}\u{CFFFF}\u{DFFFE}\u{DFFFF}\u{EFFFE}\u{EFFFF}\u{FFFFE}\u{FFFFF}\u{10FFFE}\u{10FFFF}]/u; -/** - * @param {Payload} to_copy - * @returns {Payload} - */ -export function copy_payload({ out, css, head, uid }) { - return { - out, - css: new Set(css), - head: { - title: head.title, - out: head.out, - css: new Set(head.css), - uid: head.uid - }, - uid - }; -} - -/** - * Assigns second payload to first - * @param {Payload} p1 - * @param {Payload} p2 - * @returns {void} - */ -export function assign_payload(p1, p2) { - p1.out = p2.out; - p1.css = p2.css; - p1.head = p2.head; - p1.uid = p2.uid; -} - /** * @param {Payload} payload * @param {string} tag @@ -87,16 +57,6 @@ export function element(payload, tag, attributes_fn = noop, children_fn = noop) */ export let on_destroy = []; -/** - * Creates an ID generator - * @param {string} prefix - * @returns {() => string} - */ -function props_id_generator(prefix) { - let uid = 1; - return () => `${prefix}s${uid++}`; -} - /** * Only available on the server and when compiling with the `server` option. * Takes a component and returns an object with `body` and `head` properties on it, which you can use to populate the HTML when server-rendering your app. @@ -106,14 +66,7 @@ function props_id_generator(prefix) { * @returns {RenderOutput} */ export function render(component, options = {}) { - const uid = props_id_generator(options.idPrefix ? options.idPrefix + '-' : ''); - /** @type {Payload} */ - const payload = { - out: '', - css: new Set(), - head: { title: '', out: '', css: new Set(), uid }, - uid - }; + const payload = new Payload(options.idPrefix ? options.idPrefix + '-' : ''); const prev_on_destroy = on_destroy; on_destroy = []; @@ -545,7 +498,9 @@ export { html } from './blocks/html.js'; export { push, pop } from './context.js'; -export { push_element, pop_element } from './dev.js'; +export { push_element, pop_element, validate_snippet_args } from './dev.js'; + +export { assign_payload, copy_payload } from './payload.js'; export { snapshot } from '../shared/clone.js'; diff --git a/packages/svelte/src/internal/server/payload.js b/packages/svelte/src/internal/server/payload.js new file mode 100644 index 0000000000..03bcaf492e --- /dev/null +++ b/packages/svelte/src/internal/server/payload.js @@ -0,0 +1,64 @@ +export class Payload { + /** @type {Set<{ hash: string; code: string }>} */ + css = new Set(); + out = ''; + uid = () => ''; + + head = { + /** @type {Set<{ hash: string; code: string }>} */ + css: new Set(), + title: '', + out: '', + uid: () => '' + }; + + constructor(id_prefix = '') { + this.uid = props_id_generator(id_prefix); + this.head.uid = this.uid; + } +} + +/** + * Used in legacy mode to handle bindings + * @param {Payload} to_copy + * @returns {Payload} + */ +export function copy_payload({ out, css, head, uid }) { + const payload = new Payload(); + + payload.out = out; + payload.css = new Set(css); + payload.uid = uid; + + payload.head = { + title: head.title, + out: head.out, + css: new Set(head.css), + uid: head.uid + }; + + return payload; +} + +/** + * Assigns second payload to first + * @param {Payload} p1 + * @param {Payload} p2 + * @returns {void} + */ +export function assign_payload(p1, p2) { + p1.out = p2.out; + p1.css = p2.css; + p1.head = p2.head; + p1.uid = p2.uid; +} + +/** + * Creates an ID generator + * @param {string} prefix + * @returns {() => string} + */ +function props_id_generator(prefix) { + let uid = 1; + return () => `${prefix}s${uid++}`; +} diff --git a/packages/svelte/src/internal/server/types.d.ts b/packages/svelte/src/internal/server/types.d.ts index 2fffdbbdf0..6b0fc146c4 100644 --- a/packages/svelte/src/internal/server/types.d.ts +++ b/packages/svelte/src/internal/server/types.d.ts @@ -11,19 +11,6 @@ export interface Component { function?: any; } -export interface Payload { - out: string; - css: Set<{ hash: string; code: string }>; - head: { - title: string; - out: string; - uid: () => string; - css: Set<{ hash: string; code: string }>; - }; - /** Function that generates a unique ID */ - uid: () => string; -} - export interface RenderOutput { /** HTML that goes into the `<head>` */ head: string; diff --git a/packages/svelte/src/internal/shared/errors.js b/packages/svelte/src/internal/shared/errors.js index 26d6822cdb..2e89dc1ad1 100644 --- a/packages/svelte/src/internal/shared/errors.js +++ b/packages/svelte/src/internal/shared/errors.js @@ -17,6 +17,21 @@ export function invalid_default_snippet() { } } +/** + * A snippet function was passed invalid arguments. Snippets should only be instantiated via `{@render ...}` + * @returns {never} + */ +export function invalid_snippet_arguments() { + if (DEV) { + const error = new Error(`invalid_snippet_arguments\nA snippet function was passed invalid arguments. Snippets should only be instantiated via \`{@render ...}\`\nhttps://svelte.dev/e/invalid_snippet_arguments`); + + error.name = 'Svelte error'; + throw error; + } else { + throw new Error(`https://svelte.dev/e/invalid_snippet_arguments`); + } +} + /** * `%name%(...)` can only be used during component initialisation * @param {string} name From 01171096cef8652704b92053e57156ae4ed9e5d9 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Fri, 11 Apr 2025 04:42:44 +0200 Subject: [PATCH 0332/1429] feat: add `css.hasGlobal` to `compile` output (#15450) * feat: add `hasUnscopedGlobalCss` to `compile` metadata * chore: rename to `has_unscoped_global` * fix: handle `-global` keyframes * chore: guard the check if the value is already true * update types * add tests * tweak * tweak * regenerate types * Update .changeset/plenty-hotels-mix.md * fix test, add failing test * fix * fix * fix jsdoc * unused * fix * lint * rename * rename * reduce indirection * tidy up * revert * tweak * lint --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/plenty-hotels-mix.md | 5 ++ .../src/compiler/phases/1-parse/read/style.js | 2 + .../phases/2-analyze/css/css-analyze.js | 70 ++++++++++++++----- .../src/compiler/phases/2-analyze/index.js | 3 +- .../compiler/phases/3-transform/css/index.js | 3 +- .../svelte/src/compiler/phases/types.d.ts | 1 + packages/svelte/src/compiler/types/css.d.ts | 5 ++ packages/svelte/src/compiler/types/index.d.ts | 2 + .../css/samples/global-keyframes/_config.js | 5 ++ .../samples/global-local-nested/_config.js | 5 ++ .../samples/global-local-nested/expected.css | 12 ++++ .../samples/global-local-nested/input.svelte | 15 ++++ .../tests/css/samples/global-local/_config.js | 5 ++ .../css/samples/global-local/expected.css | 8 +++ .../css/samples/global-local/input.svelte | 11 +++ .../samples/global-with-nesting/_config.js | 4 +- .../tests/css/samples/global/_config.js | 5 ++ packages/svelte/tests/css/test.ts | 9 +++ packages/svelte/tests/helpers.js | 4 ++ packages/svelte/types/index.d.ts | 2 + 20 files changed, 155 insertions(+), 21 deletions(-) create mode 100644 .changeset/plenty-hotels-mix.md create mode 100644 packages/svelte/tests/css/samples/global-keyframes/_config.js create mode 100644 packages/svelte/tests/css/samples/global-local-nested/_config.js create mode 100644 packages/svelte/tests/css/samples/global-local-nested/expected.css create mode 100644 packages/svelte/tests/css/samples/global-local-nested/input.svelte create mode 100644 packages/svelte/tests/css/samples/global-local/_config.js create mode 100644 packages/svelte/tests/css/samples/global-local/expected.css create mode 100644 packages/svelte/tests/css/samples/global-local/input.svelte create mode 100644 packages/svelte/tests/css/samples/global/_config.js diff --git a/.changeset/plenty-hotels-mix.md b/.changeset/plenty-hotels-mix.md new file mode 100644 index 0000000000..5e7aa834da --- /dev/null +++ b/.changeset/plenty-hotels-mix.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: add `css.hasGlobal` to `compile` output diff --git a/packages/svelte/src/compiler/phases/1-parse/read/style.js b/packages/svelte/src/compiler/phases/1-parse/read/style.js index f8c39f1b1d..56dbe124b7 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/style.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/style.js @@ -118,6 +118,7 @@ function read_rule(parser) { metadata: { parent_rule: null, has_local_selectors: false, + has_global_selectors: false, is_global_block: false } }; @@ -342,6 +343,7 @@ function read_selector(parser, inside_pseudo_class = false) { children, metadata: { rule: null, + is_global: false, used: false } }; diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js index 362ac9dcad..76cb2f56e9 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js @@ -7,13 +7,15 @@ import { is_keyframes_node } from '../../css.js'; import { is_global, is_unscoped_pseudo_class } from './utils.js'; /** - * @typedef {Visitors< - * AST.CSS.Node, - * { - * keyframes: string[]; - * rule: AST.CSS.Rule | null; - * } - * >} CssVisitors + * @typedef {{ + * keyframes: string[]; + * rule: AST.CSS.Rule | null; + * analysis: ComponentAnalysis; + * }} CssState + */ + +/** + * @typedef {Visitors<AST.CSS.Node, CssState>} CssVisitors */ /** @@ -28,6 +30,15 @@ function is_global_block_selector(simple_selector) { ); } +/** + * @param {AST.SvelteNode[]} path + */ +function is_unscoped(path) { + return path + .filter((node) => node.type === 'Rule') + .every((node) => node.metadata.has_global_selectors); +} + /** * * @param {Array<AST.CSS.Node>} path @@ -42,6 +53,9 @@ const css_visitors = { if (is_keyframes_node(node)) { if (!node.prelude.startsWith('-global-') && !is_in_global_block(context.path)) { context.state.keyframes.push(node.prelude); + } else if (node.prelude.startsWith('-global-')) { + // we don't check if the block.children.length because the keyframe is still added even if empty + context.state.analysis.css.has_global ||= is_unscoped(context.path); } } @@ -99,10 +113,12 @@ const css_visitors = { node.metadata.rule = context.state.rule; - node.metadata.used ||= node.children.every( + node.metadata.is_global = node.children.every( ({ metadata }) => metadata.is_global || metadata.is_global_like ); + node.metadata.used ||= node.metadata.is_global; + if ( node.metadata.rule?.metadata.parent_rule && node.children[0]?.selectors[0]?.type === 'NestingSelector' @@ -190,6 +206,7 @@ const css_visitors = { if (idx !== -1) { is_global_block = true; + for (let i = idx + 1; i < child.selectors.length; i++) { walk(/** @type {AST.CSS.Node} */ (child.selectors[i]), null, { ComplexSelector(node) { @@ -242,16 +259,26 @@ const css_visitors = { } } - context.next({ - ...context.state, - rule: node - }); + const state = { ...context.state, rule: node }; - node.metadata.has_local_selectors = node.prelude.children.some((selector) => { - return selector.children.some( - ({ metadata }) => !metadata.is_global && !metadata.is_global_like - ); - }); + // visit selector list first, to populate child selector metadata + context.visit(node.prelude, state); + + for (const selector of node.prelude.children) { + node.metadata.has_global_selectors ||= selector.metadata.is_global; + node.metadata.has_local_selectors ||= !selector.metadata.is_global; + } + + // if this rule has a ComplexSelector whose RelativeSelector children are all + // `:global(...)`, and the rule contains declarations (rather than just + // nested rules) then the component as a whole includes global CSS + context.state.analysis.css.has_global ||= + node.metadata.has_global_selectors && + node.block.children.filter((child) => child.type === 'Declaration').length > 0 && + is_unscoped(context.path); + + // visit block list, so parent rule metadata is populated + context.visit(node.block, state); }, NestingSelector(node, context) { const rule = /** @type {AST.CSS.Rule} */ (context.state.rule); @@ -289,5 +316,12 @@ const css_visitors = { * @param {ComponentAnalysis} analysis */ export function analyze_css(stylesheet, analysis) { - walk(stylesheet, { keyframes: analysis.css.keyframes, rule: null }, css_visitors); + /** @type {CssState} */ + const css_state = { + keyframes: analysis.css.keyframes, + rule: null, + analysis + }; + + walk(stylesheet, css_state, css_visitors); } diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index c62fb03e8f..a6eb9565cb 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -456,7 +456,8 @@ export function analyze_component(root, source, options) { hash }) : '', - keyframes: [] + keyframes: [], + has_global: false }, source, undefined_exports: new Map(), diff --git a/packages/svelte/src/compiler/phases/3-transform/css/index.js b/packages/svelte/src/compiler/phases/3-transform/css/index.js index 5b0dcd5588..dff034f8aa 100644 --- a/packages/svelte/src/compiler/phases/3-transform/css/index.js +++ b/packages/svelte/src/compiler/phases/3-transform/css/index.js @@ -59,7 +59,8 @@ export function render_stylesheet(source, analysis, options) { // generateMap takes care of calculating source relative to file source: options.filename, file: options.cssOutputFilename || options.filename - }) + }), + hasGlobal: analysis.css.has_global }; merge_with_preprocessor_map(css, options, css.map.sources[0]); diff --git a/packages/svelte/src/compiler/phases/types.d.ts b/packages/svelte/src/compiler/phases/types.d.ts index f09b881303..f98cbe1415 100644 --- a/packages/svelte/src/compiler/phases/types.d.ts +++ b/packages/svelte/src/compiler/phases/types.d.ts @@ -74,6 +74,7 @@ export interface ComponentAnalysis extends Analysis { ast: AST.CSS.StyleSheet | null; hash: string; keyframes: string[]; + has_global: boolean; }; source: string; undefined_exports: Map<string, Node>; diff --git a/packages/svelte/src/compiler/types/css.d.ts b/packages/svelte/src/compiler/types/css.d.ts index 7b2e6ae5f7..154a06ffb1 100644 --- a/packages/svelte/src/compiler/types/css.d.ts +++ b/packages/svelte/src/compiler/types/css.d.ts @@ -34,6 +34,10 @@ export namespace _CSS { metadata: { parent_rule: null | Rule; has_local_selectors: boolean; + /** + * `true` if the rule contains a ComplexSelector whose RelativeSelectors are all global or global-like + */ + has_global_selectors: boolean; /** * `true` if the rule contains a `:global` selector, and therefore everything inside should be unscoped */ @@ -64,6 +68,7 @@ export namespace _CSS { /** @internal */ metadata: { rule: null | Rule; + is_global: boolean; /** True if this selector applies to an element. For global selectors, this is defined in css-analyze, for others in css-prune while scoping */ used: boolean; }; diff --git a/packages/svelte/src/compiler/types/index.d.ts b/packages/svelte/src/compiler/types/index.d.ts index eec41bad9d..616c346ad3 100644 --- a/packages/svelte/src/compiler/types/index.d.ts +++ b/packages/svelte/src/compiler/types/index.d.ts @@ -18,6 +18,8 @@ export interface CompileResult { code: string; /** A source map */ map: SourceMap; + /** Whether or not the CSS includes global rules */ + hasGlobal: boolean; }; /** * An array of warning objects that were generated during compilation. Each warning has several properties: diff --git a/packages/svelte/tests/css/samples/global-keyframes/_config.js b/packages/svelte/tests/css/samples/global-keyframes/_config.js new file mode 100644 index 0000000000..30953854ad --- /dev/null +++ b/packages/svelte/tests/css/samples/global-keyframes/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + hasGlobal: true +}); diff --git a/packages/svelte/tests/css/samples/global-local-nested/_config.js b/packages/svelte/tests/css/samples/global-local-nested/_config.js new file mode 100644 index 0000000000..5a7796ebac --- /dev/null +++ b/packages/svelte/tests/css/samples/global-local-nested/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + hasGlobal: false +}); diff --git a/packages/svelte/tests/css/samples/global-local-nested/expected.css b/packages/svelte/tests/css/samples/global-local-nested/expected.css new file mode 100644 index 0000000000..8eadf2b948 --- /dev/null +++ b/packages/svelte/tests/css/samples/global-local-nested/expected.css @@ -0,0 +1,12 @@ + + div.svelte-xyz { + .whatever { + color: green; + } + } + + .whatever { + div.svelte-xyz { + color: green; + } + } diff --git a/packages/svelte/tests/css/samples/global-local-nested/input.svelte b/packages/svelte/tests/css/samples/global-local-nested/input.svelte new file mode 100644 index 0000000000..60210be753 --- /dev/null +++ b/packages/svelte/tests/css/samples/global-local-nested/input.svelte @@ -0,0 +1,15 @@ +<div>{@html whatever}</div> + +<style> + div { + :global(.whatever) { + color: green; + } + } + + :global(.whatever) { + div { + color: green; + } + } +</style> diff --git a/packages/svelte/tests/css/samples/global-local/_config.js b/packages/svelte/tests/css/samples/global-local/_config.js new file mode 100644 index 0000000000..5a7796ebac --- /dev/null +++ b/packages/svelte/tests/css/samples/global-local/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + hasGlobal: false +}); diff --git a/packages/svelte/tests/css/samples/global-local/expected.css b/packages/svelte/tests/css/samples/global-local/expected.css new file mode 100644 index 0000000000..c4fc74fb1a --- /dev/null +++ b/packages/svelte/tests/css/samples/global-local/expected.css @@ -0,0 +1,8 @@ + + div.svelte-xyz .whatever { + color: green; + } + + .whatever div.svelte-xyz { + color: green; + } diff --git a/packages/svelte/tests/css/samples/global-local/input.svelte b/packages/svelte/tests/css/samples/global-local/input.svelte new file mode 100644 index 0000000000..bff97ab485 --- /dev/null +++ b/packages/svelte/tests/css/samples/global-local/input.svelte @@ -0,0 +1,11 @@ +<div>{@html whatever}</div> + +<style> + div :global(.whatever) { + color: green; + } + + :global(.whatever) div { + color: green; + } +</style> diff --git a/packages/svelte/tests/css/samples/global-with-nesting/_config.js b/packages/svelte/tests/css/samples/global-with-nesting/_config.js index 292c6c49ac..6cec7c2360 100644 --- a/packages/svelte/tests/css/samples/global-with-nesting/_config.js +++ b/packages/svelte/tests/css/samples/global-with-nesting/_config.js @@ -1,5 +1,7 @@ import { test } from '../../test'; export default test({ - warnings: [] + warnings: [], + + hasGlobal: false }); diff --git a/packages/svelte/tests/css/samples/global/_config.js b/packages/svelte/tests/css/samples/global/_config.js new file mode 100644 index 0000000000..30953854ad --- /dev/null +++ b/packages/svelte/tests/css/samples/global/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + hasGlobal: true +}); diff --git a/packages/svelte/tests/css/test.ts b/packages/svelte/tests/css/test.ts index dd51f52eab..8846b1d986 100644 --- a/packages/svelte/tests/css/test.ts +++ b/packages/svelte/tests/css/test.ts @@ -34,6 +34,7 @@ interface CssTest extends BaseTest { compileOptions?: Partial<CompileOptions>; warnings?: Warning[]; props?: Record<string, any>; + hasGlobal?: boolean; } /** @@ -78,6 +79,14 @@ const { test, run } = suite<CssTest>(async (config, cwd) => { // assert_html_equal(actual_ssr, expected.html); } + if (config.hasGlobal !== undefined) { + const metadata = JSON.parse( + fs.readFileSync(`${cwd}/_output/client/input.svelte.css.json`, 'utf-8') + ); + + assert.equal(metadata.hasGlobal, config.hasGlobal); + } + const dom_css = fs.readFileSync(`${cwd}/_output/client/input.svelte.css`, 'utf-8').trim(); const ssr_css = fs.readFileSync(`${cwd}/_output/server/input.svelte.css`, 'utf-8').trim(); diff --git a/packages/svelte/tests/helpers.js b/packages/svelte/tests/helpers.js index 87bcb473e7..f853d5873c 100644 --- a/packages/svelte/tests/helpers.js +++ b/packages/svelte/tests/helpers.js @@ -146,6 +146,10 @@ export async function compile_directory( if (compiled.css) { write(`${output_dir}/${file}.css`, compiled.css.code); + write( + `${output_dir}/${file}.css.json`, + JSON.stringify({ hasGlobal: compiled.css.hasGlobal }) + ); if (output_map) { write(`${output_dir}/${file}.css.map`, JSON.stringify(compiled.css.map, null, '\t')); } diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index c6000fc4b6..6f12daf187 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -753,6 +753,8 @@ declare module 'svelte/compiler' { code: string; /** A source map */ map: SourceMap; + /** Whether or not the CSS includes global rules */ + hasGlobal: boolean; }; /** * An array of warning objects that were generated during compilation. Each warning has several properties: From 02448a9acdf781ea44b696d4a60f387b8856e82c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 23:19:23 -0400 Subject: [PATCH 0333/1429] Version Packages (#15731) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/bright-jeans-compare.md | 5 ----- .changeset/plenty-hotels-mix.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 .changeset/bright-jeans-compare.md delete mode 100644 .changeset/plenty-hotels-mix.md diff --git a/.changeset/bright-jeans-compare.md b/.changeset/bright-jeans-compare.md deleted file mode 100644 index eaec658e03..0000000000 --- a/.changeset/bright-jeans-compare.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: add snippet argument validation in dev diff --git a/.changeset/plenty-hotels-mix.md b/.changeset/plenty-hotels-mix.md deleted file mode 100644 index 5e7aa834da..0000000000 --- a/.changeset/plenty-hotels-mix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: add `css.hasGlobal` to `compile` output diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index f27a6640bd..68a8803663 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.26.0 + +### Minor Changes + +- feat: add `css.hasGlobal` to `compile` output ([#15450](https://github.com/sveltejs/svelte/pull/15450)) + +### Patch Changes + +- fix: add snippet argument validation in dev ([#15521](https://github.com/sveltejs/svelte/pull/15521)) + ## 5.25.12 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 67f6186fc7..8267cb5218 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.25.12", + "version": "5.26.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index c554e9018c..ade51aaf17 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.25.12'; +export const VERSION = '5.26.0'; export const PUBLIC_VERSION = '5'; From 95a020acead1a403ae2d4320bea57227e52143ec Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 11 Apr 2025 10:01:51 -0400 Subject: [PATCH 0334/1429] fix: update `state_referenced_locally` message (#15733) * fix: update state_referenced_locally message * changeset * update message --- .changeset/stale-gorillas-judge.md | 5 ++++ .../.generated/compile-warnings.md | 9 +++--- .../messages/compile-warnings/script.md | 9 +++--- .../phases/2-analyze/visitors/Identifier.js | 30 ++++++++++++++++++- packages/svelte/src/compiler/warnings.js | 8 +++-- .../static-state-reference/input.svelte | 1 + .../static-state-reference/warnings.json | 24 +++++++++++---- 7 files changed, 68 insertions(+), 18 deletions(-) create mode 100644 .changeset/stale-gorillas-judge.md diff --git a/.changeset/stale-gorillas-judge.md b/.changeset/stale-gorillas-judge.md new file mode 100644 index 0000000000..3d91f401dd --- /dev/null +++ b/.changeset/stale-gorillas-judge.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: update `state_referenced_locally` message diff --git a/documentation/docs/98-reference/.generated/compile-warnings.md b/documentation/docs/98-reference/.generated/compile-warnings.md index 57396bd7fd..0e94cbadb2 100644 --- a/documentation/docs/98-reference/.generated/compile-warnings.md +++ b/documentation/docs/98-reference/.generated/compile-warnings.md @@ -823,15 +823,16 @@ See [the migration guide](v5-migration-guide#Snippets-instead-of-slots) for more ### state_referenced_locally ``` -State referenced in its own scope will never update. Did you mean to reference it inside a closure? +This reference only captures the initial value of `%name%`. Did you mean to reference it inside a %type% instead? ``` This warning is thrown when the compiler detects the following: + - A reactive variable is declared -- the variable is reassigned -- the variable is referenced inside the same scope it is declared and it is a non-reactive context +- ...and later reassigned... +- ...and referenced in the same scope -In this case, the state reassignment will not be noticed by whatever you passed it to. For example, if you pass the state to a function, that function will not notice the updates: +This 'breaks the link' to the original state declaration. For example, if you pass the state to a function, the function loses access to the state once it is reassigned: ```svelte <!--- file: Parent.svelte ---> diff --git a/packages/svelte/messages/compile-warnings/script.md b/packages/svelte/messages/compile-warnings/script.md index 8c32fb7082..6603759156 100644 --- a/packages/svelte/messages/compile-warnings/script.md +++ b/packages/svelte/messages/compile-warnings/script.md @@ -54,14 +54,15 @@ To fix this, wrap your variable declaration with `$state`. ## state_referenced_locally -> State referenced in its own scope will never update. Did you mean to reference it inside a closure? +> This reference only captures the initial value of `%name%`. Did you mean to reference it inside a %type% instead? This warning is thrown when the compiler detects the following: + - A reactive variable is declared -- the variable is reassigned -- the variable is referenced inside the same scope it is declared and it is a non-reactive context +- ...and later reassigned... +- ...and referenced in the same scope -In this case, the state reassignment will not be noticed by whatever you passed it to. For example, if you pass the state to a function, that function will not notice the updates: +This 'breaks the link' to the original state declaration. For example, if you pass the state to a function, the function loses access to the state once it is reassigned: ```svelte <!--- file: Parent.svelte ---> diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js index 79dccd5a7c..dcbe564543 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js @@ -7,6 +7,7 @@ import * as e from '../../../errors.js'; import * as w from '../../../warnings.js'; import { is_rune } from '../../../../utils.js'; import { mark_subtree_dynamic } from './shared/fragment.js'; +import { get_rune } from '../../scope.js'; /** * @param {Identifier} node @@ -111,7 +112,34 @@ export function Identifier(node, context) { (parent.type !== 'AssignmentExpression' || parent.left !== node) && parent.type !== 'UpdateExpression' ) { - w.state_referenced_locally(node); + let type = 'closure'; + + let i = context.path.length; + while (i--) { + const parent = context.path[i]; + + if ( + parent.type === 'ArrowFunctionExpression' || + parent.type === 'FunctionDeclaration' || + parent.type === 'FunctionExpression' + ) { + break; + } + + if ( + parent.type === 'CallExpression' && + parent.arguments.includes(/** @type {any} */ (context.path[i + 1])) + ) { + const rune = get_rune(parent, context.state.scope); + + if (rune === '$state' || rune === '$state.raw') { + type = 'derived'; + break; + } + } + } + + w.state_referenced_locally(node, node.name, type); } if ( diff --git a/packages/svelte/src/compiler/warnings.js b/packages/svelte/src/compiler/warnings.js index a9ea617d3f..e6fc8caba5 100644 --- a/packages/svelte/src/compiler/warnings.js +++ b/packages/svelte/src/compiler/warnings.js @@ -641,11 +641,13 @@ export function reactive_declaration_module_script_dependency(node) { } /** - * State referenced in its own scope will never update. Did you mean to reference it inside a closure? + * This reference only captures the initial value of `%name%`. Did you mean to reference it inside a %type% instead? * @param {null | NodeLike} node + * @param {string} name + * @param {string} type */ -export function state_referenced_locally(node) { - w(node, 'state_referenced_locally', `State referenced in its own scope will never update. Did you mean to reference it inside a closure?\nhttps://svelte.dev/e/state_referenced_locally`); +export function state_referenced_locally(node, name, type) { + w(node, 'state_referenced_locally', `This reference only captures the initial value of \`${name}\`. Did you mean to reference it inside a ${type} instead?\nhttps://svelte.dev/e/state_referenced_locally`); } /** diff --git a/packages/svelte/tests/validator/samples/static-state-reference/input.svelte b/packages/svelte/tests/validator/samples/static-state-reference/input.svelte index cd0c7b7349..577527ee60 100644 --- a/packages/svelte/tests/validator/samples/static-state-reference/input.svelte +++ b/packages/svelte/tests/validator/samples/static-state-reference/input.svelte @@ -2,6 +2,7 @@ let obj = $state({ a: 0 }); let count = $state(0); let doubled = $derived(count * 2); + let tripled = $state(count * 3); console.log(obj); console.log(count); diff --git a/packages/svelte/tests/validator/samples/static-state-reference/warnings.json b/packages/svelte/tests/validator/samples/static-state-reference/warnings.json index 9ba0941519..a118d5e4a0 100644 --- a/packages/svelte/tests/validator/samples/static-state-reference/warnings.json +++ b/packages/svelte/tests/validator/samples/static-state-reference/warnings.json @@ -1,26 +1,38 @@ [ { "code": "state_referenced_locally", - "message": "State referenced in its own scope will never update. Did you mean to reference it inside a closure?", + "message": "This reference only captures the initial value of `count`. Did you mean to reference it inside a derived instead?", + "start": { + "column": 22, + "line": 5 + }, + "end": { + "column": 27, + "line": 5 + } + }, + { + "code": "state_referenced_locally", + "message": "This reference only captures the initial value of `count`. Did you mean to reference it inside a closure instead?", "start": { "column": 13, - "line": 7 + "line": 8 }, "end": { "column": 18, - "line": 7 + "line": 8 } }, { "code": "state_referenced_locally", - "message": "State referenced in its own scope will never update. Did you mean to reference it inside a closure?", + "message": "This reference only captures the initial value of `doubled`. Did you mean to reference it inside a closure instead?", "start": { "column": 13, - "line": 8 + "line": 9 }, "end": { "column": 20, - "line": 8 + "line": 9 } } ] From 9b2507131ca0134b59cef7da1fdd59603c519f0a Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 11 Apr 2025 10:50:11 -0400 Subject: [PATCH 0335/1429] chore: remove unused documentation markdown files (#15738) --- .../docs/01-introduction/xx-props.md | 139 ----------------- .../xx-reactivity-fundamentals.md | 144 ------------------ .../03-template-syntax/xx-control-flow.md | 111 -------------- .../03-template-syntax/xx-data-fetching.md | 20 --- .../docs/07-misc/xx-reactivity-indepth.md | 6 - 5 files changed, 420 deletions(-) delete mode 100644 documentation/docs/01-introduction/xx-props.md delete mode 100644 documentation/docs/01-introduction/xx-reactivity-fundamentals.md delete mode 100644 documentation/docs/03-template-syntax/xx-control-flow.md delete mode 100644 documentation/docs/03-template-syntax/xx-data-fetching.md delete mode 100644 documentation/docs/07-misc/xx-reactivity-indepth.md diff --git a/documentation/docs/01-introduction/xx-props.md b/documentation/docs/01-introduction/xx-props.md deleted file mode 100644 index cad854d878..0000000000 --- a/documentation/docs/01-introduction/xx-props.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: Public API of a component ---- - -### Public API of a component - -Svelte uses the `$props` rune to declare _properties_ or _props_, which means describing the public interface of the component which becomes accessible to consumers of the component. - -> [!NOTE] `$props` is one of several runes, which are special hints for Svelte's compiler to make things reactive. - -```svelte -<script> - let { foo, bar, baz } = $props(); - - // Values that are passed in as props - // are immediately available - console.log({ foo, bar, baz }); -</script> -``` - -You can specify a fallback value for a prop. It will be used if the component's consumer doesn't specify the prop on the component when instantiating the component, or if the passed value is `undefined` at some point. - -```svelte -<script> - let { foo = 'optional default initial value' } = $props(); -</script> -``` - -To get all properties, use rest syntax: - -```svelte -<script> - let { a, b, c, ...everythingElse } = $props(); -</script> -``` - -You can use reserved words as prop names. - -```svelte -<script> - // creates a `class` property, even - // though it is a reserved word - let { class: className } = $props(); -</script> -``` - -If you're using TypeScript, you can declare the prop types: - -```svelte -<script lang="ts"> - interface Props { - required: string; - optional?: number; - [key: string]: unknown; - } - - let { required, optional, ...everythingElse }: Props = $props(); -</script> -``` - -If you're using JavaScript, you can declare the prop types using JSDoc: - -```svelte -<script> - /** @type {{ x: string }} */ - let { x } = $props(); - - // or use @typedef if you want to document the properties: - - /** - * @typedef {Object} MyProps - * @property {string} y Some documentation - */ - - /** @type {MyProps} */ - let { y } = $props(); -</script> -``` - -If you export a `const`, `class` or `function`, it is readonly from outside the component. - -```svelte -<script> - export const thisIs = 'readonly'; - - export function greet(name) { - alert(`hello ${name}!`); - } -</script> -``` - -Readonly props can be accessed as properties on the element, tied to the component using [`bind:this` syntax](bindings#bind:this). - -### Reactive variables - -To change component state and trigger a re-render, just assign to a locally declared variable that was declared using the `$state` rune. - -Update expressions (`count += 1`) and property assignments (`obj.x = y`) have the same effect. - -```svelte -<script> - let count = $state(0); - - function handleClick() { - // calling this function will trigger an - // update if the markup references `count` - count = count + 1; - } -</script> -``` - -Svelte's `<script>` blocks are run only when the component is created, so assignments within a `<script>` block are not automatically run again when a prop updates. - -```svelte -<script> - let { person } = $props(); - // this will only set `name` on component creation - // it will not update when `person` does - let { name } = person; -</script> -``` - -If you'd like to react to changes to a prop, use the `$derived` or `$effect` runes instead. - -```svelte -<script> - let count = $state(0); - - let double = $derived(count * 2); - - $effect(() => { - if (count > 10) { - alert('Too high!'); - } - }); -</script> -``` - -For more information on reactivity, read the documentation around runes. diff --git a/documentation/docs/01-introduction/xx-reactivity-fundamentals.md b/documentation/docs/01-introduction/xx-reactivity-fundamentals.md deleted file mode 100644 index d5e67ada71..0000000000 --- a/documentation/docs/01-introduction/xx-reactivity-fundamentals.md +++ /dev/null @@ -1,144 +0,0 @@ ---- -title: Reactivity fundamentals ---- - -Reactivity is at the heart of interactive UIs. When you click a button, you expect some kind of response. It's your job as a developer to make this happen. It's Svelte's job to make your job as intuitive as possible, by providing a good API to express reactive systems. - -## Runes - -Svelte 5 uses _runes_, a powerful set of primitives for controlling reactivity inside your Svelte components and inside `.svelte.js` and `.svelte.ts` modules. - -Runes are function-like symbols that provide instructions to the Svelte compiler. You don't need to import them from anywhere — when you use Svelte, they're part of the language. - -The following sections introduce the most important runes for declare state, derived state and side effects at a high level. For more details refer to the later sections on [state](state) and [side effects](side-effects). - -## `$state` - -Reactive state is declared with the `$state` rune: - -```svelte -<script> - let count = $state(0); -</script> - -<button onclick={() => count++}> - clicks: {count} -</button> -``` - -You can also use `$state` in class fields (whether public or private): - -```js -// @errors: 7006 2554 -class Todo { - done = $state(false); - text = $state(); - - constructor(text) { - this.text = text; - } -} -``` - -> [!LEGACY] -> In Svelte 4, state was implicitly reactive if the variable was declared at the top level -> -> ```svelte -> <script> -> let count = 0; -> </script> -> -> <button on:click={() => count++}> -> clicks: {count} -> </button> -> ``` - -## `$derived` - -Derived state is declared with the `$derived` rune: - -```svelte -<script> - let count = $state(0); - let doubled = $derived(count * 2); -</script> - -<button onclick={() => count++}> - {doubled} -</button> - -<p>{count} doubled is {doubled}</p> -``` - -The expression inside `$derived(...)` should be free of side-effects. Svelte will disallow state changes (e.g. `count++`) inside derived expressions. - -As with `$state`, you can mark class fields as `$derived`. - -> [!LEGACY] -> In Svelte 4, you could use reactive statements for this. -> -> ```svelte -> <script> -> let count = 0; -> $: doubled = count * 2; -> </script> -> -> <button on:click={() => count++}> -> {doubled} -> </button> -> -> <p>{count} doubled is {doubled}</p> -> ``` -> -> This only worked at the top level of a component. - -## `$effect` - -To run _side-effects_ when the component is mounted to the DOM, and when values change, we can use the `$effect` rune ([demo](/playground/untitled#H4sIAAAAAAAAE31T24rbMBD9lUG7kAQ2sbdlX7xOYNk_aB_rQhRpbAsU2UiTW0P-vbrYubSlYGzmzMzROTPymdVKo2PFjzMzfIusYB99z14YnfoQuD1qQh-7bmdFQEonrOppVZmKNBI49QthCc-OOOH0LZ-9jxnR6c7eUpOnuv6KeT5JFdcqbvbcBcgDz1jXKGg6ncFyBedYR6IzLrAZwiN5vtSxaJA-EzadfJEjKw11C6GR22-BLH8B_wxdByWpvUYtqqal2XB6RVkG1CoHB6U1WJzbnYFDiwb3aGEdDa3Bm1oH12sQLTcNPp7r56m_00mHocSG97_zd7ICUXonA5fwKbPbkE2ZtMJGGVkEdctzQi4QzSwr9prnFYNk5hpmqVuqPQjNnfOJoMF22lUsrq_UfIN6lfSVyvQ7grB3X2mjMZYO3XO9w-U5iLx42qg29md3BP_ni5P4gy9ikTBlHxjLzAtPDlyYZmRdjAbGq7HprEQ7p64v4LU_guu0kvAkhBim3nMplWl8FreQD-CW20aZR0wq12t-KqDWeBywhvexKC3memmDwlHAv9q4Vo2ZK8KtK0CgX7u9J8wXbzdKv-nRnfF_2baTqlYoWUF2h5efl9-n0O6koAMAAA==)): - -```svelte -<script> - let size = $state(50); - let color = $state('#ff3e00'); - - let canvas; - - $effect(() => { - const context = canvas.getContext('2d'); - context.clearRect(0, 0, canvas.width, canvas.height); - - // this will re-run whenever `color` or `size` change - context.fillStyle = color; - context.fillRect(0, 0, size, size); - }); -</script> - -<canvas bind:this={canvas} width="100" height="100" /> -``` - -The function passed to `$effect` will run when the component mounts, and will re-run after any changes to the values it reads that were declared with `$state` or `$derived` (including those passed in with `$props`). Re-runs are batched (i.e. changing `color` and `size` in the same moment won't cause two separate runs), and happen after any DOM updates have been applied. - -> [!LEGACY] -> In Svelte 4, you could use reactive statements for this. -> -> ```svelte -> <script> -> let size = 50; -> let color = '#ff3e00'; -> -> let canvas; -> -> $: { -> const context = canvas.getContext('2d'); -> context.clearRect(0, 0, canvas.width, canvas.height); -> -> // this will re-run whenever `color` or `size` change -> context.fillStyle = color; -> context.fillRect(0, 0, size, size); -> } -> </script> -> -> <canvas bind:this={canvas} width="100" height="100" /> -> ``` -> -> This only worked at the top level of a component. diff --git a/documentation/docs/03-template-syntax/xx-control-flow.md b/documentation/docs/03-template-syntax/xx-control-flow.md deleted file mode 100644 index b73917997b..0000000000 --- a/documentation/docs/03-template-syntax/xx-control-flow.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: Control flow ---- - -- if -- each -- await (or move that into some kind of data loading section?) -- NOT: key (move into transition section, because that's the common use case) - -Svelte augments HTML with control flow blocks to be able to express conditionally rendered content or lists. - -The syntax between these blocks is the same: - -- `{#` denotes the start of a block -- `{:` denotes a different branch part of the block. Depending on the block, there can be multiple of these -- `{/` denotes the end of a block - -## {#if ...} - -## {#each ...} - -```svelte -<!--- copy: false ---> -{#each expression as name}...{/each} -``` - -```svelte -<!--- copy: false ---> -{#each expression as name, index}...{/each} -``` - -```svelte -<!--- copy: false ---> -{#each expression as name (key)}...{/each} -``` - -```svelte -<!--- copy: false ---> -{#each expression as name, index (key)}...{/each} -``` - -```svelte -<!--- copy: false ---> -{#each expression as name}...{:else}...{/each} -``` - -Iterating over lists of values can be done with an each block. - -```svelte -<h1>Shopping list</h1> -<ul> - {#each items as item} - <li>{item.name} x {item.qty}</li> - {/each} -</ul> -``` - -You can use each blocks to iterate over any array or array-like value — that is, any object with a `length` property. - -An each block can also specify an _index_, equivalent to the second argument in an `array.map(...)` callback: - -```svelte -{#each items as item, i} - <li>{i + 1}: {item.name} x {item.qty}</li> -{/each} -``` - -If a _key_ expression is provided — which must uniquely identify each list item — Svelte will use it to diff the list when data changes, rather than adding or removing items at the end. The key can be any object, but strings and numbers are recommended since they allow identity to persist when the objects themselves change. - -```svelte -{#each items as item (item.id)} - <li>{item.name} x {item.qty}</li> -{/each} - -<!-- or with additional index value --> -{#each items as item, i (item.id)} - <li>{i + 1}: {item.name} x {item.qty}</li> -{/each} -``` - -You can freely use destructuring and rest patterns in each blocks. - -```svelte -{#each items as { id, name, qty }, i (id)} - <li>{i + 1}: {name} x {qty}</li> -{/each} - -{#each objects as { id, ...rest }} - <li><span>{id}</span><MyComponent {...rest} /></li> -{/each} - -{#each items as [id, ...rest]} - <li><span>{id}</span><MyComponent values={rest} /></li> -{/each} -``` - -An each block can also have an `{:else}` clause, which is rendered if the list is empty. - -```svelte -{#each todos as todo} - <p>{todo.text}</p> -{:else} - <p>No tasks today!</p> -{/each} -``` - -It is possible to iterate over iterables like `Map` or `Set`. Iterables need to be finite and static (they shouldn't change while being iterated over). Under the hood, they are transformed to an array using `Array.from` before being passed off to rendering. If you're writing performance-sensitive code, try to avoid iterables and use regular arrays as they are more performant. - -## Other block types - -Svelte also provides [`#snippet`](snippets), [`#key`](transitions-and-animations) and [`#await`](data-fetching) blocks. You can find out more about them in their respective sections. diff --git a/documentation/docs/03-template-syntax/xx-data-fetching.md b/documentation/docs/03-template-syntax/xx-data-fetching.md deleted file mode 100644 index 4526d51335..0000000000 --- a/documentation/docs/03-template-syntax/xx-data-fetching.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: Data fetching ---- - -Fetching data is a fundamental part of apps interacting with the outside world. Svelte is unopinionated with how you fetch your data. The simplest way would be using the built-in `fetch` method: - -```svelte -<script> - let response = $state(); - fetch('/api/data').then(async (r) => (response = r.json())); -</script> -``` - -While this works, it makes working with promises somewhat unergonomic. Svelte alleviates this problem using the `#await` block. - -## {#await ...} - -## SvelteKit loaders - -Fetching inside your components is great for simple use cases, but it's prone to data loading waterfalls and makes code harder to work with because of the promise handling. SvelteKit solves this problem by providing a opinionated data loading story that is coupled to its router. Learn more about it [in the docs](../kit). diff --git a/documentation/docs/07-misc/xx-reactivity-indepth.md b/documentation/docs/07-misc/xx-reactivity-indepth.md deleted file mode 100644 index b40072552f..0000000000 --- a/documentation/docs/07-misc/xx-reactivity-indepth.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: Reactivity in depth ---- - -- how to think about Runes ("just JavaScript" with added reactivity, what this means for keeping reactivity alive across boundaries) -- signals From fd57eb362ddf6e9e43936a7d098087f5e4720181 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Apr 2025 17:16:49 -0400 Subject: [PATCH 0336/1429] Version Packages (#15737) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/stale-gorillas-judge.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/stale-gorillas-judge.md diff --git a/.changeset/stale-gorillas-judge.md b/.changeset/stale-gorillas-judge.md deleted file mode 100644 index 3d91f401dd..0000000000 --- a/.changeset/stale-gorillas-judge.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: update `state_referenced_locally` message diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 68a8803663..8b46efc94c 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.26.1 + +### Patch Changes + +- fix: update `state_referenced_locally` message ([#15733](https://github.com/sveltejs/svelte/pull/15733)) + ## 5.26.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 8267cb5218..a06c73429a 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.26.0", + "version": "5.26.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index ade51aaf17..e5cb34ecd5 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.26.0'; +export const VERSION = '5.26.1'; export const PUBLIC_VERSION = '5'; From d38504d073e9a6704660a4b98718b577975ce5d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Apr 2025 17:56:33 -0400 Subject: [PATCH 0337/1429] chore(deps-dev): bump vite from 5.4.17 to 5.4.18 (#15743) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.17 to 5.4.18. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v5.4.18/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.4.18/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 5.4.18 dependency-type: direct:development ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- playgrounds/sandbox/package.json | 2 +- pnpm-lock.yaml | 86 ++++++++++++++++---------------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index d392349b60..5aee92ab17 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -18,7 +18,7 @@ "polka": "^1.0.0-next.25", "svelte": "workspace:*", "tinyglobby": "^0.2.12", - "vite": "^5.4.17", + "vite": "^5.4.18", "vite-plugin-inspect": "^0.8.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f0e66d1681..3518b0e57e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,7 +152,7 @@ importers: devDependencies: '@sveltejs/vite-plugin-svelte': specifier: ^4.0.0-next.6 - version: 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) polka: specifier: ^1.0.0-next.25 version: 1.0.0-next.25 @@ -163,11 +163,11 @@ importers: specifier: ^0.2.12 version: 0.2.12 vite: - specifier: ^5.4.17 - version: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + specifier: ^5.4.18 + version: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vite-plugin-inspect: specifier: ^0.8.4 - version: 0.8.4(rollup@4.39.0)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 0.8.4(rollup@4.39.0)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) packages: @@ -816,8 +816,8 @@ packages: resolution: {integrity: sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.29.0': - resolution: {integrity: sha512-aO1PVsq7Gm+tcghabUpzEnVSFMCU4/nYIgC2GOatJcllvWfnhrgW0ZEbnTxm36QsikmCN1K/6ZgM7fok2I7xNw==} + '@typescript-eslint/scope-manager@8.29.1': + resolution: {integrity: sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/type-utils@8.26.0': @@ -831,8 +831,8 @@ packages: resolution: {integrity: sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.29.0': - resolution: {integrity: sha512-wcJL/+cOXV+RE3gjCyl/V2G877+2faqvlgtso/ZRbTCnZazh0gXhe+7gbAnfubzN2bNsBtZjDvlh7ero8uIbzg==} + '@typescript-eslint/types@8.29.1': + resolution: {integrity: sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.26.0': @@ -841,8 +841,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/typescript-estree@8.29.0': - resolution: {integrity: sha512-yOfen3jE9ISZR/hHpU/bmNvTtBW1NjRbkSFdZOksL1N+ybPEE7UVGMwqvS6CP022Rp00Sb0tdiIkhSCe6NI8ow==} + '@typescript-eslint/typescript-estree@8.29.1': + resolution: {integrity: sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' @@ -854,8 +854,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@8.29.0': - resolution: {integrity: sha512-gX/A0Mz9Bskm8avSWFcK0gP7cZpbY4AIo6B0hWYFCaIsz750oaiWR4Jr2CI+PQhfW1CpcQr9OlfPS+kMFegjXA==} + '@typescript-eslint/utils@8.29.1': + resolution: {integrity: sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -865,8 +865,8 @@ packages: resolution: {integrity: sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.29.0': - resolution: {integrity: sha512-Sne/pVz8ryR03NFK21VpN88dZ2FdQXOlq3VIklbrTYEt8yXtRFr9tvUhqvCeKjqYk5FSim37sHbooT6vzBTZcg==} + '@typescript-eslint/visitor-keys@8.29.1': + resolution: {integrity: sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitest/coverage-v8@2.0.5': @@ -2310,8 +2310,8 @@ packages: terser: optional: true - vite@5.4.17: - resolution: {integrity: sha512-5+VqZryDj4wgCs55o9Lp+p8GE78TLVg0lasCH5xFZ4jacZjtqZa6JUw9/p0WeAojaOfncSM6v77InkFPGnvPvg==} + vite@5.4.18: + resolution: {integrity: sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -3000,25 +3000,25 @@ snapshots: typescript: 5.5.4 typescript-eslint: 8.26.0(eslint@9.9.1)(typescript@5.5.4) - '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.4.0 svelte: link:packages/svelte - vite: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 svelte: link:packages/svelte - vite: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) - vitefu: 0.2.5(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vitefu: 0.2.5(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) transitivePeerDependencies: - supports-color @@ -3088,10 +3088,10 @@ snapshots: '@typescript-eslint/types': 8.26.0 '@typescript-eslint/visitor-keys': 8.26.0 - '@typescript-eslint/scope-manager@8.29.0': + '@typescript-eslint/scope-manager@8.29.1': dependencies: - '@typescript-eslint/types': 8.29.0 - '@typescript-eslint/visitor-keys': 8.29.0 + '@typescript-eslint/types': 8.29.1 + '@typescript-eslint/visitor-keys': 8.29.1 '@typescript-eslint/type-utils@8.26.0(eslint@9.9.1)(typescript@5.5.4)': dependencies: @@ -3106,7 +3106,7 @@ snapshots: '@typescript-eslint/types@8.26.0': {} - '@typescript-eslint/types@8.29.0': {} + '@typescript-eslint/types@8.29.1': {} '@typescript-eslint/typescript-estree@8.26.0(typescript@5.5.4)': dependencies: @@ -3122,10 +3122,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.29.0(typescript@5.5.4)': + '@typescript-eslint/typescript-estree@8.29.1(typescript@5.5.4)': dependencies: - '@typescript-eslint/types': 8.29.0 - '@typescript-eslint/visitor-keys': 8.29.0 + '@typescript-eslint/types': 8.29.1 + '@typescript-eslint/visitor-keys': 8.29.1 debug: 4.4.0 fast-glob: 3.3.3 is-glob: 4.0.3 @@ -3147,12 +3147,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.29.0(eslint@9.9.1)(typescript@5.5.4)': + '@typescript-eslint/utils@8.29.1(eslint@9.9.1)(typescript@5.5.4)': dependencies: '@eslint-community/eslint-utils': 4.5.1(eslint@9.9.1) - '@typescript-eslint/scope-manager': 8.29.0 - '@typescript-eslint/types': 8.29.0 - '@typescript-eslint/typescript-estree': 8.29.0(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.29.1 + '@typescript-eslint/types': 8.29.1 + '@typescript-eslint/typescript-estree': 8.29.1(typescript@5.5.4) eslint: 9.9.1 typescript: 5.5.4 transitivePeerDependencies: @@ -3163,9 +3163,9 @@ snapshots: '@typescript-eslint/types': 8.26.0 eslint-visitor-keys: 4.2.0 - '@typescript-eslint/visitor-keys@8.29.0': + '@typescript-eslint/visitor-keys@8.29.1': dependencies: - '@typescript-eslint/types': 8.29.0 + '@typescript-eslint/types': 8.29.1 eslint-visitor-keys: 4.2.0 '@vitest/coverage-v8@2.0.5(vitest@2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': @@ -3509,7 +3509,7 @@ snapshots: eslint-plugin-n@17.16.1(eslint@9.9.1)(typescript@5.5.4): dependencies: '@eslint-community/eslint-utils': 4.5.1(eslint@9.9.1) - '@typescript-eslint/utils': 8.29.0(eslint@9.9.1)(typescript@5.5.4) + '@typescript-eslint/utils': 8.29.1(eslint@9.9.1)(typescript@5.5.4) enhanced-resolve: 5.18.1 eslint: 9.9.1 eslint-plugin-es-x: 7.8.0(eslint@9.9.1) @@ -4562,7 +4562,7 @@ snapshots: debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 1.1.2 - vite: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - '@types/node' - less @@ -4574,7 +4574,7 @@ snapshots: - supports-color - terser - vite-plugin-inspect@0.8.4(rollup@4.39.0)(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vite-plugin-inspect@0.8.4(rollup@4.39.0)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): dependencies: '@antfu/utils': 0.7.8 '@rollup/pluginutils': 5.1.0(rollup@4.39.0) @@ -4585,7 +4585,7 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 2.0.4 - vite: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - rollup - supports-color @@ -4602,7 +4602,7 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): + vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: esbuild: 0.21.5 postcss: 8.5.3 @@ -4614,9 +4614,9 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vitefu@0.2.5(vite@5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vitefu@0.2.5(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): optionalDependencies: - vite: 5.4.17(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vitest@2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: From a5240ccfe48ecc2f9eebf3de45ffcd60825bc1b0 Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 11 Apr 2025 18:49:38 -0400 Subject: [PATCH 0338/1429] use pnpm/action-setup (#15744) --- .github/workflows/pkg.pr.new.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml index b2b521dc6f..b1ba217e5a 100644 --- a/.github/workflows/pkg.pr.new.yml +++ b/.github/workflows/pkg.pr.new.yml @@ -8,10 +8,8 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v4 - - - run: corepack enable + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: node-version: 22.x From 3d6da41b1d3aaf80b5e9cb9aa8aaa1f91ffc485b Mon Sep 17 00:00:00 2001 From: Rich Harris <rich.harris@vercel.com> Date: Fri, 11 Apr 2025 22:19:01 -0400 Subject: [PATCH 0339/1429] chore: remove some unused code (#15747) --- .../svelte/src/internal/client/reactivity/sources.js | 3 +-- packages/svelte/src/internal/client/runtime.js | 9 +-------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 27e8fd824d..69a41338c0 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -1,4 +1,4 @@ -/** @import { Derived, Effect, Reaction, Source, Value } from '#client' */ +/** @import { Derived, Effect, Source, Value } from '#client' */ import { DEV } from 'esm-env'; import { active_reaction, @@ -12,7 +12,6 @@ import { increment_write_version, update_effect, reaction_sources, - set_reaction_sources, check_dirtiness, untracking, is_destroying_effect, diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index a7662be617..2acad3d258 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -94,18 +94,11 @@ export function set_active_effect(effect) { */ export let reaction_sources = null; -/** - * @param {Source[] | null} sources - */ -export function set_reaction_sources(sources) { - reaction_sources = sources; -} - /** @param {Value} value */ export function push_reaction_value(value) { if (active_reaction !== null && active_reaction.f & EFFECT_IS_UPDATING) { if (reaction_sources === null) { - set_reaction_sources([value]); + reaction_sources = [value]; } else { reaction_sources.push(value); } From 69a427518dc4eeae39ec8be8c55486d0093784df Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Sat, 12 Apr 2025 16:39:22 +0200 Subject: [PATCH 0340/1429] fix: correctly validate `undefined` snippet params with default value (#15750) * fix: correctly validate `undefined` snippet params with default value * use arguments * unused * drive-by --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/angry-mayflies-matter.md | 5 +++ .../client/visitors/SnippetBlock.js | 34 ++++++------------- .../_config.js | 5 +++ .../main.svelte | 5 +++ 4 files changed, 25 insertions(+), 24 deletions(-) create mode 100644 .changeset/angry-mayflies-matter.md create mode 100644 packages/svelte/tests/runtime-runes/samples/validate-undefined-snippet-default-arg/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/validate-undefined-snippet-default-arg/main.svelte diff --git a/.changeset/angry-mayflies-matter.md b/.changeset/angry-mayflies-matter.md new file mode 100644 index 0000000000..289fbf87b6 --- /dev/null +++ b/.changeset/angry-mayflies-matter.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly validate `undefined` snippet params with default value diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js index 7eb043aa5d..f28f8c8a59 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js @@ -21,6 +21,10 @@ export function SnippetBlock(node, context) { /** @type {Statement[]} */ const declarations = []; + if (dev) { + declarations.push(b.stmt(b.call('$.validate_snippet_args', b.spread(b.id('arguments'))))); + } + const transform = { ...context.state.transform }; const child_state = { ...context.state, transform }; @@ -30,12 +34,7 @@ export function SnippetBlock(node, context) { if (!argument) continue; if (argument.type === 'Identifier') { - args.push({ - type: 'AssignmentPattern', - left: argument, - right: b.id('$.noop') - }); - + args.push(b.assignment_pattern(argument, b.id('$.noop'))); transform[argument.name] = { read: b.call }; continue; @@ -66,29 +65,16 @@ export function SnippetBlock(node, context) { } } } - if (dev) { - declarations.unshift( - b.stmt( - b.call( - '$.validate_snippet_args', - .../** @type {Identifier[]} */ ( - args.map((arg) => (arg?.type === 'Identifier' ? arg : arg?.left)) - ) - ) - ) - ); - } + body = b.block([ ...declarations, .../** @type {BlockStatement} */ (context.visit(node.body, child_state)).body ]); - /** @type {Expression} */ - let snippet = b.arrow(args, body); - - if (dev) { - snippet = b.call('$.wrap_snippet', b.id(context.state.analysis.name), snippet); - } + // in dev we use a FunctionExpression (not arrow function) so we can use `arguments` + let snippet = dev + ? b.call('$.wrap_snippet', b.id(context.state.analysis.name), b.function(null, args, body)) + : b.arrow(args, body); const declaration = b.const(node.expression, snippet); diff --git a/packages/svelte/tests/runtime-runes/samples/validate-undefined-snippet-default-arg/_config.js b/packages/svelte/tests/runtime-runes/samples/validate-undefined-snippet-default-arg/_config.js new file mode 100644 index 0000000000..bddb75e677 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/validate-undefined-snippet-default-arg/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: `<p>default</p>` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/validate-undefined-snippet-default-arg/main.svelte b/packages/svelte/tests/runtime-runes/samples/validate-undefined-snippet-default-arg/main.svelte new file mode 100644 index 0000000000..3f00eba46b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/validate-undefined-snippet-default-arg/main.svelte @@ -0,0 +1,5 @@ +{#snippet test(param = "default")} + <p>{param}</p> +{/snippet} + +{@render test()} \ No newline at end of file From 3153384928c444b484fd504268ba87aa580396a1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 12 Apr 2025 10:42:58 -0400 Subject: [PATCH 0341/1429] Version Packages (#15751) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/angry-mayflies-matter.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/angry-mayflies-matter.md diff --git a/.changeset/angry-mayflies-matter.md b/.changeset/angry-mayflies-matter.md deleted file mode 100644 index 289fbf87b6..0000000000 --- a/.changeset/angry-mayflies-matter.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly validate `undefined` snippet params with default value diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 8b46efc94c..3c876880b8 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.26.2 + +### Patch Changes + +- fix: correctly validate `undefined` snippet params with default value ([#15750](https://github.com/sveltejs/svelte/pull/15750)) + ## 5.26.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index a06c73429a..75568f4a77 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.26.1", + "version": "5.26.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index e5cb34ecd5..2dc75f7e59 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.26.1'; +export const VERSION = '5.26.2'; export const PUBLIC_VERSION = '5'; From fd0bc2997340bb8164e2b77996e81645315b8c5d Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti <ricciutipaolo@gmail.com> Date: Sun, 13 Apr 2025 12:20:48 +0200 Subject: [PATCH 0342/1429] fix: correctly validate head snippets on the server (#15755) * fix: correctly validate head snippets on the server * put the logic in copy_payload so it gets treeshaken in most cases --------- Co-authored-by: Rich Harris <rich.harris@vercel.com> --- .changeset/dirty-zebras-do.md | 5 +++ packages/svelte/src/internal/server/dev.js | 8 +++-- .../svelte/src/internal/server/payload.js | 34 ++++++++++++------- .../head-payload-validation/_config.js | 11 ++++++ .../head-payload-validation/main.svelte | 7 ++++ 5 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 .changeset/dirty-zebras-do.md create mode 100644 packages/svelte/tests/runtime-runes/samples/head-payload-validation/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/head-payload-validation/main.svelte diff --git a/.changeset/dirty-zebras-do.md b/.changeset/dirty-zebras-do.md new file mode 100644 index 0000000000..f0b266b09c --- /dev/null +++ b/.changeset/dirty-zebras-do.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: correctly validate head snippets on the server diff --git a/packages/svelte/src/internal/server/dev.js b/packages/svelte/src/internal/server/dev.js index 34849196b7..157f22f929 100644 --- a/packages/svelte/src/internal/server/dev.js +++ b/packages/svelte/src/internal/server/dev.js @@ -6,7 +6,7 @@ import { } from '../../html-tree-validation.js'; import { current_component } from './context.js'; import { invalid_snippet_arguments } from '../shared/errors.js'; -import { Payload } from './payload.js'; +import { HeadPayload, Payload } from './payload.js'; /** * @typedef {{ @@ -105,7 +105,11 @@ export function pop_element() { * @param {Payload} payload */ export function validate_snippet_args(payload) { - if (typeof payload !== 'object' || !(payload instanceof Payload)) { + if ( + typeof payload !== 'object' || + // for some reason typescript consider the type of payload as never after the first instanceof + !(payload instanceof Payload || /** @type {any} */ (payload) instanceof HeadPayload) + ) { invalid_snippet_arguments(); } } diff --git a/packages/svelte/src/internal/server/payload.js b/packages/svelte/src/internal/server/payload.js index 03bcaf492e..8df5787ba4 100644 --- a/packages/svelte/src/internal/server/payload.js +++ b/packages/svelte/src/internal/server/payload.js @@ -1,16 +1,25 @@ +export class HeadPayload { + /** @type {Set<{ hash: string; code: string }>} */ + css = new Set(); + out = ''; + uid = () => ''; + title = ''; + + constructor(css = new Set(), out = '', title = '', uid = () => '') { + this.css = css; + this.out = out; + this.title = title; + this.uid = uid; + } +} + export class Payload { /** @type {Set<{ hash: string; code: string }>} */ css = new Set(); out = ''; uid = () => ''; - head = { - /** @type {Set<{ hash: string; code: string }>} */ - css: new Set(), - title: '', - out: '', - uid: () => '' - }; + head = new HeadPayload(); constructor(id_prefix = '') { this.uid = props_id_generator(id_prefix); @@ -30,12 +39,11 @@ export function copy_payload({ out, css, head, uid }) { payload.css = new Set(css); payload.uid = uid; - payload.head = { - title: head.title, - out: head.out, - css: new Set(head.css), - uid: head.uid - }; + payload.head = new HeadPayload(); + payload.head.out = head.out; + payload.head.css = new Set(head.css); + payload.head.title = head.title; + payload.head.uid = head.uid; return payload; } diff --git a/packages/svelte/tests/runtime-runes/samples/head-payload-validation/_config.js b/packages/svelte/tests/runtime-runes/samples/head-payload-validation/_config.js new file mode 100644 index 0000000000..7c609205df --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/head-payload-validation/_config.js @@ -0,0 +1,11 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + mode: ['server'], + async test({ errors, assert }) { + assert.equal(errors, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/head-payload-validation/main.svelte b/packages/svelte/tests/runtime-runes/samples/head-payload-validation/main.svelte new file mode 100644 index 0000000000..7eb31d3a9e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/head-payload-validation/main.svelte @@ -0,0 +1,7 @@ +{#snippet head()} + <title>Cool +{/snippet} + + + {@render head()} + \ No newline at end of file From de4376235cb355d818ba7aaa17c00a4fe76fe9cd Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 14 Apr 2025 07:02:58 -0400 Subject: [PATCH 0343/1429] docs: add some missing details around string coercion and handling of nullish values (#15739) closes #14716 --- documentation/docs/02-runes/05-$props.md | 4 ++-- documentation/docs/03-template-syntax/01-basic-markup.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/documentation/docs/02-runes/05-$props.md b/documentation/docs/02-runes/05-$props.md index f300fb239d..222b4831b6 100644 --- a/documentation/docs/02-runes/05-$props.md +++ b/documentation/docs/02-runes/05-$props.md @@ -37,7 +37,7 @@ On the other side, inside `MyComponent.svelte`, we can receive props with the `$ ## Fallback values -Destructuring allows us to declare fallback values, which are used if the parent component does not set a given prop: +Destructuring allows us to declare fallback values, which are used if the parent component does not set a given prop (or the value is `undefined`): ```js let { adjective = 'happy' } = $props(); @@ -219,4 +219,4 @@ This is useful for linking elements via attributes like `for` and `aria-labelled -``` \ No newline at end of file +``` diff --git a/documentation/docs/03-template-syntax/01-basic-markup.md b/documentation/docs/03-template-syntax/01-basic-markup.md index 5e8b4342d3..fe5f8b02aa 100644 --- a/documentation/docs/03-template-syntax/01-basic-markup.md +++ b/documentation/docs/03-template-syntax/01-basic-markup.md @@ -154,6 +154,8 @@ A JavaScript expression can be included as text by surrounding it with curly bra {expression} ``` +Expressions that are `null` or `undefined` will be omitted; all others are [coerced to strings](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#string_coercion). + Curly braces can be included in a Svelte template by using their [HTML entity](https://developer.mozilla.org/docs/Glossary/Entity) strings: `{`, `{`, or `{` for `{` and `}`, `}`, or `}` for `}`. If you're using a regular expression (`RegExp`) [literal notation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#literal_notation_and_constructor), you'll need to wrap it in parentheses. From bdf033e30f40b7b40792eafe694a02ce84387089 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:56:43 +0200 Subject: [PATCH 0344/1429] fix: allow self-closing tags within math namespace (#15761) fixes #15757 --- .changeset/wicked-cheetahs-juggle.md | 5 +++++ .../compiler/phases/2-analyze/visitors/RegularElement.js | 3 ++- .../samples/invalid-self-closing-tag/input.svelte | 1 + .../samples/invalid-self-closing-tag/warnings.json | 8 ++++---- 4 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 .changeset/wicked-cheetahs-juggle.md diff --git a/.changeset/wicked-cheetahs-juggle.md b/.changeset/wicked-cheetahs-juggle.md new file mode 100644 index 0000000000..58dca62bec --- /dev/null +++ b/.changeset/wicked-cheetahs-juggle.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow self-closing tags within math namespace diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js index 03dfaebcb7..d5689e5d55 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/RegularElement.js @@ -173,7 +173,8 @@ export function RegularElement(node, context) { if ( context.state.analysis.source[node.end - 2] === '/' && !is_void(node_name) && - !is_svg(node_name) + !is_svg(node_name) && + !is_mathml(node_name) ) { w.element_invalid_self_closing_tag(node, node.name); } diff --git a/packages/svelte/tests/validator/samples/invalid-self-closing-tag/input.svelte b/packages/svelte/tests/validator/samples/invalid-self-closing-tag/input.svelte index 376c9f79bd..07582e8343 100644 --- a/packages/svelte/tests/validator/samples/invalid-self-closing-tag/input.svelte +++ b/packages/svelte/tests/validator/samples/invalid-self-closing-tag/input.svelte @@ -1,6 +1,7 @@ + diff --git a/packages/svelte/tests/validator/samples/invalid-self-closing-tag/warnings.json b/packages/svelte/tests/validator/samples/invalid-self-closing-tag/warnings.json index 40b87ec7c8..3e45bca5ad 100644 --- a/packages/svelte/tests/validator/samples/invalid-self-closing-tag/warnings.json +++ b/packages/svelte/tests/validator/samples/invalid-self-closing-tag/warnings.json @@ -3,11 +3,11 @@ "code": "element_invalid_self_closing_tag", "message": "Self-closing HTML tags for non-void elements are ambiguous — use `
` rather than `
`", "start": { - "line": 8, + "line": 9, "column": 0 }, "end": { - "line": 8, + "line": 9, "column": 7 } }, @@ -15,11 +15,11 @@ "code": "element_invalid_self_closing_tag", "message": "Self-closing HTML tags for non-void elements are ambiguous — use `` rather than ``", "start": { - "line": 9, + "line": 10, "column": 0 }, "end": { - "line": 9, + "line": 10, "column": 12 } } From 26e574f27f6866383044a85da9d6845944bd0fe2 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:57:37 +0200 Subject: [PATCH 0345/1429] fix: ignore mutation validation for props that are not proxies in more cases (#15759) This fixes an off-by-one error - we did not bail if the top level of the prop wasn't a state already. Fixes #15727 --- .changeset/modern-ducks-reflect.md | 5 +++++ .../src/internal/client/dev/ownership.js | 7 ++++--- .../samples/non-local-mutation-ok/_config.js | 18 ++++++++++++++++ .../non-local-mutation-ok/child.svelte | 8 +++++++ .../samples/non-local-mutation-ok/main.svelte | 21 +++++++++++++++++++ 5 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 .changeset/modern-ducks-reflect.md create mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/main.svelte diff --git a/.changeset/modern-ducks-reflect.md b/.changeset/modern-ducks-reflect.md new file mode 100644 index 0000000000..dfbb9a18cc --- /dev/null +++ b/.changeset/modern-ducks-reflect.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ignore mutation validation for props that are not proxies in more cases diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index 108c7adf92..5a8af6d522 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -31,13 +31,14 @@ export function create_ownership_validator(props) { return result; } - let value = props[name]; + /** @type {any} */ + let value = props; - for (let i = 1; i < path.length - 1; i++) { + for (let i = 0; i < path.length - 1; i++) { + value = value[path[i]]; if (!value?.[STATE_SYMBOL]) { return result; } - value = value[path[i]]; } const location = sanitize_location(`${component[FILENAME]}:${line}:${column}`); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/_config.js b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/_config.js new file mode 100644 index 0000000000..437385e185 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/_config.js @@ -0,0 +1,18 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + + test({ assert, target, warnings }) { + const btn = target.querySelector('button'); + btn?.click(); + flushSync(); + + assert.deepEqual(warnings, []); + }, + + warnings: [] +}); diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/child.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/child.svelte new file mode 100644 index 0000000000..0243a6c7d1 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/child.svelte @@ -0,0 +1,8 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/main.svelte b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/main.svelte new file mode 100644 index 0000000000..8685664ab1 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/non-local-mutation-ok/main.svelte @@ -0,0 +1,21 @@ + + + From db111f61ea51edd655b13a0949dc696bb8688d81 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 14 Apr 2025 09:08:19 -0400 Subject: [PATCH 0346/1429] docs: headers for snippet prop section (#15745) Closes #14020 Closes #15172 --- documentation/docs/03-template-syntax/06-snippet.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/documentation/docs/03-template-syntax/06-snippet.md b/documentation/docs/03-template-syntax/06-snippet.md index c9951d3f34..ab536c6e5c 100644 --- a/documentation/docs/03-template-syntax/06-snippet.md +++ b/documentation/docs/03-template-syntax/06-snippet.md @@ -112,6 +112,8 @@ Snippets can reference themselves and each other ([demo](/playground/untitled#H4 ## Passing snippets to components +### Explicit props + Within the template, snippets are values just like any other. As such, they can be passed to components as props ([demo](/playground/untitled#H4sIAAAAAAAAE3VS247aMBD9lZGpBGwDASRegonaPvQL2qdlH5zYEKvBNvbQLbL875VzAcKyj3PmzJnLGU8UOwqSkd8KJdaCk4TsZS0cyV49wYuJuQiQpGd-N2bu_ooaI1YwJ57hpVYoFDqSEepKKw3mO7VDeTTaIvxiRS1gb_URxvO0ibrS8WanIrHUyiHs7Vmigy28RmyHHmKvDMbMmFq4cQInvGSwTsBYWYoMVhCSB2rBFFPsyl0uruTlR3JZCWvlTXl1Yy_mawiR_rbZKZrellJ-5JQ0RiBUgnFhJ9OGR7HKmwVoilXeIye8DOJGfYCgRlZ3iE876TBsZPX7hPdteO75PC4QaIo8vwNPePmANQ2fMeEFHrLD7rR1jTNkW986E8C3KwfwVr8HSHOSEBT_kGRozyIkn_zQveXDL3rIfPJHtUDwzShJd_Qk3gQCbOGLsdq4yfTRJopRuin3I7nv6kL7ARRjmLdBDG3uv1mhuLA3V2mKtqNEf_oCn8p9aN-WYqH5peP4kWBl1UwJzAEPT9U7K--0fRrrWnPTXpCm1_EVdXjpNmlA8G1hPPyM1fKgMqjFHjctXGjLhZ05w0qpDhksGrybuNEHtJnCalZWsuaTlfq6nPaaBSv_HKw-K57BjzOiVj9ZKQYKzQjZodYFqydYTRN4gPhVzTDO2xnma3HsVWjaLjT8nbfwHy7Q5f2dBAAA)): ```svelte @@ -144,6 +146,8 @@ Within the template, snippets are values just like any other. As such, they can Think about it like passing content instead of data to a component. The concept is similar to slots in web components. +### Implicit props + As an authoring convenience, snippets declared directly _inside_ a component implicitly become props _on_ the component ([demo](/playground/untitled#H4sIAAAAAAAAE3VSTa_aMBD8Kyu_SkAbCA-JSzBR20N_QXt6vIMTO8SqsY29tI2s_PcqTiB8vaPHs7MzuxuIZgdBMvJLo0QlOElIJZXwJHsLBBvb_XUASc7Mb9Yu_B-hsMMK5sUzvDQahUZPMkJ96aTFfKd3KA_WOISfrFACKmcOMFmk8TWUTjY73RFLoz1C5U4SPWzhrcN2GKDrlcGEWauEnyRwxCaDdQLWyVJksII2uaMWTDPNLtzX5YX8-kgua-GcHJVXI3u5WEPb0d83O03TMZSmfRzOkG1Db7mNacOL19JagVALxoWbztq-H8U6j0SaYp2P2BGbOyQ2v8PQIFMXLKRDk177pq0zf6d8bMrzwBdd0pamyPMb-IjNEzS2f86Gz_Dwf-2F9nvNSUJQ_EOSoTuJNvngqK5v4Pas7n4-OCwlEEJcQTIMO-nSQwtb-GSdsX46e9gbRoP9yGQ11I0rEuycunu6PHx1QnPhxm3SFN15MOlYEFJZtf0dUywMbwZOeBGsrKNLYB54-1R9WNqVdki7usim6VmQphf7mnpshiQRhNAXdoOfMyX3OgMlKtz0cGEcF27uLSul3mewjPjgOOoDukxjPS9rqfh0pb-8zs6aBSt_7505aZ7B9xOi0T9YKW4UooVsr0zB1BTrWQJ3EL-oWcZ572GxFoezCk37QLe3897-B2i2U62uBAAA)): ```svelte @@ -165,6 +169,8 @@ As an authoring convenience, snippets declared directly _inside_ a component imp ``` +### Implicit `children` snippet + Any content inside the component tags that is _not_ a snippet declaration implicitly becomes part of the `children` snippet ([demo](/playground/untitled#H4sIAAAAAAAAE3WOQQrCMBBFrzIMggql3ddY1Du4si5sOmIwnYRkFKX07lKqglqX8_7_w2uRDw1hjlsWI5ZqTPBoLEXMdy3K3fdZDzB5Ndfep_FKVnpWHSKNce1YiCVijirqYLwUJQOYxrsgsLmIOIZjcA1M02w4n-PpomSVvTclqyEutDX6DA2pZ7_ABIVugrmEC3XJH92P55_G39GodCmWBFrQJ2PrQAwdLGHig_NxNv9xrQa1dhWIawrv1Wzeqawa8953D-8QOmaEAQAA)): ```svelte @@ -184,6 +190,8 @@ Any content inside the component tags that is _not_ a snippet declaration implic > [!NOTE] Note that you cannot have a prop called `children` if you also have content inside the component — for this reason, you should avoid having props with that name +### Optional snippet props + You can declare snippet props as being optional. You can either use optional chaining to not render anything if the snippet isn't set... ```svelte From 7aed6beeaabbcdbaa0a6fab952c6e06a0b385edd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:12:10 -0400 Subject: [PATCH 0347/1429] Version Packages (#15756) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/dirty-zebras-do.md | 5 ----- .changeset/modern-ducks-reflect.md | 5 ----- .changeset/wicked-cheetahs-juggle.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 .changeset/dirty-zebras-do.md delete mode 100644 .changeset/modern-ducks-reflect.md delete mode 100644 .changeset/wicked-cheetahs-juggle.md diff --git a/.changeset/dirty-zebras-do.md b/.changeset/dirty-zebras-do.md deleted file mode 100644 index f0b266b09c..0000000000 --- a/.changeset/dirty-zebras-do.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: correctly validate head snippets on the server diff --git a/.changeset/modern-ducks-reflect.md b/.changeset/modern-ducks-reflect.md deleted file mode 100644 index dfbb9a18cc..0000000000 --- a/.changeset/modern-ducks-reflect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ignore mutation validation for props that are not proxies in more cases diff --git a/.changeset/wicked-cheetahs-juggle.md b/.changeset/wicked-cheetahs-juggle.md deleted file mode 100644 index 58dca62bec..0000000000 --- a/.changeset/wicked-cheetahs-juggle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: allow self-closing tags within math namespace diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 3c876880b8..58f2317796 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.26.3 + +### Patch Changes + +- fix: correctly validate head snippets on the server ([#15755](https://github.com/sveltejs/svelte/pull/15755)) + +- fix: ignore mutation validation for props that are not proxies in more cases ([#15759](https://github.com/sveltejs/svelte/pull/15759)) + +- fix: allow self-closing tags within math namespace ([#15761](https://github.com/sveltejs/svelte/pull/15761)) + ## 5.26.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 75568f4a77..b8654671ec 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.26.2", + "version": "5.26.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 2dc75f7e59..1909588146 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.26.2'; +export const VERSION = '5.26.3'; export const PUBLIC_VERSION = '5'; From a051f96ed607751f502dadcd9cbaff1bf880b15f Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 14 Apr 2025 20:38:02 +0200 Subject: [PATCH 0348/1429] fix: relax `:global` selector list validation (#15762) We have to allow `:global x, :global y` selector lists because CSS preprocessors might generate that from `:global { x, y {...} }` --- .changeset/green-starfishes-shave.md | 5 ++ .../98-reference/.generated/compile-errors.md | 26 +++++- .../svelte/messages/compile-errors/style.md | 26 +++++- packages/svelte/src/compiler/errors.js | 4 +- .../phases/2-analyze/css/css-analyze.js | 90 +++++++++---------- .../compiler/phases/3-transform/css/index.js | 37 +++++--- .../css-global-block-multiple-1/_config.js | 10 +++ .../css-global-block-multiple-1/main.svelte | 9 ++ .../css-global-block-multiple-2/_config.js | 10 +++ .../css-global-block-multiple-2/main.svelte | 6 ++ .../css-global-block-multiple/_config.js | 9 -- .../css-global-block-multiple/main.svelte | 3 - .../tests/css/samples/global-block/_config.js | 14 +++ .../css/samples/global-block/expected.css | 10 +++ .../css/samples/global-block/input.svelte | 10 +++ 15 files changed, 198 insertions(+), 71 deletions(-) create mode 100644 .changeset/green-starfishes-shave.md create mode 100644 packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-1/_config.js create mode 100644 packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-1/main.svelte create mode 100644 packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-2/_config.js create mode 100644 packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-2/main.svelte delete mode 100644 packages/svelte/tests/compiler-errors/samples/css-global-block-multiple/_config.js delete mode 100644 packages/svelte/tests/compiler-errors/samples/css-global-block-multiple/main.svelte diff --git a/.changeset/green-starfishes-shave.md b/.changeset/green-starfishes-shave.md new file mode 100644 index 0000000000..967bba753c --- /dev/null +++ b/.changeset/green-starfishes-shave.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: relax `:global` selector list validation diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index a8c39aaf97..6196a85ade 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -235,7 +235,31 @@ A top-level `:global {...}` block can only contain rules, not declarations ### css_global_block_invalid_list ``` -A `:global` selector cannot be part of a selector list with more than one item +A `:global` selector cannot be part of a selector list with entries that don't contain `:global` +``` + +The following CSS is invalid: + +```css +:global, x { + y { + color: red; + } +} +``` + +This is mixing a `:global` block, which means "everything in here is unscoped", with a scoped selector (`x` in this case). As a result it's not possible to transform the inner selector (`y` in this case) into something that satisfies both requirements. You therefore have to split this up into two selectors: + +```css +:global { + y { + color: red; + } +} + +x y { + color: red; +} ``` ### css_global_block_invalid_modifier diff --git a/packages/svelte/messages/compile-errors/style.md b/packages/svelte/messages/compile-errors/style.md index 1e1ab45e8c..f08a2156a3 100644 --- a/packages/svelte/messages/compile-errors/style.md +++ b/packages/svelte/messages/compile-errors/style.md @@ -16,7 +16,31 @@ ## css_global_block_invalid_list -> A `:global` selector cannot be part of a selector list with more than one item +> A `:global` selector cannot be part of a selector list with entries that don't contain `:global` + +The following CSS is invalid: + +```css +:global, x { + y { + color: red; + } +} +``` + +This is mixing a `:global` block, which means "everything in here is unscoped", with a scoped selector (`x` in this case). As a result it's not possible to transform the inner selector (`y` in this case) into something that satisfies both requirements. You therefore have to split this up into two selectors: + +```css +:global { + y { + color: red; + } +} + +x y { + color: red; +} +``` ## css_global_block_invalid_modifier diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index 6bf973948b..aa328764e1 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -555,12 +555,12 @@ export function css_global_block_invalid_declaration(node) { } /** - * A `:global` selector cannot be part of a selector list with more than one item + * A `:global` selector cannot be part of a selector list with entries that don't contain `:global` * @param {null | number | NodeLike} node * @returns {never} */ export function css_global_block_invalid_list(node) { - e(node, 'css_global_block_invalid_list', `A \`:global\` selector cannot be part of a selector list with more than one item\nhttps://svelte.dev/e/css_global_block_invalid_list`); + e(node, 'css_global_block_invalid_list', `A \`:global\` selector cannot be part of a selector list with entries that don't contain \`:global\`\nhttps://svelte.dev/e/css_global_block_invalid_list`); } /** diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js index 76cb2f56e9..2dc3435648 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js @@ -193,10 +193,12 @@ const css_visitors = { Rule(node, context) { node.metadata.parent_rule = context.state.rule; - node.metadata.is_global_block = node.prelude.children.some((selector) => { + // We gotta allow :global x, :global y because CSS preprocessors might generate that from :global { x, y {...} } + for (const complex_selector of node.prelude.children) { let is_global_block = false; - for (const child of selector.children) { + for (let selector_idx = 0; selector_idx < complex_selector.children.length; selector_idx++) { + const child = complex_selector.children[selector_idx]; const idx = child.selectors.findIndex(is_global_block_selector); if (is_global_block) { @@ -204,58 +206,56 @@ const css_visitors = { child.metadata.is_global_like = true; } - if (idx !== -1) { - is_global_block = true; + if (idx === 0) { + if ( + child.selectors.length > 1 && + selector_idx === 0 && + node.metadata.parent_rule === null + ) { + e.css_global_block_invalid_modifier_start(child.selectors[1]); + } else { + // `child` starts with `:global` + node.metadata.is_global_block = is_global_block = true; + + for (let i = 1; i < child.selectors.length; i++) { + walk(/** @type {AST.CSS.Node} */ (child.selectors[i]), null, { + ComplexSelector(node) { + node.metadata.used = true; + } + }); + } - for (let i = idx + 1; i < child.selectors.length; i++) { - walk(/** @type {AST.CSS.Node} */ (child.selectors[i]), null, { - ComplexSelector(node) { - node.metadata.used = true; - } - }); - } - } - } + if (child.combinator && child.combinator.name !== ' ') { + e.css_global_block_invalid_combinator(child, child.combinator.name); + } - return is_global_block; - }); + const declaration = node.block.children.find((child) => child.type === 'Declaration'); + const is_lone_global = + complex_selector.children.length === 1 && + complex_selector.children[0].selectors.length === 1; // just `:global`, not e.g. `:global x` - if (node.metadata.is_global_block) { - if (node.prelude.children.length > 1) { - e.css_global_block_invalid_list(node.prelude); - } + if (is_lone_global && node.prelude.children.length > 1) { + // `:global, :global x { z { ... } }` would become `x { z { ... } }` which means `z` is always + // constrained by `x`, which is not what the user intended + e.css_global_block_invalid_list(node.prelude); + } - const complex_selector = node.prelude.children[0]; - const global_selector = complex_selector.children.find((r, selector_idx) => { - const idx = r.selectors.findIndex(is_global_block_selector); - if (idx === 0) { - if (r.selectors.length > 1 && selector_idx === 0 && node.metadata.parent_rule === null) { - e.css_global_block_invalid_modifier_start(r.selectors[1]); + if ( + declaration && + // :global { color: red; } is invalid, but foo :global { color: red; } is valid + node.prelude.children.length === 1 && + is_lone_global + ) { + e.css_global_block_invalid_declaration(declaration); + } } - return true; } else if (idx !== -1) { - e.css_global_block_invalid_modifier(r.selectors[idx]); + e.css_global_block_invalid_modifier(child.selectors[idx]); } - }); - - if (!global_selector) { - throw new Error('Internal error: global block without :global selector'); - } - - if (global_selector.combinator && global_selector.combinator.name !== ' ') { - e.css_global_block_invalid_combinator(global_selector, global_selector.combinator.name); } - const declaration = node.block.children.find((child) => child.type === 'Declaration'); - - if ( - declaration && - // :global { color: red; } is invalid, but foo :global { color: red; } is valid - node.prelude.children.length === 1 && - node.prelude.children[0].children.length === 1 && - node.prelude.children[0].children[0].selectors.length === 1 - ) { - e.css_global_block_invalid_declaration(declaration); + if (node.metadata.is_global_block && !is_global_block) { + e.css_global_block_invalid_list(node.prelude); } } diff --git a/packages/svelte/src/compiler/phases/3-transform/css/index.js b/packages/svelte/src/compiler/phases/3-transform/css/index.js index dff034f8aa..9f1142cce9 100644 --- a/packages/svelte/src/compiler/phases/3-transform/css/index.js +++ b/packages/svelte/src/compiler/phases/3-transform/css/index.js @@ -170,7 +170,11 @@ const visitors = { if (node.metadata.is_global_block) { const selector = node.prelude.children[0]; - if (selector.children.length === 1 && selector.children[0].selectors.length === 1) { + if ( + node.prelude.children.length === 1 && + selector.children.length === 1 && + selector.children[0].selectors.length === 1 + ) { // `:global {...}` if (state.minify) { state.code.remove(node.start, node.block.start + 1); @@ -194,7 +198,7 @@ const visitors = { SelectorList(node, { state, next, path }) { // Only add comments if we're not inside a complex selector that itself is unused or a global block if ( - !is_in_global_block(path) && + (!is_in_global_block(path) || node.children.length > 1) && !path.find((n) => n.type === 'ComplexSelector' && !n.metadata.used) ) { const children = node.children; @@ -282,13 +286,24 @@ const visitors = { const global = /** @type {AST.CSS.PseudoClassSelector} */ (relative_selector.selectors[0]); remove_global_pseudo_class(global, relative_selector.combinator, context.state); - if ( - node.metadata.rule?.metadata.parent_rule && - global.args === null && - relative_selector.combinator === null - ) { - // div { :global.x { ... } } becomes div { &.x { ... } } - context.state.code.prependRight(global.start, '&'); + const parent_rule = node.metadata.rule?.metadata.parent_rule; + if (parent_rule && global.args === null) { + if (relative_selector.combinator === null) { + // div { :global.x { ... } } becomes div { &.x { ... } } + context.state.code.prependRight(global.start, '&'); + } + + // In case of multiple :global selectors in a selector list we gotta delete the comma, too, but only if + // the next selector is used; if it's unused then the comma deletion happens as part of removal of that next selector + if ( + parent_rule.prelude.children.length > 1 && + node.children.length === node.children.findIndex((s) => s === relative_selector) - 1 + ) { + const next_selector = parent_rule.prelude.children.find((s) => s.start > global.end); + if (next_selector && next_selector.metadata.used) { + context.state.code.update(global.end, next_selector.start, ''); + } + } } continue; } else { @@ -380,7 +395,9 @@ function remove_global_pseudo_class(selector, combinator, state) { // div :global.x becomes div.x while (/\s/.test(state.code.original[start - 1])) start--; } - state.code.remove(start, selector.start + ':global'.length); + + // update(...), not remove(...) because there could be a closing unused comment at the end + state.code.update(start, selector.start + ':global'.length, ''); } else { state.code .remove(selector.start, selector.start + ':global('.length) diff --git a/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-1/_config.js b/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-1/_config.js new file mode 100644 index 0000000000..85dedc8012 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-1/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'css_global_block_invalid_list', + message: + "A `:global` selector cannot be part of a selector list with entries that don't contain `:global`", + position: [232, 246] + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-1/main.svelte b/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-1/main.svelte new file mode 100644 index 0000000000..260921f704 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-1/main.svelte @@ -0,0 +1,9 @@ + diff --git a/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-2/_config.js b/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-2/_config.js new file mode 100644 index 0000000000..f24095800a --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-2/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'css_global_block_invalid_list', + message: + "A `:global` selector cannot be part of a selector list with entries that don't contain `:global`", + position: [24, 43] + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-2/main.svelte b/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-2/main.svelte new file mode 100644 index 0000000000..2a09ec10ce --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple-2/main.svelte @@ -0,0 +1,6 @@ + diff --git a/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple/_config.js b/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple/_config.js deleted file mode 100644 index 9ae4e758c4..0000000000 --- a/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple/_config.js +++ /dev/null @@ -1,9 +0,0 @@ -import { test } from '../../test'; - -export default test({ - error: { - code: 'css_global_block_invalid_list', - message: 'A `:global` selector cannot be part of a selector list with more than one item', - position: [9, 31] - } -}); diff --git a/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple/main.svelte b/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple/main.svelte deleted file mode 100644 index 75178bc664..0000000000 --- a/packages/svelte/tests/compiler-errors/samples/css-global-block-multiple/main.svelte +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/packages/svelte/tests/css/samples/global-block/_config.js b/packages/svelte/tests/css/samples/global-block/_config.js index bee0d7204d..a8b11a73ec 100644 --- a/packages/svelte/tests/css/samples/global-block/_config.js +++ b/packages/svelte/tests/css/samples/global-block/_config.js @@ -16,6 +16,20 @@ export default test({ column: 16, character: 932 } + }, + { + code: 'css_unused_selector', + message: 'Unused CSS selector "unused :global"', + start: { + line: 100, + column: 29, + character: 1223 + }, + end: { + line: 100, + column: 43, + character: 1237 + } } ] }); diff --git a/packages/svelte/tests/css/samples/global-block/expected.css b/packages/svelte/tests/css/samples/global-block/expected.css index 438749224b..12f9a75032 100644 --- a/packages/svelte/tests/css/samples/global-block/expected.css +++ b/packages/svelte/tests/css/samples/global-block/expected.css @@ -90,3 +90,13 @@ opacity: 1; } } + + x, y { + color: green; + } + + div.svelte-xyz, div.svelte-xyz y /* (unused) unused*/ { + z { + color: green; + } + } diff --git a/packages/svelte/tests/css/samples/global-block/input.svelte b/packages/svelte/tests/css/samples/global-block/input.svelte index a1833636a1..ee05205d67 100644 --- a/packages/svelte/tests/css/samples/global-block/input.svelte +++ b/packages/svelte/tests/css/samples/global-block/input.svelte @@ -92,4 +92,14 @@ opacity: 1; } } + + :global x, :global y { + color: green; + } + + div :global, div :global y, unused :global { + z { + color: green; + } + } From 90563e903fd8428db4c087b6fa4f51e0200dbb45 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 14 Apr 2025 14:38:31 -0400 Subject: [PATCH 0349/1429] feat: add partial evaluation (#15494) * feat: add partial evaluation * fix * tweak * more * more * evaluate stuff in template * update test * SSR * unused * changeset * remove TODO * Apply suggestions from code review Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> * allow unknown operators * use blocks and block-scoping in switch statement --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- .changeset/selfish-onions-begin.md | 5 + .../client/visitors/RegularElement.js | 9 +- .../client/visitors/shared/utils.js | 26 +- .../server/visitors/shared/utils.js | 16 +- packages/svelte/src/compiler/phases/scope.js | 322 +++++++++++++++++- .../_expected/client/index.svelte.js | 6 +- .../_expected/server/index.svelte.js | 2 +- .../_expected/client/index.svelte.js | 2 +- .../samples/attached-sourcemap/input.svelte | 2 +- 9 files changed, 357 insertions(+), 33 deletions(-) create mode 100644 .changeset/selfish-onions-begin.md diff --git a/.changeset/selfish-onions-begin.md b/.changeset/selfish-onions-begin.md new file mode 100644 index 0000000000..decf0d5fc6 --- /dev/null +++ b/.changeset/selfish-onions-begin.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: partially evaluate certain expressions diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 45a594af1f..fa4ee9867f 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -685,14 +685,13 @@ function build_element_special_value_attribute(element, node_id, attribute, cont : value ); + const evaluated = context.state.scope.evaluate(value); + const assignment = b.assignment('=', b.member(node_id, '__value'), value); + const inner_assignment = b.assignment( '=', b.member(node_id, 'value'), - b.conditional( - b.binary('==', b.null, b.assignment('=', b.member(node_id, '__value'), value)), - b.literal(''), // render null/undefined values as empty string to support placeholder options - value - ) + evaluated.is_defined ? assignment : b.logical('??', assignment, b.literal('')) ); const update = b.stmt( diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index af6e56f70c..55362d75af 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -89,21 +89,21 @@ export function build_template_chunk( } } - const is_defined = - value.type === 'BinaryExpression' || - (value.type === 'UnaryExpression' && value.operator !== 'void') || - (value.type === 'LogicalExpression' && value.right.type === 'Literal') || - (value.type === 'Identifier' && value.name === state.analysis.props_id?.name); - - if (!is_defined) { - // add `?? ''` where necessary (TODO optimise more cases) - value = b.logical('??', value, b.literal('')); - } + const evaluated = state.scope.evaluate(value); - expressions.push(value); + if (evaluated.is_known) { + quasi.value.cooked += evaluated.value + ''; + } else { + if (!evaluated.is_defined) { + // add `?? ''` where necessary + value = b.logical('??', value, b.literal('')); + } - quasi = b.quasi('', i + 1 === values.length); - quasis.push(quasi); + expressions.push(value); + + quasi = b.quasi('', i + 1 === values.length); + quasis.push(quasi); + } } } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js index 2c6aa2f316..807e12a8fa 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js @@ -44,15 +44,17 @@ export function process_children(nodes, { visit, state }) { if (node.type === 'Text' || node.type === 'Comment') { quasi.value.cooked += node.type === 'Comment' ? `` : escape_html(node.data); - } else if (node.type === 'ExpressionTag' && node.expression.type === 'Literal') { - if (node.expression.value != null) { - quasi.value.cooked += escape_html(node.expression.value + ''); - } } else { - expressions.push(b.call('$.escape', /** @type {Expression} */ (visit(node.expression)))); + const evaluated = state.scope.evaluate(node.expression); + + if (evaluated.is_known) { + quasi.value.cooked += escape_html((evaluated.value ?? '') + ''); + } else { + expressions.push(b.call('$.escape', /** @type {Expression} */ (visit(node.expression)))); - quasi = b.quasi('', i + 1 === sequence.length); - quasis.push(quasi); + quasi = b.quasi('', i + 1 === sequence.length); + quasis.push(quasi); + } } } diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index b6063c3234..73dfeea1d9 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -1,4 +1,4 @@ -/** @import { ArrowFunctionExpression, ClassDeclaration, Expression, FunctionDeclaration, FunctionExpression, Identifier, ImportDeclaration, MemberExpression, Node, Pattern, VariableDeclarator } from 'estree' */ +/** @import { ArrowFunctionExpression, BinaryOperator, ClassDeclaration, Expression, FunctionDeclaration, FunctionExpression, Identifier, ImportDeclaration, MemberExpression, LogicalOperator, Node, Pattern, UnaryOperator, VariableDeclarator } from 'estree' */ /** @import { Context, Visitor } from 'zimmerframe' */ /** @import { AST, BindingKind, DeclarationKind } from '#compiler' */ import is_reference from 'is-reference'; @@ -16,6 +16,11 @@ import { is_reserved, is_rune } from '../../utils.js'; import { determine_slot } from '../utils/slot.js'; import { validate_identifier_name } from './2-analyze/visitors/shared/utils.js'; +export const UNKNOWN = Symbol('unknown'); +/** Includes `BigInt` */ +export const NUMBER = Symbol('number'); +export const STRING = Symbol('string'); + export class Binding { /** @type {Scope} */ scope; @@ -34,7 +39,7 @@ export class Binding { * For destructured props such as `let { foo = 'bar' } = $props()` this is `'bar'` and not `$props()` * @type {null | Expression | FunctionDeclaration | ClassDeclaration | ImportDeclaration | AST.EachBlock | AST.SnippetBlock} */ - initial; + initial = null; /** @type {Array<{ node: Identifier; path: AST.SvelteNode[] }>} */ references = []; @@ -100,6 +105,264 @@ export class Binding { } } +class Evaluation { + /** @type {Set} */ + values = new Set(); + + /** + * True if there is exactly one possible value + * @readonly + * @type {boolean} + */ + is_known = true; + + /** + * True if the value is known to not be null/undefined + * @readonly + * @type {boolean} + */ + is_defined = true; + + /** + * True if the value is known to be a string + * @readonly + * @type {boolean} + */ + is_string = true; + + /** + * True if the value is known to be a number + * @readonly + * @type {boolean} + */ + is_number = true; + + /** + * @readonly + * @type {any} + */ + value = undefined; + + /** + * + * @param {Scope} scope + * @param {Expression} expression + */ + constructor(scope, expression) { + switch (expression.type) { + case 'Literal': { + this.values.add(expression.value); + break; + } + + case 'Identifier': { + const binding = scope.get(expression.name); + + if (binding) { + if ( + binding.initial?.type === 'CallExpression' && + get_rune(binding.initial, scope) === '$props.id' + ) { + this.values.add(STRING); + break; + } + + const is_prop = + binding.kind === 'prop' || + binding.kind === 'rest_prop' || + binding.kind === 'bindable_prop'; + + if (!binding.updated && binding.initial !== null && !is_prop) { + const evaluation = binding.scope.evaluate(/** @type {Expression} */ (binding.initial)); + for (const value of evaluation.values) { + this.values.add(value); + } + break; + } + + // TODO each index is always defined + } + + // TODO glean what we can from reassignments + // TODO one day, expose props and imports somehow + + this.values.add(UNKNOWN); + break; + } + + case 'BinaryExpression': { + const a = scope.evaluate(/** @type {Expression} */ (expression.left)); // `left` cannot be `PrivateIdentifier` unless operator is `in` + const b = scope.evaluate(expression.right); + + if (a.is_known && b.is_known) { + this.values.add(binary[expression.operator](a.value, b.value)); + break; + } + + switch (expression.operator) { + case '!=': + case '!==': + case '<': + case '<=': + case '>': + case '>=': + case '==': + case '===': + case 'in': + case 'instanceof': + this.values.add(true); + this.values.add(false); + break; + + case '%': + case '&': + case '*': + case '**': + case '-': + case '/': + case '<<': + case '>>': + case '>>>': + case '^': + case '|': + this.values.add(NUMBER); + break; + + case '+': + if (a.is_string || b.is_string) { + this.values.add(STRING); + } else if (a.is_number && b.is_number) { + this.values.add(NUMBER); + } else { + this.values.add(STRING); + this.values.add(NUMBER); + } + break; + + default: + this.values.add(UNKNOWN); + } + break; + } + + case 'ConditionalExpression': { + const test = scope.evaluate(expression.test); + const consequent = scope.evaluate(expression.consequent); + const alternate = scope.evaluate(expression.alternate); + + if (test.is_known) { + for (const value of (test.value ? consequent : alternate).values) { + this.values.add(value); + } + } else { + for (const value of consequent.values) { + this.values.add(value); + } + + for (const value of alternate.values) { + this.values.add(value); + } + } + break; + } + + case 'LogicalExpression': { + const a = scope.evaluate(expression.left); + const b = scope.evaluate(expression.right); + + if (a.is_known) { + if (b.is_known) { + this.values.add(logical[expression.operator](a.value, b.value)); + break; + } + + if ( + (expression.operator === '&&' && !a.value) || + (expression.operator === '||' && a.value) || + (expression.operator === '??' && a.value != null) + ) { + this.values.add(a.value); + } else { + for (const value of b.values) { + this.values.add(value); + } + } + + break; + } + + for (const value of a.values) { + this.values.add(value); + } + + for (const value of b.values) { + this.values.add(value); + } + break; + } + + case 'UnaryExpression': { + const argument = scope.evaluate(expression.argument); + + if (argument.is_known) { + this.values.add(unary[expression.operator](argument.value)); + break; + } + + switch (expression.operator) { + case '!': + case 'delete': + this.values.add(false); + this.values.add(true); + break; + + case '+': + case '-': + case '~': + this.values.add(NUMBER); + break; + + case 'typeof': + this.values.add(STRING); + break; + + case 'void': + this.values.add(undefined); + break; + + default: + this.values.add(UNKNOWN); + } + break; + } + + default: { + this.values.add(UNKNOWN); + } + } + + for (const value of this.values) { + this.value = value; // saves having special logic for `size === 1` + + if (value !== STRING && typeof value !== 'string') { + this.is_string = false; + } + + if (value !== NUMBER && typeof value !== 'number') { + this.is_number = false; + } + + if (value == null || value === UNKNOWN) { + this.is_defined = false; + } + } + + if (this.values.size > 1 || typeof this.value === 'symbol') { + this.is_known = false; + } + } +} + export class Scope { /** @type {ScopeRoot} */ root; @@ -279,8 +542,63 @@ export class Scope { this.root.conflicts.add(node.name); } } + + /** + * Does partial evaluation to find an exact value or at least the rough type of the expression. + * Only call this once scope has been fully generated in a first pass, + * else this evaluates on incomplete data and may yield wrong results. + * @param {Expression} expression + * @param {Set} values + */ + evaluate(expression, values = new Set()) { + return new Evaluation(this, expression); + } } +/** @type {Record any>} */ +const binary = { + '!=': (left, right) => left != right, + '!==': (left, right) => left !== right, + '<': (left, right) => left < right, + '<=': (left, right) => left <= right, + '>': (left, right) => left > right, + '>=': (left, right) => left >= right, + '==': (left, right) => left == right, + '===': (left, right) => left === right, + in: (left, right) => left in right, + instanceof: (left, right) => left instanceof right, + '%': (left, right) => left % right, + '&': (left, right) => left & right, + '*': (left, right) => left * right, + '**': (left, right) => left ** right, + '+': (left, right) => left + right, + '-': (left, right) => left - right, + '/': (left, right) => left / right, + '<<': (left, right) => left << right, + '>>': (left, right) => left >> right, + '>>>': (left, right) => left >>> right, + '^': (left, right) => left ^ right, + '|': (left, right) => left | right +}; + +/** @type {Record any>} */ +const unary = { + '-': (argument) => -argument, + '+': (argument) => +argument, + '!': (argument) => !argument, + '~': (argument) => ~argument, + typeof: (argument) => typeof argument, + void: () => undefined, + delete: () => true +}; + +/** @type {Record any>} */ +const logical = { + '||': (left, right) => left || right, + '&&': (left, right) => left && right, + '??': (left, right) => left ?? right +}; + export class ScopeRoot { /** @type {Set} */ conflicts = new Set(); diff --git a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js index 332c909ebe..21f6ed9680 100644 --- a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js @@ -10,11 +10,11 @@ export default function Nullish_coallescence_omittance($$anchor) { var fragment = root(); var h1 = $.first_child(fragment); - h1.textContent = `Hello, ${name ?? ''}!`; + h1.textContent = 'Hello, world!'; var b = $.sibling(h1, 2); - b.textContent = `${1 ?? 'stuff'}${2 ?? 'more stuff'}${3 ?? 'even more stuff'}`; + b.textContent = '123'; var button = $.sibling(b, 2); @@ -26,7 +26,7 @@ export default function Nullish_coallescence_omittance($$anchor) { var h1_1 = $.sibling(button, 2); - h1_1.textContent = `Hello, ${name ?? 'earth' ?? ''}`; + h1_1.textContent = 'Hello, world'; $.template_effect(() => $.set_text(text, `Count is ${$.get(count) ?? ''}`)); $.append($$anchor, fragment); } diff --git a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js index 8181bfd98e..3b23befcd4 100644 --- a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/server/index.svelte.js @@ -4,5 +4,5 @@ export default function Nullish_coallescence_omittance($$payload) { let name = 'world'; let count = 0; - $$payload.out += `

Hello, ${$.escape(name)}!

${$.escape(1 ?? 'stuff')}${$.escape(2 ?? 'more stuff')}${$.escape(3 ?? 'even more stuff')}

Hello, ${$.escape(name ?? 'earth' ?? null)}

`; + $$payload.out += `

Hello, world!

123

Hello, world

`; } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js index 46d376aca2..b341d39f28 100644 --- a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js @@ -38,7 +38,7 @@ export default function Skip_static_subtree($$anchor, $$props) { var select = $.sibling(div_1, 2); var option = $.child(select); - option.value = null == (option.__value = 'a') ? '' : 'a'; + option.value = option.__value = 'a'; $.reset(select); var img = $.sibling(select, 2); diff --git a/packages/svelte/tests/sourcemaps/samples/attached-sourcemap/input.svelte b/packages/svelte/tests/sourcemaps/samples/attached-sourcemap/input.svelte index 21a47a72a9..715bbda8d9 100644 --- a/packages/svelte/tests/sourcemaps/samples/attached-sourcemap/input.svelte +++ b/packages/svelte/tests/sourcemaps/samples/attached-sourcemap/input.svelte @@ -8,4 +8,4 @@ replace_me_script = 'hello' ; -

{done_replace_script_2}

+

{Math.random() < 1 && done_replace_script_2}

From 6a7e53feaa53425624a47d7ebed98ff8d6fb1d8b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 22:23:50 -0400 Subject: [PATCH 0350/1429] Version Packages (#15764) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/green-starfishes-shave.md | 5 ----- .changeset/selfish-onions-begin.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 .changeset/green-starfishes-shave.md delete mode 100644 .changeset/selfish-onions-begin.md diff --git a/.changeset/green-starfishes-shave.md b/.changeset/green-starfishes-shave.md deleted file mode 100644 index 967bba753c..0000000000 --- a/.changeset/green-starfishes-shave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: relax `:global` selector list validation diff --git a/.changeset/selfish-onions-begin.md b/.changeset/selfish-onions-begin.md deleted file mode 100644 index decf0d5fc6..0000000000 --- a/.changeset/selfish-onions-begin.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: partially evaluate certain expressions diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 58f2317796..c8f0ad7ed9 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.27.0 + +### Minor Changes + +- feat: partially evaluate certain expressions ([#15494](https://github.com/sveltejs/svelte/pull/15494)) + +### Patch Changes + +- fix: relax `:global` selector list validation ([#15762](https://github.com/sveltejs/svelte/pull/15762)) + ## 5.26.3 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index b8654671ec..af78d2679a 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.26.3", + "version": "5.27.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 1909588146..27a39136f8 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.26.3'; +export const VERSION = '5.27.0'; export const PUBLIC_VERSION = '5'; From e079ac92b869a8405f2e76dd7a3d830cd3629c55 Mon Sep 17 00:00:00 2001 From: Elliott Johnson Date: Tue, 15 Apr 2025 19:58:51 -0600 Subject: [PATCH 0351/1429] fix: Throw on unrendered snippets in `dev` (#15766) --- .changeset/strong-pianos-promise.md | 5 +++ .../98-reference/.generated/shared-errors.md | 37 +++++++++++++++++++ .../svelte/messages/shared-errors/errors.md | 35 ++++++++++++++++++ .../server/visitors/SnippetBlock.js | 25 ++++++++----- .../server/visitors/shared/component.js | 9 ++++- .../src/internal/client/dom/blocks/snippet.js | 7 +++- packages/svelte/src/internal/client/index.js | 3 +- packages/svelte/src/internal/server/index.js | 3 +- packages/svelte/src/internal/shared/errors.js | 15 ++++++++ .../svelte/src/internal/shared/validate.js | 12 ++++++ .../_config.js | 8 ++++ .../main.svelte | 5 +++ .../_config.js | 3 ++ .../main.svelte | 5 +++ .../_config.js | 3 ++ .../main.svelte | 5 +++ .../unrendered-children.svelte | 5 +++ .../_config.js | 8 ++++ .../main.svelte | 5 +++ .../unrendered-children.svelte | 5 +++ .../_expected/server/index.svelte.js | 4 +- 21 files changed, 192 insertions(+), 15 deletions(-) create mode 100644 .changeset/strong-pianos-promise.md create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-dev/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-dev/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-prod/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-prod/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev-prod/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev-prod/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev-prod/unrendered-children.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev/unrendered-children.svelte diff --git a/.changeset/strong-pianos-promise.md b/.changeset/strong-pianos-promise.md new file mode 100644 index 0000000000..f5214c7dcb --- /dev/null +++ b/.changeset/strong-pianos-promise.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: Throw on unrendered snippets in `dev` diff --git a/documentation/docs/98-reference/.generated/shared-errors.md b/documentation/docs/98-reference/.generated/shared-errors.md index 4c81d7b894..6c31aaafd0 100644 --- a/documentation/docs/98-reference/.generated/shared-errors.md +++ b/documentation/docs/98-reference/.generated/shared-errors.md @@ -60,6 +60,43 @@ Certain lifecycle methods can only be used during component initialisation. To f ``` +### snippet_without_render_tag + +``` +Attempted to render a snippet without a `{@render}` block. This would cause the snippet code to be stringified instead of its content being rendered to the DOM. To fix this, change `{snippet}` to `{@render snippet()}`. +``` + +A component throwing this error will look something like this (`children` is not being rendered): + +```svelte + + +{children} +``` + +...or like this (a parent component is passing a snippet where a non-snippet value is expected): + +```svelte + + + {#snippet label()} + Hi! + {/snippet} + +``` + +```svelte + + + + +

{label}

+``` + ### store_invalid_shape ``` diff --git a/packages/svelte/messages/shared-errors/errors.md b/packages/svelte/messages/shared-errors/errors.md index 20f3d193d9..4b4d332202 100644 --- a/packages/svelte/messages/shared-errors/errors.md +++ b/packages/svelte/messages/shared-errors/errors.md @@ -52,6 +52,41 @@ Certain lifecycle methods can only be used during component initialisation. To f ``` +## snippet_without_render_tag + +> Attempted to render a snippet without a `{@render}` block. This would cause the snippet code to be stringified instead of its content being rendered to the DOM. To fix this, change `{snippet}` to `{@render snippet()}`. + +A component throwing this error will look something like this (`children` is not being rendered): + +```svelte + + +{children} +``` + +...or like this (a parent component is passing a snippet where a non-snippet value is expected): + +```svelte + + + {#snippet label()} + Hi! + {/snippet} + +``` + +```svelte + + + + +

{label}

+``` + ## store_invalid_shape > `%name%` is not a store with a `subscribe` method diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js index cae3e7d79c..a67fcc8885 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js @@ -1,4 +1,4 @@ -/** @import { BlockStatement } from 'estree' */ +/** @import { ArrowFunctionExpression, BlockStatement, CallExpression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ import { dev } from '../../../../state.js'; @@ -9,20 +9,27 @@ import * as b from '../../../../utils/builders.js'; * @param {ComponentContext} context */ export function SnippetBlock(node, context) { - const fn = b.function_declaration( - node.expression, - [b.id('$$payload'), ...node.parameters], - /** @type {BlockStatement} */ (context.visit(node.body)) - ); + const body = /** @type {BlockStatement} */ (context.visit(node.body)); + if (dev) { - fn.body.body.unshift(b.stmt(b.call('$.validate_snippet_args', b.id('$$payload')))); + body.body.unshift(b.stmt(b.call('$.validate_snippet_args', b.id('$$payload')))); } + + /** @type {ArrowFunctionExpression | CallExpression} */ + let fn = b.arrow([b.id('$$payload'), ...node.parameters], body); + + if (dev) { + fn = b.call('$.prevent_snippet_stringification', fn); + } + + const declaration = b.declaration('const', [b.declarator(node.expression, fn)]); + // @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone fn.___snippet = true; if (node.metadata.can_hoist) { - context.state.hoisted.push(fn); + context.state.hoisted.push(declaration); } else { - context.state.init.push(fn); + context.state.init.push(declaration); } } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js index 695161ff9b..f4b3dd1b09 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js @@ -4,6 +4,7 @@ import { empty_comment, build_attribute_value } from './utils.js'; import * as b from '../../../../../utils/builders.js'; import { is_element_node } from '../../../../nodes.js'; +import { dev } from '../../../../../state.js'; /** * @param {AST.Component | AST.SvelteComponent | AST.SvelteSelf} node @@ -238,7 +239,13 @@ export function build_inline_component(node, expression, context) { ) ) { // create `children` prop... - push_prop(b.prop('init', b.id('children'), slot_fn)); + push_prop( + b.prop( + 'init', + b.id('children'), + dev ? b.call('$.prevent_snippet_stringification', slot_fn) : slot_fn + ) + ); // and `$$slots.default: true` so that `` on the child works serialized_slots.push(b.init(slot_name, b.true)); diff --git a/packages/svelte/src/internal/client/dom/blocks/snippet.js b/packages/svelte/src/internal/client/dom/blocks/snippet.js index b916a02ce5..a48153900f 100644 --- a/packages/svelte/src/internal/client/dom/blocks/snippet.js +++ b/packages/svelte/src/internal/client/dom/blocks/snippet.js @@ -15,6 +15,7 @@ import * as e from '../../errors.js'; import { DEV } from 'esm-env'; import { get_first_child, get_next_sibling } from '../operations.js'; import { noop } from '../../../shared/utils.js'; +import { prevent_snippet_stringification } from '../../../shared/validate.js'; /** * @template {(node: TemplateNode, ...args: any[]) => void} SnippetFn @@ -60,7 +61,7 @@ export function snippet(node, get_snippet, ...args) { * @param {(node: TemplateNode, ...args: any[]) => void} fn */ export function wrap_snippet(component, fn) { - return (/** @type {TemplateNode} */ node, /** @type {any[]} */ ...args) => { + const snippet = (/** @type {TemplateNode} */ node, /** @type {any[]} */ ...args) => { var previous_component_function = dev_current_component_function; set_dev_current_component_function(component); @@ -70,6 +71,10 @@ export function wrap_snippet(component, fn) { set_dev_current_component_function(previous_component_function); } }; + + prevent_snippet_stringification(snippet); + + return snippet; } /** diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index e977bf3b0f..14d6e29f5b 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -157,7 +157,8 @@ export { invalid_default_snippet, validate_dynamic_element_tag, validate_store, - validate_void_dynamic_element + validate_void_dynamic_element, + prevent_snippet_stringification } from '../shared/validate.js'; export { strict_equals, equals } from './dev/equality.js'; export { log_if_contains_state } from './dev/console-log.js'; diff --git a/packages/svelte/src/internal/server/index.js b/packages/svelte/src/internal/server/index.js index d711778a44..b58a1d4372 100644 --- a/packages/svelte/src/internal/server/index.js +++ b/packages/svelte/src/internal/server/index.js @@ -509,7 +509,8 @@ export { fallback } from '../shared/utils.js'; export { invalid_default_snippet, validate_dynamic_element_tag, - validate_void_dynamic_element + validate_void_dynamic_element, + prevent_snippet_stringification } from '../shared/validate.js'; export { escape_html as escape }; diff --git a/packages/svelte/src/internal/shared/errors.js b/packages/svelte/src/internal/shared/errors.js index 2e89dc1ad1..b8606fbf6f 100644 --- a/packages/svelte/src/internal/shared/errors.js +++ b/packages/svelte/src/internal/shared/errors.js @@ -48,6 +48,21 @@ export function lifecycle_outside_component(name) { } } +/** + * Attempted to render a snippet without a `{@render}` block. This would cause the snippet code to be stringified instead of its content being rendered to the DOM. To fix this, change `{snippet}` to `{@render snippet()}`. + * @returns {never} + */ +export function snippet_without_render_tag() { + if (DEV) { + const error = new Error(`snippet_without_render_tag\nAttempted to render a snippet without a \`{@render}\` block. This would cause the snippet code to be stringified instead of its content being rendered to the DOM. To fix this, change \`{snippet}\` to \`{@render snippet()}\`.\nhttps://svelte.dev/e/snippet_without_render_tag`); + + error.name = 'Svelte error'; + throw error; + } else { + throw new Error(`https://svelte.dev/e/snippet_without_render_tag`); + } +} + /** * `%name%` is not a store with a `subscribe` method * @param {string} name diff --git a/packages/svelte/src/internal/shared/validate.js b/packages/svelte/src/internal/shared/validate.js index 852c0e83bf..bbb237594b 100644 --- a/packages/svelte/src/internal/shared/validate.js +++ b/packages/svelte/src/internal/shared/validate.js @@ -35,3 +35,15 @@ export function validate_store(store, name) { e.store_invalid_shape(name); } } + +/** + * @template {() => unknown} T + * @param {T} fn + */ +export function prevent_snippet_stringification(fn) { + fn.toString = () => { + e.snippet_without_render_tag(); + return ''; + }; + return fn; +} diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-dev/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-dev/_config.js new file mode 100644 index 0000000000..94c5de10af --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-dev/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + runtime_error: 'snippet_without_render_tag' +}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-dev/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-dev/main.svelte new file mode 100644 index 0000000000..3f8edfe4fa --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-dev/main.svelte @@ -0,0 +1,5 @@ +{testSnippet} + +{#snippet testSnippet()} +

hi again

+{/snippet} diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-prod/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-prod/_config.js new file mode 100644 index 0000000000..f47bee71df --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-prod/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-prod/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-prod/main.svelte new file mode 100644 index 0000000000..3f8edfe4fa --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-block-without-render-tag-prod/main.svelte @@ -0,0 +1,5 @@ +{testSnippet} + +{#snippet testSnippet()} +

hi again

+{/snippet} diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev-prod/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev-prod/_config.js new file mode 100644 index 0000000000..f47bee71df --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev-prod/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev-prod/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev-prod/main.svelte new file mode 100644 index 0000000000..4a4ed3176f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev-prod/main.svelte @@ -0,0 +1,5 @@ + + +Hi diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev-prod/unrendered-children.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev-prod/unrendered-children.svelte new file mode 100644 index 0000000000..6b7154a5a4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev-prod/unrendered-children.svelte @@ -0,0 +1,5 @@ + + +{children} diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev/_config.js new file mode 100644 index 0000000000..94c5de10af --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + runtime_error: 'snippet_without_render_tag' +}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev/main.svelte new file mode 100644 index 0000000000..4a4ed3176f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev/main.svelte @@ -0,0 +1,5 @@ + + +Hi diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev/unrendered-children.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev/unrendered-children.svelte new file mode 100644 index 0000000000..6b7154a5a4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-children-without-render-tag-dev/unrendered-children.svelte @@ -0,0 +1,5 @@ + + +{children} diff --git a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js index cadae2cf15..04bfbf6ae4 100644 --- a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js @@ -1,9 +1,9 @@ import * as $ from 'svelte/internal/server'; import TextInput from './Child.svelte'; -function snippet($$payload) { +const snippet = ($$payload) => { $$payload.out += `Something`; -} +}; export default function Bind_component_snippet($$payload) { let value = ''; From ea4843c5ad6f37d2b480e55cdd9f9f27797d119e Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:24:04 +0200 Subject: [PATCH 0352/1429] fix: avoid unnecessary read version increments (#15777) Fixes #15262 --- .changeset/sweet-adults-complain.md | 5 +++++ packages/svelte/src/internal/client/runtime.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/sweet-adults-complain.md diff --git a/.changeset/sweet-adults-complain.md b/.changeset/sweet-adults-complain.md new file mode 100644 index 0000000000..429b034b3d --- /dev/null +++ b/.changeset/sweet-adults-complain.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: avoid unnecessary read version increments diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 2acad3d258..e621536055 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -469,7 +469,7 @@ export function update_reaction(reaction) { // we need to increment the read version to ensure that // any dependencies in this reaction aren't marked with // the same version - if (previous_reaction !== reaction) { + if (previous_reaction !== null && previous_reaction !== reaction) { read_version++; if (untracked_writes !== null) { From 2db5bccde32c207e03c74a02d48e049743a2c907 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 16 Apr 2025 18:22:21 -0400 Subject: [PATCH 0353/1429] chore: default params for html blocks (#15778) * chore: default params for html blocks * fix * changeset * update test --- .changeset/chatty-apples-flash.md | 5 ++++ .../3-transform/client/visitors/HtmlTag.js | 27 +++++++++++-------- .../src/internal/client/dom/blocks/html.js | 6 ++--- .../_expected/client/index.svelte.js | 2 +- 4 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 .changeset/chatty-apples-flash.md diff --git a/.changeset/chatty-apples-flash.md b/.changeset/chatty-apples-flash.md new file mode 100644 index 0000000000..fc689a003c --- /dev/null +++ b/.changeset/chatty-apples-flash.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: default params for html blocks diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/HtmlTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/HtmlTag.js index 32439879de..ace73691d1 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/HtmlTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/HtmlTag.js @@ -11,17 +11,22 @@ import * as b from '../../../../utils/builders.js'; export function HtmlTag(node, context) { context.state.template.push(''); - // push into init, so that bindings run afterwards, which might trigger another run and override hydration - context.state.init.push( - b.stmt( - b.call( - '$.html', - context.state.node, - b.thunk(/** @type {Expression} */ (context.visit(node.expression))), - b.literal(context.state.metadata.namespace === 'svg'), - b.literal(context.state.metadata.namespace === 'mathml'), - is_ignored(node, 'hydration_html_changed') && b.true - ) + const expression = /** @type {Expression} */ (context.visit(node.expression)); + + const is_svg = context.state.metadata.namespace === 'svg'; + const is_mathml = context.state.metadata.namespace === 'mathml'; + + const statement = b.stmt( + b.call( + '$.html', + context.state.node, + b.thunk(expression), + is_svg && b.true, + is_mathml && b.true, + is_ignored(node, 'hydration_html_changed') && b.true ) ); + + // push into init, so that bindings run afterwards, which might trigger another run and override hydration + context.state.init.push(statement); } diff --git a/packages/svelte/src/internal/client/dom/blocks/html.js b/packages/svelte/src/internal/client/dom/blocks/html.js index b3fc5a9c72..e332108012 100644 --- a/packages/svelte/src/internal/client/dom/blocks/html.js +++ b/packages/svelte/src/internal/client/dom/blocks/html.js @@ -34,12 +34,12 @@ function check_hash(element, server_hash, value) { /** * @param {Element | Text | Comment} node * @param {() => string} get_value - * @param {boolean} svg - * @param {boolean} mathml + * @param {boolean} [svg] + * @param {boolean} [mathml] * @param {boolean} [skip_warning] * @returns {void} */ -export function html(node, get_value, svg, mathml, skip_warning) { +export function html(node, get_value, svg = false, mathml = false, skip_warning = false) { var anchor = node; var value = ''; diff --git a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js index b341d39f28..541b56a407 100644 --- a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js @@ -13,7 +13,7 @@ export default function Skip_static_subtree($$anchor, $$props) { var node = $.sibling(h1, 10); - $.html(node, () => $$props.content, false, false); + $.html(node, () => $$props.content); $.next(14); $.reset(main); From 3d9a9ab32d31a3a09794bffaead83b9043947dca Mon Sep 17 00:00:00 2001 From: Thor Galle Date: Thu, 17 Apr 2025 08:35:44 +0200 Subject: [PATCH 0354/1429] docs: correct the suggested type for custom events without detail (Svelte 4) (#15763) * docs: correct the suggested type for custom events without detail * docs: generate fixed types for the Svelte 4 event dispatcher --- .changeset/fifty-buckets-return.md | 5 +++++ packages/svelte/src/index-client.js | 2 +- packages/svelte/types/index.d.ts | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/fifty-buckets-return.md diff --git a/.changeset/fifty-buckets-return.md b/.changeset/fifty-buckets-return.md new file mode 100644 index 0000000000..7c79f0b596 --- /dev/null +++ b/.changeset/fifty-buckets-return.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +correct the suggested type for custom events without detail diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js index fd8e999da7..efd5628ae9 100644 --- a/packages/svelte/src/index-client.js +++ b/packages/svelte/src/index-client.js @@ -114,7 +114,7 @@ function create_custom_event(type, detail, { bubbles = false, cancelable = false * The event dispatcher can be typed to narrow the allowed event names and the type of the `detail` argument: * ```ts * const dispatch = createEventDispatcher<{ - * loaded: never; // does not take a detail argument + * loaded: null; // does not take a detail argument * change: string; // takes a detail argument of type string, which is required * optional: number | null; // takes an optional detail argument of type number * }>(); diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 6f12daf187..8fc174b0a9 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -381,7 +381,7 @@ declare module 'svelte' { * The event dispatcher can be typed to narrow the allowed event names and the type of the `detail` argument: * ```ts * const dispatch = createEventDispatcher<{ - * loaded: never; // does not take a detail argument + * loaded: null; // does not take a detail argument * change: string; // takes a detail argument of type string, which is required * optional: number | null; // takes an optional detail argument of type number * }>(); From 19836e29f23a70223e3e5601b33345b9bdf617aa Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 17 Apr 2025 08:16:27 -0400 Subject: [PATCH 0355/1429] chore: add log_effect_tree utility (#15780) --- .../svelte/src/internal/client/dev/debug.js | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 packages/svelte/src/internal/client/dev/debug.js diff --git a/packages/svelte/src/internal/client/dev/debug.js b/packages/svelte/src/internal/client/dev/debug.js new file mode 100644 index 0000000000..f449dfa2cc --- /dev/null +++ b/packages/svelte/src/internal/client/dev/debug.js @@ -0,0 +1,109 @@ +/** @import { Derived, Effect, Value } from '#client' */ + +import { + BLOCK_EFFECT, + BOUNDARY_EFFECT, + BRANCH_EFFECT, + CLEAN, + DERIVED, + EFFECT, + MAYBE_DIRTY, + RENDER_EFFECT, + ROOT_EFFECT +} from '../constants.js'; + +/** + * + * @param {Effect} effect + */ +export function root(effect) { + while (effect.parent !== null) { + effect = effect.parent; + } + + return effect; +} + +/** + * + * @param {Effect} effect + */ +export function log_effect_tree(effect, depth = 0) { + const flags = effect.f; + + let label = '(unknown)'; + + if ((flags & ROOT_EFFECT) !== 0) { + label = 'root'; + } else if ((flags & BOUNDARY_EFFECT) !== 0) { + label = 'boundary'; + } else if ((flags & BLOCK_EFFECT) !== 0) { + label = 'block'; + } else if ((flags & BRANCH_EFFECT) !== 0) { + label = 'branch'; + } else if ((flags & RENDER_EFFECT) !== 0) { + label = 'render effect'; + } else if ((flags & EFFECT) !== 0) { + label = 'effect'; + } + + let status = + (flags & CLEAN) !== 0 ? 'clean' : (flags & MAYBE_DIRTY) !== 0 ? 'maybe dirty' : 'dirty'; + + // eslint-disable-next-line no-console + console.group(`%c${label} (${status})`, `font-weight: ${status === 'clean' ? 'normal' : 'bold'}`); + + if (depth === 0) { + const callsite = new Error().stack + ?.split('\n')[2] + .replace(/\s+at (?: \w+\(?)?(.+)\)?/, (m, $1) => $1.replace(/\?[^:]+/, '')); + + // eslint-disable-next-line no-console + console.log(callsite); + } + + if (effect.deps !== null) { + // eslint-disable-next-line no-console + console.groupCollapsed('%cdeps', 'font-weight: normal'); + + for (const dep of effect.deps) { + log_dep(dep); + } + + // eslint-disable-next-line no-console + console.groupEnd(); + } + + let child = effect.first; + while (child !== null) { + log_effect_tree(child, depth + 1); + child = child.next; + } + + // eslint-disable-next-line no-console + console.groupEnd(); +} + +/** + * + * @param {Value} dep + */ +function log_dep(dep) { + if ((dep.f & DERIVED) !== 0) { + const derived = /** @type {Derived} */ (dep); + + // eslint-disable-next-line no-console + console.groupCollapsed('%cderived', 'font-weight: normal', derived.v); + if (derived.deps) { + for (const d of derived.deps) { + log_dep(d); + } + } + + // eslint-disable-next-line no-console + console.groupEnd(); + } else { + // eslint-disable-next-line no-console + console.log('state', dep.v); + } +} From 80f62b5b100e322157e01b2cad4eb451c1d38606 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 17 Apr 2025 08:16:52 -0400 Subject: [PATCH 0356/1429] chore: use template_effect for html tags (#15779) --- .../src/internal/client/dom/blocks/html.js | 113 ++++++++---------- .../src/internal/client/reactivity/effects.js | 28 +++-- 2 files changed, 69 insertions(+), 72 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/blocks/html.js b/packages/svelte/src/internal/client/dom/blocks/html.js index e332108012..92c8243478 100644 --- a/packages/svelte/src/internal/client/dom/blocks/html.js +++ b/packages/svelte/src/internal/client/dom/blocks/html.js @@ -1,6 +1,6 @@ /** @import { Effect, TemplateNode } from '#client' */ import { FILENAME, HYDRATION_ERROR } from '../../../../constants.js'; -import { block, branch, destroy_effect } from '../../reactivity/effects.js'; +import { remove_effect_dom, template_effect } from '../../reactivity/effects.js'; import { hydrate_next, hydrate_node, hydrating, set_hydrate_node } from '../hydration.js'; import { create_fragment_from_html } from '../reconciler.js'; import { assign_nodes } from '../template.js'; @@ -9,6 +9,7 @@ import { hash, sanitize_location } from '../../../../utils.js'; import { DEV } from 'esm-env'; import { dev_current_component_function } from '../../context.js'; import { get_first_child, get_next_sibling } from '../operations.js'; +import { active_effect } from '../../runtime.js'; /** * @param {Element} element @@ -44,79 +45,71 @@ export function html(node, get_value, svg = false, mathml = false, skip_warning var value = ''; - /** @type {Effect | undefined} */ - var effect; + template_effect(() => { + var effect = /** @type {Effect} */ (active_effect); - block(() => { if (value === (value = get_value() ?? '')) { - if (hydrating) { - hydrate_next(); - } + if (hydrating) hydrate_next(); return; } - if (effect !== undefined) { - destroy_effect(effect); - effect = undefined; + if (effect.nodes_start !== null) { + remove_effect_dom(effect.nodes_start, /** @type {TemplateNode} */ (effect.nodes_end)); + effect.nodes_start = effect.nodes_end = null; } if (value === '') return; - effect = branch(() => { - if (hydrating) { - // We're deliberately not trying to repair mismatches between server and client, - // as it's costly and error-prone (and it's an edge case to have a mismatch anyway) - var hash = /** @type {Comment} */ (hydrate_node).data; - var next = hydrate_next(); - var last = next; - - while ( - next !== null && - (next.nodeType !== 8 || /** @type {Comment} */ (next).data !== '') - ) { - last = next; - next = /** @type {TemplateNode} */ (get_next_sibling(next)); - } - - if (next === null) { - w.hydration_mismatch(); - throw HYDRATION_ERROR; - } - - if (DEV && !skip_warning) { - check_hash(/** @type {Element} */ (next.parentNode), hash, value); - } - - assign_nodes(hydrate_node, last); - anchor = set_hydrate_node(next); - return; - } + if (hydrating) { + // We're deliberately not trying to repair mismatches between server and client, + // as it's costly and error-prone (and it's an edge case to have a mismatch anyway) + var hash = /** @type {Comment} */ (hydrate_node).data; + var next = hydrate_next(); + var last = next; - var html = value + ''; - if (svg) html = `${html}`; - else if (mathml) html = `${html}`; + while (next !== null && (next.nodeType !== 8 || /** @type {Comment} */ (next).data !== '')) { + last = next; + next = /** @type {TemplateNode} */ (get_next_sibling(next)); + } - // Don't use create_fragment_with_script_from_html here because that would mean script tags are executed. - // @html is basically `.innerHTML = ...` and that doesn't execute scripts either due to security reasons. - /** @type {DocumentFragment | Element} */ - var node = create_fragment_from_html(html); + if (next === null) { + w.hydration_mismatch(); + throw HYDRATION_ERROR; + } - if (svg || mathml) { - node = /** @type {Element} */ (get_first_child(node)); + if (DEV && !skip_warning) { + check_hash(/** @type {Element} */ (next.parentNode), hash, value); } - assign_nodes( - /** @type {TemplateNode} */ (get_first_child(node)), - /** @type {TemplateNode} */ (node.lastChild) - ); - - if (svg || mathml) { - while (get_first_child(node)) { - anchor.before(/** @type {Node} */ (get_first_child(node))); - } - } else { - anchor.before(node); + assign_nodes(hydrate_node, last); + anchor = set_hydrate_node(next); + return; + } + + var html = value + ''; + if (svg) html = `${html}`; + else if (mathml) html = `${html}`; + + // Don't use create_fragment_with_script_from_html here because that would mean script tags are executed. + // @html is basically `.innerHTML = ...` and that doesn't execute scripts either due to security reasons. + /** @type {DocumentFragment | Element} */ + var node = create_fragment_from_html(html); + + if (svg || mathml) { + node = /** @type {Element} */ (get_first_child(node)); + } + + assign_nodes( + /** @type {TemplateNode} */ (get_first_child(node)), + /** @type {TemplateNode} */ (node.lastChild) + ); + + if (svg || mathml) { + while (get_first_child(node)) { + anchor.before(/** @type {Node} */ (get_first_child(node))); } - }); + } else { + anchor.before(node); + } }); } diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 468bb94ab4..76b014c916 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -427,18 +427,7 @@ export function destroy_effect(effect, remove_dom = true) { var removed = false; if ((remove_dom || (effect.f & HEAD_EFFECT) !== 0) && effect.nodes_start !== null) { - /** @type {TemplateNode | null} */ - var node = effect.nodes_start; - var end = effect.nodes_end; - - while (node !== null) { - /** @type {TemplateNode | null} */ - var next = node === end ? null : /** @type {TemplateNode} */ (get_next_sibling(node)); - - node.remove(); - node = next; - } - + remove_effect_dom(effect.nodes_start, /** @type {TemplateNode} */ (effect.nodes_end)); removed = true; } @@ -480,6 +469,21 @@ export function destroy_effect(effect, remove_dom = true) { null; } +/** + * + * @param {TemplateNode | null} node + * @param {TemplateNode} end + */ +export function remove_effect_dom(node, end) { + while (node !== null) { + /** @type {TemplateNode | null} */ + var next = node === end ? null : /** @type {TemplateNode} */ (get_next_sibling(node)); + + node.remove(); + node = next; + } +} + /** * Detach an effect from the effect tree, freeing up memory and * reducing the amount of work that happens on subsequent traversals From 189bd4788e9de8fc4f65495040e5dce6ed42b9ac Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 09:03:18 -0400 Subject: [PATCH 0357/1429] Version Packages (#15772) * Version Packages * Update packages/svelte/CHANGELOG.md --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Rich Harris --- .changeset/chatty-apples-flash.md | 5 ----- .changeset/fifty-buckets-return.md | 5 ----- .changeset/strong-pianos-promise.md | 5 ----- .changeset/sweet-adults-complain.md | 5 ----- packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 7 files changed, 14 insertions(+), 22 deletions(-) delete mode 100644 .changeset/chatty-apples-flash.md delete mode 100644 .changeset/fifty-buckets-return.md delete mode 100644 .changeset/strong-pianos-promise.md delete mode 100644 .changeset/sweet-adults-complain.md diff --git a/.changeset/chatty-apples-flash.md b/.changeset/chatty-apples-flash.md deleted file mode 100644 index fc689a003c..0000000000 --- a/.changeset/chatty-apples-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: default params for html blocks diff --git a/.changeset/fifty-buckets-return.md b/.changeset/fifty-buckets-return.md deleted file mode 100644 index 7c79f0b596..0000000000 --- a/.changeset/fifty-buckets-return.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -correct the suggested type for custom events without detail diff --git a/.changeset/strong-pianos-promise.md b/.changeset/strong-pianos-promise.md deleted file mode 100644 index f5214c7dcb..0000000000 --- a/.changeset/strong-pianos-promise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: Throw on unrendered snippets in `dev` diff --git a/.changeset/sweet-adults-complain.md b/.changeset/sweet-adults-complain.md deleted file mode 100644 index 429b034b3d..0000000000 --- a/.changeset/sweet-adults-complain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: avoid unnecessary read version increments diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index c8f0ad7ed9..4ffcb263ab 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.27.1 + +### Patch Changes + +- chore: default params for html blocks ([#15778](https://github.com/sveltejs/svelte/pull/15778)) + +- fix: correct suggested type for custom events without detail ([#15763](https://github.com/sveltejs/svelte/pull/15763)) + +- fix: Throw on unrendered snippets in `dev` ([#15766](https://github.com/sveltejs/svelte/pull/15766)) + +- fix: avoid unnecessary read version increments ([#15777](https://github.com/sveltejs/svelte/pull/15777)) + ## 5.27.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index af78d2679a..7ad4835a9a 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.27.0", + "version": "5.27.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 27a39136f8..b0424e8232 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.27.0'; +export const VERSION = '5.27.1'; export const PUBLIC_VERSION = '5'; From fc61262b61e43bdb8c1421d84b9fd0f6d4cc4ddc Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 17 Apr 2025 09:52:30 -0400 Subject: [PATCH 0358/1429] chore: delete some unused code (#15783) * tidy up * more * more --- packages/svelte/src/compiler/migrate/index.js | 2 +- packages/svelte/src/compiler/phases/2-analyze/index.js | 2 +- .../compiler/phases/2-analyze/visitors/CallExpression.js | 2 +- .../phases/2-analyze/visitors/ExportNamedDeclaration.js | 4 +--- .../src/compiler/phases/2-analyze/visitors/Identifier.js | 1 - .../phases/2-analyze/visitors/MemberExpression.js | 5 +---- .../compiler/phases/2-analyze/visitors/shared/utils.js | 2 +- .../phases/3-transform/client/visitors/BlockStatement.js | 2 +- .../phases/3-transform/client/visitors/ClassBody.js | 2 -- .../phases/3-transform/client/visitors/IfBlock.js | 2 +- .../3-transform/client/visitors/LabeledStatement.js | 2 -- .../phases/3-transform/client/visitors/RegularElement.js | 2 +- .../phases/3-transform/client/visitors/SvelteElement.js | 2 +- .../phases/3-transform/client/visitors/shared/element.js | 2 +- .../phases/3-transform/server/visitors/EachBlock.js | 2 +- .../phases/3-transform/server/visitors/IfBlock.js | 2 +- .../phases/3-transform/server/visitors/shared/element.js | 8 ++------ .../phases/3-transform/server/visitors/shared/utils.js | 2 +- packages/svelte/src/compiler/phases/scope.js | 6 +++--- packages/svelte/src/constants.js | 1 - packages/svelte/src/internal/client/context.js | 3 +-- packages/svelte/src/internal/client/dev/tracing.js | 2 +- .../svelte/src/internal/client/dom/blocks/css-props.js | 2 +- packages/svelte/src/internal/client/dom/blocks/each.js | 2 +- .../src/internal/client/dom/elements/bindings/media.js | 1 - .../src/internal/client/dom/elements/custom-element.js | 2 +- .../svelte/src/internal/client/dom/elements/events.js | 1 - .../src/internal/client/dom/legacy/event-modifiers.js | 1 - .../svelte/src/internal/client/reactivity/equality.js | 1 + packages/svelte/src/internal/client/reactivity/props.js | 2 +- packages/svelte/src/internal/client/runtime.js | 2 +- packages/svelte/src/internal/server/dev.js | 8 -------- 32 files changed, 27 insertions(+), 53 deletions(-) diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 523389a25a..75a9a64905 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -1,7 +1,7 @@ /** @import { VariableDeclarator, Node, Identifier, AssignmentExpression, LabeledStatement, ExpressionStatement } from 'estree' */ /** @import { Visitors } from 'zimmerframe' */ /** @import { ComponentAnalysis } from '../phases/types.js' */ -/** @import { Scope, ScopeRoot } from '../phases/scope.js' */ +/** @import { Scope } from '../phases/scope.js' */ /** @import { AST, Binding, ValidatedCompileOptions } from '#compiler' */ import MagicString from 'magic-string'; import { walk } from 'zimmerframe'; diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index a6eb9565cb..66e6c6e71f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -5,7 +5,7 @@ import { walk } from 'zimmerframe'; import * as e from '../../errors.js'; import * as w from '../../warnings.js'; -import { extract_identifiers, is_text_attribute } from '../../utils/ast.js'; +import { extract_identifiers } from '../../utils/ast.js'; import * as b from '../../utils/builders.js'; import { Scope, ScopeRoot, create_scopes, get_rune, set_scope } from '../scope.js'; import check_graph_for_cycles from './utils/check_graph_for_cycles.js'; diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index 2eac934b33..e7821dac66 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -3,7 +3,7 @@ /** @import { Context } from '../types' */ import { get_rune } from '../../scope.js'; import * as e from '../../../errors.js'; -import { get_parent, unwrap_optional } from '../../../utils/ast.js'; +import { get_parent } from '../../../utils/ast.js'; import { is_pure, is_safe_identifier } from './shared/utils.js'; import { dev, locate_node, source } from '../../../state.js'; import * as b from '../../../utils/builders.js'; diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js index 547f6ab9c7..4b85894e52 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/ExportNamedDeclaration.js @@ -1,7 +1,5 @@ -/** @import { ExportNamedDeclaration, Identifier, Node } from 'estree' */ -/** @import { Binding } from '#compiler' */ +/** @import { ExportNamedDeclaration, Identifier } from 'estree' */ /** @import { Context } from '../types' */ -/** @import { Scope } from '../../scope' */ import * as e from '../../../errors.js'; import { extract_identifiers } from '../../../utils/ast.js'; diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js index dcbe564543..efbbe6cfa2 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js @@ -1,5 +1,4 @@ /** @import { Expression, Identifier } from 'estree' */ -/** @import { EachBlock } from '#compiler' */ /** @import { Context } from '../types' */ import is_reference from 'is-reference'; import { should_proxy } from '../../3-transform/client/utils.js'; diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js index 6ea8f238e1..245a164c71 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js @@ -1,10 +1,7 @@ -/** @import { MemberExpression, Node } from 'estree' */ +/** @import { MemberExpression } from 'estree' */ /** @import { Context } from '../types' */ import * as e from '../../../errors.js'; -import * as w from '../../../warnings.js'; -import { object } from '../../../utils/ast.js'; import { is_pure, is_safe_identifier } from './shared/utils.js'; -import { mark_subtree_dynamic } from './shared/fragment.js'; /** * @param {MemberExpression} node diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js index d6c74eddb6..2716ec09af 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js @@ -1,4 +1,4 @@ -/** @import { AssignmentExpression, Expression, Identifier, Literal, Node, Pattern, PrivateIdentifier, Super, UpdateExpression, VariableDeclarator } from 'estree' */ +/** @import { AssignmentExpression, Expression, Literal, Node, Pattern, Super, UpdateExpression, VariableDeclarator } from 'estree' */ /** @import { AST, Binding } from '#compiler' */ /** @import { AnalysisState, Context } from '../../types' */ /** @import { Scope } from '../../../scope' */ diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/BlockStatement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/BlockStatement.js index 5bfc8a3ef9..4d2d385702 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/BlockStatement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/BlockStatement.js @@ -1,4 +1,4 @@ -/** @import { ArrowFunctionExpression, BlockStatement, CallExpression, Expression, FunctionDeclaration, FunctionExpression, Statement } from 'estree' */ +/** @import { ArrowFunctionExpression, BlockStatement, Expression, FunctionDeclaration, FunctionExpression, Statement } from 'estree' */ /** @import { ComponentContext } from '../types' */ import { add_state_transformers } from './shared/declarations.js'; import * as b from '../../../../utils/builders.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js index efc3c95c3c..c9d34bd9bc 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js @@ -1,7 +1,5 @@ /** @import { ClassBody, Expression, Identifier, Literal, MethodDefinition, PrivateIdentifier, PropertyDefinition } from 'estree' */ -/** @import { } from '#compiler' */ /** @import { Context, StateField } from '../types' */ -import { dev, is_ignored } from '../../../../state.js'; import * as b from '../../../../utils/builders.js'; import { regex_invalid_identifier_chars } from '../../../patterns.js'; import { get_rune } from '../../../scope.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js index fdd21b2b7e..2a7871a09b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js @@ -1,4 +1,4 @@ -/** @import { BlockStatement, Expression, Identifier } from 'estree' */ +/** @import { BlockStatement, Expression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ import * as b from '../../../../utils/builders.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/LabeledStatement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/LabeledStatement.js index 3c8f57f46b..87f56262a8 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/LabeledStatement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/LabeledStatement.js @@ -1,8 +1,6 @@ -/** @import { Location } from 'locate-character' */ /** @import { Expression, LabeledStatement, Statement } from 'estree' */ /** @import { ReactiveStatement } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import { dev, is_ignored, locator } from '../../../../state.js'; import * as b from '../../../../utils/builders.js'; import { build_getter } from '../utils.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index fa4ee9867f..203dc0cdb1 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -1,4 +1,4 @@ -/** @import { ArrayExpression, Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression, Statement } from 'estree' */ +/** @import { ArrayExpression, Expression, ExpressionStatement, Identifier, MemberExpression, ObjectExpression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { SourceLocation } from '#shared' */ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */ diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js index 115eb6ccc1..35aac662cc 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js @@ -6,7 +6,7 @@ import { is_text_attribute } from '../../../../utils/ast.js'; import * as b from '../../../../utils/builders.js'; import { determine_namespace_for_children } from '../../utils.js'; import { build_attribute_value, build_set_attributes, build_set_class } from './shared/element.js'; -import { build_render_statement, get_expression_id } from './shared/utils.js'; +import { build_render_statement } from './shared/utils.js'; /** * @param {AST.SvelteElement} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index 97cec7a729..6026445a9a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -1,6 +1,6 @@ /** @import { ArrayExpression, Expression, Identifier, ObjectExpression } from 'estree' */ /** @import { AST, ExpressionMetadata } from '#compiler' */ -/** @import { ComponentClientTransformState, ComponentContext } from '../../types' */ +/** @import { ComponentContext } from '../../types' */ import { escape_html } from '../../../../../../escaping.js'; import { normalize_attribute } from '../../../../../../utils.js'; import { is_ignored } from '../../../../../state.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js index 104f1f2405..7c7ff0ea37 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js @@ -1,4 +1,4 @@ -/** @import { BlockStatement, Expression, Pattern, Statement } from 'estree' */ +/** @import { BlockStatement, Expression, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ import { BLOCK_OPEN_ELSE } from '../../../../../internal/server/hydration.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js index cbdd2cd8cc..0fbc3e9fcb 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js @@ -1,4 +1,4 @@ -/** @import { BlockStatement, Expression, IfStatement } from 'estree' */ +/** @import { BlockStatement, Expression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ import { BLOCK_OPEN_ELSE } from '../../../../../internal/server/hydration.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js index 4a5becfb2f..78c8342864 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js @@ -1,11 +1,7 @@ /** @import { ArrayExpression, Expression, Literal, ObjectExpression } from 'estree' */ -/** @import { AST, Namespace } from '#compiler' */ +/** @import { AST } from '#compiler' */ /** @import { ComponentContext, ComponentServerTransformState } from '../../types.js' */ -import { - get_attribute_chunks, - is_event_attribute, - is_text_attribute -} from '../../../../../utils/ast.js'; +import { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js'; import { binding_properties } from '../../../../bindings.js'; import { create_attribute, diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js index 807e12a8fa..0353b55357 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js @@ -1,4 +1,4 @@ -/** @import { AssignmentOperator, Expression, Identifier, Node, Statement, TemplateElement } from 'estree' */ +/** @import { AssignmentOperator, Expression, Identifier, Node, Statement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext, ServerTransformState } from '../../types.js' */ diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 73dfeea1d9..51b38a6454 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -16,10 +16,10 @@ import { is_reserved, is_rune } from '../../utils.js'; import { determine_slot } from '../utils/slot.js'; import { validate_identifier_name } from './2-analyze/visitors/shared/utils.js'; -export const UNKNOWN = Symbol('unknown'); +const UNKNOWN = Symbol('unknown'); /** Includes `BigInt` */ -export const NUMBER = Symbol('number'); -export const STRING = Symbol('string'); +const NUMBER = Symbol('number'); +const STRING = Symbol('string'); export class Binding { /** @type {Scope} */ diff --git a/packages/svelte/src/constants.js b/packages/svelte/src/constants.js index 6ea407d448..8861e440fc 100644 --- a/packages/svelte/src/constants.js +++ b/packages/svelte/src/constants.js @@ -22,7 +22,6 @@ export const HYDRATION_START = '['; /** used to indicate that an `{:else}...` block was rendered */ export const HYDRATION_START_ELSE = '[!'; export const HYDRATION_END = ']'; -export const HYDRATION_AWAIT_THEN = '!'; export const HYDRATION_ERROR = {}; export const ELEMENT_IS_NAMESPACED = 1; diff --git a/packages/svelte/src/internal/client/context.js b/packages/svelte/src/internal/client/context.js index 7a2fdd0edb..7c7213b7a2 100644 --- a/packages/svelte/src/internal/client/context.js +++ b/packages/svelte/src/internal/client/context.js @@ -7,8 +7,7 @@ import { active_effect, active_reaction, set_active_effect, - set_active_reaction, - untrack + set_active_reaction } from './runtime.js'; import { effect, teardown } from './reactivity/effects.js'; import { legacy_mode_flag } from '../flags/index.js'; diff --git a/packages/svelte/src/internal/client/dev/tracing.js b/packages/svelte/src/internal/client/dev/tracing.js index 3881ef3442..0f2ce747d8 100644 --- a/packages/svelte/src/internal/client/dev/tracing.js +++ b/packages/svelte/src/internal/client/dev/tracing.js @@ -1,4 +1,4 @@ -/** @import { Derived, Reaction, Signal, Value } from '#client' */ +/** @import { Derived, Reaction, Value } from '#client' */ import { UNINITIALIZED } from '../../../constants.js'; import { snapshot } from '../../shared/clone.js'; import { define_property } from '../../shared/utils.js'; diff --git a/packages/svelte/src/internal/client/dom/blocks/css-props.js b/packages/svelte/src/internal/client/dom/blocks/css-props.js index 473d35b122..ecbcfd3e83 100644 --- a/packages/svelte/src/internal/client/dom/blocks/css-props.js +++ b/packages/svelte/src/internal/client/dom/blocks/css-props.js @@ -1,6 +1,6 @@ /** @import { TemplateNode } from '#client' */ import { render_effect, teardown } from '../../reactivity/effects.js'; -import { hydrate_node, hydrating, set_hydrate_node } from '../hydration.js'; +import { hydrating, set_hydrate_node } from '../hydration.js'; import { get_first_child } from '../operations.js'; /** diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 3baa03a917..228832fcf5 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -35,7 +35,7 @@ import { source, mutable_source, internal_set } from '../../reactivity/sources.j import { array_from, is_array } from '../../../shared/utils.js'; import { INERT } from '../../constants.js'; import { queue_micro_task } from '../task.js'; -import { active_effect, active_reaction, get } from '../../runtime.js'; +import { active_effect, get } from '../../runtime.js'; import { DEV } from 'esm-env'; import { derived_safe_equal } from '../../reactivity/deriveds.js'; diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/media.js b/packages/svelte/src/internal/client/dom/elements/bindings/media.js index 4893426d55..30a8dac1af 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/media.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/media.js @@ -1,4 +1,3 @@ -import { hydrating } from '../../hydration.js'; import { render_effect, effect, teardown } from '../../../reactivity/effects.js'; import { listen } from './shared.js'; diff --git a/packages/svelte/src/internal/client/dom/elements/custom-element.js b/packages/svelte/src/internal/client/dom/elements/custom-element.js index 6195b2c561..2d118bfab3 100644 --- a/packages/svelte/src/internal/client/dom/elements/custom-element.js +++ b/packages/svelte/src/internal/client/dom/elements/custom-element.js @@ -1,5 +1,5 @@ import { createClassComponent } from '../../../../legacy/legacy-client.js'; -import { destroy_effect, effect_root, render_effect } from '../../reactivity/effects.js'; +import { effect_root, render_effect } from '../../reactivity/effects.js'; import { append } from '../template.js'; import { define_property, get_descriptor, object_keys } from '../../../shared/utils.js'; diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js index 0c1bb1dada..3374fe713f 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -1,4 +1,3 @@ -/** @import { Location } from 'locate-character' */ import { teardown } from '../../reactivity/effects.js'; import { define_property, is_array } from '../../../shared/utils.js'; import { hydrating } from '../hydration.js'; diff --git a/packages/svelte/src/internal/client/dom/legacy/event-modifiers.js b/packages/svelte/src/internal/client/dom/legacy/event-modifiers.js index 918832dfa5..2e5312f1b0 100644 --- a/packages/svelte/src/internal/client/dom/legacy/event-modifiers.js +++ b/packages/svelte/src/internal/client/dom/legacy/event-modifiers.js @@ -1,4 +1,3 @@ -/** @import { ActionReturn } from 'svelte/action' */ import { noop } from '../../../shared/utils.js'; import { user_pre_effect } from '../../reactivity/effects.js'; import { on } from '../elements/events.js'; diff --git a/packages/svelte/src/internal/client/reactivity/equality.js b/packages/svelte/src/internal/client/reactivity/equality.js index 37a9994ab8..1041238573 100644 --- a/packages/svelte/src/internal/client/reactivity/equality.js +++ b/packages/svelte/src/internal/client/reactivity/equality.js @@ -1,4 +1,5 @@ /** @import { Equals } from '#client' */ + /** @type {Equals} */ export function equals(value) { return value === this.v; diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index 341d7c768a..bd85b14df0 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -7,7 +7,7 @@ import { PROPS_IS_RUNES, PROPS_IS_UPDATED } from '../../../constants.js'; -import { define_property, get_descriptor, is_function } from '../../shared/utils.js'; +import { get_descriptor, is_function } from '../../shared/utils.js'; import { mutable_source, set, source, update } from './sources.js'; import { derived, derived_safe_equal } from './deriveds.js'; import { get, captured_signals, untrack } from '../runtime.js'; diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index e621536055..7595aa4a19 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -27,7 +27,7 @@ import { } from './constants.js'; import { flush_tasks } from './dom/task.js'; import { internal_set, old_values } from './reactivity/sources.js'; -import { destroy_derived_effects, execute_derived, update_derived } from './reactivity/deriveds.js'; +import { destroy_derived_effects, update_derived } from './reactivity/deriveds.js'; import * as e from './errors.js'; import { FILENAME } from '../../constants.js'; import { tracing_mode_flag } from '../flags/index.js'; diff --git a/packages/svelte/src/internal/server/dev.js b/packages/svelte/src/internal/server/dev.js index 157f22f929..efc761d7c5 100644 --- a/packages/svelte/src/internal/server/dev.js +++ b/packages/svelte/src/internal/server/dev.js @@ -26,14 +26,6 @@ let parent = null; /** @type {Set} */ let seen; -/** - * @param {Element} element - */ -function stringify(element) { - if (element.filename === null) return `\`<${element.tag}>\``; - return `\`<${element.tag}>\` (${element.filename}:${element.line}:${element.column})`; -} - /** * @param {Payload} payload * @param {string} message From 262b281c9182ebc3012c252afd8d86d9a425b8be Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 17 Apr 2025 10:02:42 -0400 Subject: [PATCH 0359/1429] docs: add inline documentation for svelte/reactivity (#15722) * docs: add inline documentation for svelte/reactivity * map * set * more * dedupe * tweak * typo * Apply suggestions from code review Co-authored-by: bytecodemanipulator --------- Co-authored-by: bytecodemanipulator --- .../docs/98-reference/21-svelte-reactivity.md | 20 +-- packages/svelte/src/reactivity/date.js | 32 ++++ packages/svelte/src/reactivity/map.js | 41 +++++ packages/svelte/src/reactivity/set.js | 31 ++++ .../src/reactivity/url-search-params.js | 27 +++ packages/svelte/src/reactivity/url.js | 27 +++ packages/svelte/types/index.d.ts | 163 ++++++++++++++++++ 7 files changed, 322 insertions(+), 19 deletions(-) diff --git a/documentation/docs/98-reference/21-svelte-reactivity.md b/documentation/docs/98-reference/21-svelte-reactivity.md index 6857c1dba8..8070331f48 100644 --- a/documentation/docs/98-reference/21-svelte-reactivity.md +++ b/documentation/docs/98-reference/21-svelte-reactivity.md @@ -2,24 +2,6 @@ title: svelte/reactivity --- -Svelte provides reactive versions of various built-ins like `SvelteMap`, `SvelteSet` and `SvelteURL`. These can be imported from `svelte/reactivity` and used just like their native counterparts. - -```svelte - - - - - - - -
- - - -``` +Svelte provides reactive versions of various built-ins like [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map), [`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) and [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) that can be used just like their native counterparts, as well as a handful of additional utilities for handling reactivity. > MODULE: svelte/reactivity diff --git a/packages/svelte/src/reactivity/date.js b/packages/svelte/src/reactivity/date.js index 33da2e1761..721673bc36 100644 --- a/packages/svelte/src/reactivity/date.js +++ b/packages/svelte/src/reactivity/date.js @@ -5,6 +5,38 @@ import { active_reaction, get, set_active_reaction } from '../internal/client/ru var inited = false; +/** + * A reactive version of the built-in [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object. + * Reading the date (whether with methods like `date.getTime()` or `date.toString()`, or via things like [`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat)) + * in an [effect](https://svelte.dev/docs/svelte/$effect) or [derived](https://svelte.dev/docs/svelte/$derived) + * will cause it to be re-evaluated when the value of the date changes. + * + * ```svelte + * + * + *

The time is {formatter.format(date)}

+ * ``` + */ export class SvelteDate extends Date { #time = source(super.getTime()); diff --git a/packages/svelte/src/reactivity/map.js b/packages/svelte/src/reactivity/map.js index 3fa2945ef0..3ae8fe5ad1 100644 --- a/packages/svelte/src/reactivity/map.js +++ b/packages/svelte/src/reactivity/map.js @@ -5,6 +5,47 @@ import { get } from '../internal/client/runtime.js'; import { increment } from './utils.js'; /** + * A reactive version of the built-in [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object. + * Reading contents of the map (by iterating, or by reading `map.size` or calling `map.get(...)` or `map.has(...)` as in the [tic-tac-toe example](https://svelte.dev/playground/0b0ff4aa49c9443f9b47fe5203c78293) below) in an [effect](https://svelte.dev/docs/svelte/$effect) or [derived](https://svelte.dev/docs/svelte/$derived) + * will cause it to be re-evaluated as necessary when the map is updated. + * + * Note that values in a reactive map are _not_ made [deeply reactive](https://svelte.dev/docs/svelte/$state#Deep-state). + * + * ```svelte + * + * + *
+ * {#each Array(9), i} + * + * {/each} + *
+ * + * {#if winner} + *

{winner} wins!

+ * + * {:else} + *

{player} is next

+ * {/if} + * ``` + * * @template K * @template V * @extends {Map} diff --git a/packages/svelte/src/reactivity/set.js b/packages/svelte/src/reactivity/set.js index be0c2d2cf5..4a0b4dfdb3 100644 --- a/packages/svelte/src/reactivity/set.js +++ b/packages/svelte/src/reactivity/set.js @@ -10,6 +10,37 @@ var set_like_methods = ['difference', 'intersection', 'symmetricDifference', 'un var inited = false; /** + * A reactive version of the built-in [`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) object. + * Reading contents of the set (by iterating, or by reading `set.size` or calling `set.has(...)` as in the [example](https://svelte.dev/playground/53438b51194b4882bcc18cddf9f96f15) below) in an [effect](https://svelte.dev/docs/svelte/$effect) or [derived](https://svelte.dev/docs/svelte/$derived) + * will cause it to be re-evaluated as necessary when the set is updated. + * + * Note that values in a reactive set are _not_ made [deeply reactive](https://svelte.dev/docs/svelte/$state#Deep-state). + * + * ```svelte + * + * + * {#each ['🙈', '🙉', '🙊'] as monkey} + * + * {/each} + * + * + * + * {#if monkeys.has('🙈')}

see no evil

{/if} + * {#if monkeys.has('🙉')}

hear no evil

{/if} + * {#if monkeys.has('🙊')}

speak no evil

{/if} + * ``` + * * @template T * @extends {Set} */ diff --git a/packages/svelte/src/reactivity/url-search-params.js b/packages/svelte/src/reactivity/url-search-params.js index 13f6971996..c1a8275f15 100644 --- a/packages/svelte/src/reactivity/url-search-params.js +++ b/packages/svelte/src/reactivity/url-search-params.js @@ -5,6 +5,32 @@ import { increment } from './utils.js'; export const REPLACE = Symbol(); +/** + * A reactive version of the built-in [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) object. + * Reading its contents (by iterating, or by calling `params.get(...)` or `params.getAll(...)` as in the [example](https://svelte.dev/playground/b3926c86c5384bab9f2cf993bc08c1c8) below) in an [effect](https://svelte.dev/docs/svelte/$effect) or [derived](https://svelte.dev/docs/svelte/$derived) + * will cause it to be re-evaluated as necessary when the params are updated. + * + * ```svelte + * + * + * + * + * + * + *

?{params.toString()}

+ * + * {#each params as [key, value]} + *

{key}: {value}

+ * {/each} + * ``` + */ export class SvelteURLSearchParams extends URLSearchParams { #version = source(0); #url = get_current_url(); @@ -23,6 +49,7 @@ export class SvelteURLSearchParams extends URLSearchParams { /** * @param {URLSearchParams} params + * @internal */ [REPLACE](params) { if (this.#updating) return; diff --git a/packages/svelte/src/reactivity/url.js b/packages/svelte/src/reactivity/url.js index 5d003be021..879006f057 100644 --- a/packages/svelte/src/reactivity/url.js +++ b/packages/svelte/src/reactivity/url.js @@ -10,6 +10,33 @@ export function get_current_url() { return current_url; } +/** + * A reactive version of the built-in [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) object. + * Reading properties of the URL (such as `url.href` or `url.pathname`) in an [effect](https://svelte.dev/docs/svelte/$effect) or [derived](https://svelte.dev/docs/svelte/$derived) + * will cause it to be re-evaluated as necessary when the URL changes. + * + * The `searchParams` property is an instance of [SvelteURLSearchParams](https://svelte.dev/docs/svelte/svelte-reactivity#SvelteURLSearchParams). + * + * [Example](https://svelte.dev/playground/5a694758901b448c83dc40dc31c71f2a): + * + * ```svelte + * + * + * + * + * + * + * + *
+ * + * + * + * ``` + */ export class SvelteURL extends URL { #protocol = source(super.protocol); #username = source(super.username); diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 8fc174b0a9..b233cfcc0b 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1900,11 +1900,77 @@ declare module 'svelte/motion' { } declare module 'svelte/reactivity' { + /** + * A reactive version of the built-in [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object. + * Reading the date (whether with methods like `date.getTime()` or `date.toString()`, or via things like [`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat)) + * in an [effect](https://svelte.dev/docs/svelte/$effect) or [derived](https://svelte.dev/docs/svelte/$derived) + * will cause it to be re-evaluated when the value of the date changes. + * + * ```svelte + * + * + *

The time is {formatter.format(date)}

+ * ``` + */ export class SvelteDate extends Date { constructor(...params: any[]); #private; } + /** + * A reactive version of the built-in [`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) object. + * Reading contents of the set (by iterating, or by reading `set.size` or calling `set.has(...)` as in the [example](https://svelte.dev/playground/53438b51194b4882bcc18cddf9f96f15) below) in an [effect](https://svelte.dev/docs/svelte/$effect) or [derived](https://svelte.dev/docs/svelte/$derived) + * will cause it to be re-evaluated as necessary when the set is updated. + * + * Note that values in a reactive set are _not_ made [deeply reactive](https://svelte.dev/docs/svelte/$state#Deep-state). + * + * ```svelte + * + * + * {#each ['🙈', '🙉', '🙊'] as monkey} + * + * {/each} + * + * + * + * {#if monkeys.has('🙈')}

see no evil

{/if} + * {#if monkeys.has('🙉')}

hear no evil

{/if} + * {#if monkeys.has('🙊')}

speak no evil

{/if} + * ``` + * + * + */ export class SvelteSet extends Set { constructor(value?: Iterable | null | undefined); @@ -1912,6 +1978,50 @@ declare module 'svelte/reactivity' { add(value: T): this; #private; } + /** + * A reactive version of the built-in [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) object. + * Reading contents of the map (by iterating, or by reading `map.size` or calling `map.get(...)` or `map.has(...)` as in the [tic-tac-toe example](https://svelte.dev/playground/0b0ff4aa49c9443f9b47fe5203c78293) below) in an [effect](https://svelte.dev/docs/svelte/$effect) or [derived](https://svelte.dev/docs/svelte/$derived) + * will cause it to be re-evaluated as necessary when the map is updated. + * + * Note that values in a reactive map are _not_ made [deeply reactive](https://svelte.dev/docs/svelte/$state#Deep-state). + * + * ```svelte + * + * + *
+ * {#each Array(9), i} + * + * {/each} + *
+ * + * {#if winner} + *

{winner} wins!

+ * + * {:else} + *

{player} is next

+ * {/if} + * ``` + * + * + */ export class SvelteMap extends Map { constructor(value?: Iterable | null | undefined); @@ -1919,11 +2029,64 @@ declare module 'svelte/reactivity' { set(key: K, value: V): this; #private; } + /** + * A reactive version of the built-in [`URL`](https://developer.mozilla.org/en-US/docs/Web/API/URL) object. + * Reading properties of the URL (such as `url.href` or `url.pathname`) in an [effect](https://svelte.dev/docs/svelte/$effect) or [derived](https://svelte.dev/docs/svelte/$derived) + * will cause it to be re-evaluated as necessary when the URL changes. + * + * The `searchParams` property is an instance of [SvelteURLSearchParams](https://svelte.dev/docs/svelte/svelte-reactivity#SvelteURLSearchParams). + * + * [Example](https://svelte.dev/playground/5a694758901b448c83dc40dc31c71f2a): + * + * ```svelte + * + * + * + * + * + * + * + *
+ * + * + * + * ``` + */ export class SvelteURL extends URL { get searchParams(): SvelteURLSearchParams; #private; } const REPLACE: unique symbol; + /** + * A reactive version of the built-in [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) object. + * Reading its contents (by iterating, or by calling `params.get(...)` or `params.getAll(...)` as in the [example](https://svelte.dev/playground/b3926c86c5384bab9f2cf993bc08c1c8) below) in an [effect](https://svelte.dev/docs/svelte/$effect) or [derived](https://svelte.dev/docs/svelte/$derived) + * will cause it to be re-evaluated as necessary when the params are updated. + * + * ```svelte + * + * + * + * + * + * + *

?{params.toString()}

+ * + * {#each params as [key, value]} + *

{key}: {value}

+ * {/each} + * ``` + */ export class SvelteURLSearchParams extends URLSearchParams { [REPLACE](params: URLSearchParams): void; From 4f8708a8241df37f2be93384eabc8873d1cfb048 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 17 Apr 2025 14:19:35 -0400 Subject: [PATCH 0360/1429] chore: use pkg.imports for builder.js (#15785) * chore: use pkg.imports for builder.js * use pkg.imports instead of tsconfig * #client/constants --- packages/svelte/package.json | 11 +++++++++++ packages/svelte/scripts/generate-types.js | 7 ++++++- .../phases/1-parse/remove_typescript_nodes.js | 2 +- .../svelte/src/compiler/phases/2-analyze/index.js | 2 +- .../phases/2-analyze/visitors/CallExpression.js | 2 +- .../phases/2-analyze/visitors/shared/utils.js | 2 +- .../phases/3-transform/client/transform-client.js | 2 +- .../src/compiler/phases/3-transform/client/utils.js | 2 +- .../3-transform/client/visitors/AnimateDirective.js | 2 +- .../client/visitors/AssignmentExpression.js | 2 +- .../phases/3-transform/client/visitors/AwaitBlock.js | 2 +- .../3-transform/client/visitors/BinaryExpression.js | 2 +- .../3-transform/client/visitors/BindDirective.js | 2 +- .../3-transform/client/visitors/BlockStatement.js | 2 +- .../3-transform/client/visitors/BreakStatement.js | 2 +- .../3-transform/client/visitors/CallExpression.js | 2 +- .../phases/3-transform/client/visitors/ClassBody.js | 2 +- .../phases/3-transform/client/visitors/Component.js | 2 +- .../phases/3-transform/client/visitors/ConstTag.js | 2 +- .../phases/3-transform/client/visitors/DebugTag.js | 2 +- .../phases/3-transform/client/visitors/EachBlock.js | 2 +- .../client/visitors/ExportNamedDeclaration.js | 2 +- .../client/visitors/ExpressionStatement.js | 2 +- .../phases/3-transform/client/visitors/Fragment.js | 2 +- .../client/visitors/FunctionDeclaration.js | 2 +- .../phases/3-transform/client/visitors/HtmlTag.js | 2 +- .../phases/3-transform/client/visitors/Identifier.js | 2 +- .../phases/3-transform/client/visitors/IfBlock.js | 2 +- .../3-transform/client/visitors/ImportDeclaration.js | 2 +- .../phases/3-transform/client/visitors/KeyBlock.js | 2 +- .../3-transform/client/visitors/LabeledStatement.js | 2 +- .../3-transform/client/visitors/LetDirective.js | 2 +- .../3-transform/client/visitors/MemberExpression.js | 2 +- .../phases/3-transform/client/visitors/OnDirective.js | 2 +- .../phases/3-transform/client/visitors/Program.js | 2 +- .../3-transform/client/visitors/RegularElement.js | 2 +- .../phases/3-transform/client/visitors/RenderTag.js | 2 +- .../phases/3-transform/client/visitors/SlotElement.js | 2 +- .../3-transform/client/visitors/SnippetBlock.js | 2 +- .../3-transform/client/visitors/SvelteBoundary.js | 2 +- .../3-transform/client/visitors/SvelteElement.js | 2 +- .../phases/3-transform/client/visitors/SvelteHead.js | 2 +- .../3-transform/client/visitors/TitleElement.js | 2 +- .../client/visitors/TransitionDirective.js | 2 +- .../3-transform/client/visitors/UpdateExpression.js | 2 +- .../3-transform/client/visitors/UseDirective.js | 2 +- .../client/visitors/VariableDeclaration.js | 2 +- .../3-transform/client/visitors/shared/component.js | 2 +- .../client/visitors/shared/declarations.js | 2 +- .../3-transform/client/visitors/shared/element.js | 2 +- .../3-transform/client/visitors/shared/events.js | 2 +- .../3-transform/client/visitors/shared/fragment.js | 2 +- .../client/visitors/shared/special_element.js | 2 +- .../3-transform/client/visitors/shared/utils.js | 2 +- .../phases/3-transform/server/transform-server.js | 2 +- .../server/visitors/AssignmentExpression.js | 2 +- .../phases/3-transform/server/visitors/AwaitBlock.js | 2 +- .../3-transform/server/visitors/CallExpression.js | 2 +- .../phases/3-transform/server/visitors/ClassBody.js | 2 +- .../phases/3-transform/server/visitors/Component.js | 2 +- .../phases/3-transform/server/visitors/ConstTag.js | 2 +- .../phases/3-transform/server/visitors/DebugTag.js | 2 +- .../phases/3-transform/server/visitors/EachBlock.js | 2 +- .../server/visitors/ExpressionStatement.js | 2 +- .../phases/3-transform/server/visitors/Fragment.js | 2 +- .../phases/3-transform/server/visitors/HtmlTag.js | 2 +- .../phases/3-transform/server/visitors/Identifier.js | 2 +- .../phases/3-transform/server/visitors/IfBlock.js | 2 +- .../3-transform/server/visitors/LabeledStatement.js | 2 +- .../3-transform/server/visitors/MemberExpression.js | 2 +- .../3-transform/server/visitors/PropertyDefinition.js | 2 +- .../3-transform/server/visitors/RegularElement.js | 2 +- .../phases/3-transform/server/visitors/RenderTag.js | 2 +- .../phases/3-transform/server/visitors/SlotElement.js | 2 +- .../3-transform/server/visitors/SnippetBlock.js | 2 +- .../3-transform/server/visitors/SvelteBoundary.js | 2 +- .../3-transform/server/visitors/SvelteElement.js | 2 +- .../phases/3-transform/server/visitors/SvelteHead.js | 2 +- .../phases/3-transform/server/visitors/SvelteSelf.js | 2 +- .../3-transform/server/visitors/TitleElement.js | 2 +- .../3-transform/server/visitors/UpdateExpression.js | 2 +- .../server/visitors/VariableDeclaration.js | 2 +- .../3-transform/server/visitors/shared/component.js | 2 +- .../3-transform/server/visitors/shared/element.js | 2 +- .../3-transform/server/visitors/shared/utils.js | 2 +- .../compiler/phases/3-transform/shared/assignments.js | 2 +- .../svelte/src/compiler/phases/3-transform/utils.js | 2 +- packages/svelte/src/compiler/phases/scope.js | 2 +- packages/svelte/src/compiler/private.d.ts | 2 ++ packages/svelte/src/compiler/utils/ast.js | 2 +- .../svelte/src/internal/client/dev/console-log.js | 2 +- packages/svelte/src/internal/client/dev/debug.js | 2 +- packages/svelte/src/internal/client/dev/hmr.js | 2 +- packages/svelte/src/internal/client/dev/ownership.js | 2 +- packages/svelte/src/internal/client/dev/tracing.js | 2 +- .../svelte/src/internal/client/dom/blocks/boundary.js | 2 +- .../svelte/src/internal/client/dom/blocks/each.js | 2 +- packages/svelte/src/internal/client/dom/blocks/if.js | 2 +- .../svelte/src/internal/client/dom/blocks/snippet.js | 2 +- .../internal/client/dom/blocks/svelte-component.js | 2 +- .../src/internal/client/dom/blocks/svelte-element.js | 2 +- .../src/internal/client/dom/blocks/svelte-head.js | 2 +- .../src/internal/client/dom/elements/attributes.js | 2 +- .../src/internal/client/dom/elements/bindings/this.js | 2 +- .../src/internal/client/dom/elements/transitions.js | 2 +- packages/svelte/src/internal/client/proxy.js | 2 +- .../svelte/src/internal/client/reactivity/deriveds.js | 2 +- .../svelte/src/internal/client/reactivity/effects.js | 2 +- .../svelte/src/internal/client/reactivity/props.js | 2 +- .../svelte/src/internal/client/reactivity/sources.js | 2 +- packages/svelte/tsconfig.json | 6 +----- 111 files changed, 127 insertions(+), 113 deletions(-) create mode 100644 packages/svelte/src/compiler/private.d.ts diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 7ad4835a9a..e03c9ee083 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -103,6 +103,17 @@ "default": "./src/events/index.js" } }, + "imports": { + "#client": "./src/internal/client/types.d.ts", + "#client/constants": "./src/internal/client/constants.js", + "#compiler": { + "types": "./src/compiler/private.d.ts", + "default": "./src/compiler/index.js" + }, + "#compiler/builders": "./src/compiler/utils/builders.js", + "#server": "./src/internal/server/types.d.ts", + "#shared": "./src/internal/shared/types.d.ts" + }, "repository": { "type": "git", "url": "git+https://github.com/sveltejs/svelte.git", diff --git a/packages/svelte/scripts/generate-types.js b/packages/svelte/scripts/generate-types.js index d44afe8205..377fca4343 100644 --- a/packages/svelte/scripts/generate-types.js +++ b/packages/svelte/scripts/generate-types.js @@ -24,7 +24,12 @@ await createBundle({ output: `${dir}/types/index.d.ts`, compilerOptions: { // so that types/properties with `@internal` (and its dependencies) are removed from the output - stripInternal: true + stripInternal: true, + paths: Object.fromEntries( + Object.entries(pkg.imports).map(([key, value]) => { + return [key, [value.types ?? value.default ?? value]]; + }) + ) }, modules: { [pkg.name]: `${dir}/src/index.d.ts`, diff --git a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js index 4ff6a782b4..aba94ee20d 100644 --- a/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js +++ b/packages/svelte/src/compiler/phases/1-parse/remove_typescript_nodes.js @@ -1,7 +1,7 @@ /** @import { Context, Visitors } from 'zimmerframe' */ /** @import { FunctionExpression, FunctionDeclaration } from 'estree' */ import { walk } from 'zimmerframe'; -import * as b from '../../utils/builders.js'; +import * as b from '#compiler/builders'; import * as e from '../../errors.js'; /** diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 66e6c6e71f..2e36a89649 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -6,7 +6,7 @@ import { walk } from 'zimmerframe'; import * as e from '../../errors.js'; import * as w from '../../warnings.js'; import { extract_identifiers } from '../../utils/ast.js'; -import * as b from '../../utils/builders.js'; +import * as b from '#compiler/builders'; import { Scope, ScopeRoot, create_scopes, get_rune, set_scope } from '../scope.js'; import check_graph_for_cycles from './utils/check_graph_for_cycles.js'; import { create_attribute, is_custom_element_node } from '../nodes.js'; diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js index e7821dac66..904817b014 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js @@ -6,7 +6,7 @@ import * as e from '../../../errors.js'; import { get_parent } from '../../../utils/ast.js'; import { is_pure, is_safe_identifier } from './shared/utils.js'; import { dev, locate_node, source } from '../../../state.js'; -import * as b from '../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {CallExpression} node diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js index 2716ec09af..12e21c386c 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js @@ -6,7 +6,7 @@ import * as e from '../../../../errors.js'; import { extract_identifiers } from '../../../../utils/ast.js'; import * as w from '../../../../warnings.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { get_rune } from '../../../scope.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index 098b3ecae8..f0da5a4918 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -3,7 +3,7 @@ /** @import { ComponentAnalysis, Analysis } from '../../types' */ /** @import { Visitors, ComponentClientTransformState, ClientTransformState } from './types' */ import { walk } from 'zimmerframe'; -import * as b from '../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_getter, is_state_source } from './utils.js'; import { render_stylesheet } from '../css/index.js'; import { dev, filename } from '../../../state.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js index a37ecd31cc..6d9dac8a33 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js @@ -3,7 +3,7 @@ /** @import { ClientTransformState, ComponentClientTransformState, ComponentContext } from './types.js' */ /** @import { Analysis } from '../../types.js' */ /** @import { Scope } from '../../scope.js' */ -import * as b from '../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { is_simple_expression } from '../../../utils/ast.js'; import { PROPS_IS_LAZY_INITIAL, diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AnimateDirective.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AnimateDirective.js index 2e051ec674..16f9735370 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AnimateDirective.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AnimateDirective.js @@ -1,7 +1,7 @@ /** @import { Expression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { parse_directive_name } from './shared/utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js index 4baa1c8e6c..e3f9450050 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AssignmentExpression.js @@ -1,7 +1,7 @@ /** @import { AssignmentExpression, AssignmentOperator, Expression, Identifier, Pattern } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { Context } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_assignment_value, get_attribute_expression, diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js index 7588b24280..96a4addb72 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AwaitBlock.js @@ -2,7 +2,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */ import { extract_identifiers } from '../../../../utils/ast.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { create_derived } from '../utils.js'; import { get_value } from './shared/declarations.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/BinaryExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/BinaryExpression.js index c563920855..18028fa071 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/BinaryExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/BinaryExpression.js @@ -1,7 +1,7 @@ /** @import { Expression, BinaryExpression } from 'estree' */ /** @import { ComponentContext } from '../types' */ import { dev } from '../../../../state.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {BinaryExpression} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/BindDirective.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/BindDirective.js index 0a49e89cfe..506fd4aafd 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/BindDirective.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/BindDirective.js @@ -3,7 +3,7 @@ /** @import { ComponentContext } from '../types' */ import { dev, is_ignored } from '../../../../state.js'; import { is_text_attribute } from '../../../../utils/ast.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { binding_properties } from '../../../bindings.js'; import { build_attribute_value } from './shared/element.js'; import { build_bind_this, validate_binding } from './shared/utils.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/BlockStatement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/BlockStatement.js index 4d2d385702..d1c0978a81 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/BlockStatement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/BlockStatement.js @@ -1,7 +1,7 @@ /** @import { ArrowFunctionExpression, BlockStatement, Expression, FunctionDeclaration, FunctionExpression, Statement } from 'estree' */ /** @import { ComponentContext } from '../types' */ import { add_state_transformers } from './shared/declarations.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {BlockStatement} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/BreakStatement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/BreakStatement.js index 66b66c64f2..daa54018c0 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/BreakStatement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/BreakStatement.js @@ -1,6 +1,6 @@ /** @import { BreakStatement } from 'estree' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {BreakStatement} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js index fda43ad791..b110f8eae8 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/CallExpression.js @@ -1,7 +1,7 @@ /** @import { CallExpression, Expression } from 'estree' */ /** @import { Context } from '../types' */ import { dev, is_ignored } from '../../../../state.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { get_rune } from '../../../scope.js'; import { transform_inspect_rune } from '../../utils.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js index c9d34bd9bc..f3ebd94069 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ClassBody.js @@ -1,6 +1,6 @@ /** @import { ClassBody, Expression, Identifier, Literal, MethodDefinition, PrivateIdentifier, PropertyDefinition } from 'estree' */ /** @import { Context, StateField } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { regex_invalid_identifier_chars } from '../../../patterns.js'; import { get_rune } from '../../../scope.js'; import { should_proxy } from '../utils.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Component.js index a10a3da6e3..783bc38e3c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Component.js @@ -1,7 +1,7 @@ /** @import { Expression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_component } from './shared/component.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js index 7e33aea435..2f3c0b3d0e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ConstTag.js @@ -3,7 +3,7 @@ /** @import { ComponentContext } from '../types' */ import { dev } from '../../../../state.js'; import { extract_identifiers } from '../../../../utils/ast.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { create_derived } from '../utils.js'; import { get_value } from './shared/declarations.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/DebugTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/DebugTag.js index d2697fd039..ef9a070859 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/DebugTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/DebugTag.js @@ -1,7 +1,7 @@ /** @import { Expression} from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {AST.DebugTag} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js index 629cacda01..e5aee24765 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/EachBlock.js @@ -11,7 +11,7 @@ import { } from '../../../../../constants.js'; import { dev } from '../../../../state.js'; import { extract_paths, object } from '../../../../utils/ast.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_getter } from '../utils.js'; import { get_value } from './shared/declarations.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ExportNamedDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ExportNamedDeclaration.js index cab7f90c3d..16e400d50c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ExportNamedDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ExportNamedDeclaration.js @@ -1,6 +1,6 @@ /** @import { ExportNamedDeclaration } from 'estree' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {ExportNamedDeclaration} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ExpressionStatement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ExpressionStatement.js index 0424e595be..859842ebc3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ExpressionStatement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ExpressionStatement.js @@ -1,6 +1,6 @@ /** @import { Expression, ExpressionStatement } from 'estree' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { get_rune } from '../../../scope.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js index 389a694741..b6dca0779a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Fragment.js @@ -4,7 +4,7 @@ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */ import { TEMPLATE_FRAGMENT, TEMPLATE_USE_IMPORT_NODE } from '../../../../../constants.js'; import { dev } from '../../../../state.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { sanitize_template_string } from '../../../../utils/sanitize_template_string.js'; import { clean_nodes, infer_namespace } from '../../utils.js'; import { process_children } from './shared/fragment.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/FunctionDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/FunctionDeclaration.js index ed8fefc6ba..5dc8fa5cf9 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/FunctionDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/FunctionDeclaration.js @@ -1,7 +1,7 @@ /** @import { FunctionDeclaration } from 'estree' */ /** @import { ComponentContext } from '../types' */ import { build_hoisted_params } from '../utils.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {FunctionDeclaration} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/HtmlTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/HtmlTag.js index ace73691d1..a69b9cfe70 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/HtmlTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/HtmlTag.js @@ -2,7 +2,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ import { is_ignored } from '../../../../state.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {AST.HtmlTag} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Identifier.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Identifier.js index ae62909eff..b01ed01bd7 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Identifier.js @@ -1,7 +1,7 @@ /** @import { Identifier, Node } from 'estree' */ /** @import { Context } from '../types' */ import is_reference from 'is-reference'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_getter } from '../utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js index 2a7871a09b..c650a1e15c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/IfBlock.js @@ -1,7 +1,7 @@ /** @import { BlockStatement, Expression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {AST.IfBlock} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ImportDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ImportDeclaration.js index 29700246d4..b572e1d17f 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/ImportDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/ImportDeclaration.js @@ -1,6 +1,6 @@ /** @import { ImportDeclaration } from 'estree' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {ImportDeclaration} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/KeyBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/KeyBlock.js index a013827f60..7d6a8b0006 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/KeyBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/KeyBlock.js @@ -1,7 +1,7 @@ /** @import { Expression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {AST.KeyBlock} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/LabeledStatement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/LabeledStatement.js index 87f56262a8..8d24d260c5 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/LabeledStatement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/LabeledStatement.js @@ -1,7 +1,7 @@ /** @import { Expression, LabeledStatement, Statement } from 'estree' */ /** @import { ReactiveStatement } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_getter } from '../utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/LetDirective.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/LetDirective.js index e174073a26..abdbc381d9 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/LetDirective.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/LetDirective.js @@ -1,7 +1,7 @@ /** @import { Expression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { create_derived } from '../utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/MemberExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/MemberExpression.js index 3f2aada1f5..ab88345ddd 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/MemberExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/MemberExpression.js @@ -1,6 +1,6 @@ /** @import { MemberExpression } from 'estree' */ /** @import { Context } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {MemberExpression} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/OnDirective.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/OnDirective.js index 7c2b1209e9..7a66a8ecbb 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/OnDirective.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/OnDirective.js @@ -1,6 +1,6 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_event, build_event_handler } from './shared/events.js'; const modifiers = [ diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js index 29403ca6ed..07342da314 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/Program.js @@ -1,7 +1,7 @@ /** @import { Expression, ImportDeclaration, MemberExpression, Program } from 'estree' */ /** @import { ComponentContext } from '../types' */ import { build_getter, is_prop_source } from '../utils.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { add_state_transformers } from './shared/declarations.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 203dc0cdb1..7468fcbbc7 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -13,7 +13,7 @@ import { import { escape_html } from '../../../../../escaping.js'; import { dev, is_ignored, locator } from '../../../../state.js'; import { is_event_attribute, is_text_attribute } from '../../../../utils/ast.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { is_custom_element_node } from '../../../nodes.js'; import { clean_nodes, determine_namespace_for_children } from '../../utils.js'; import { build_getter } from '../utils.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js index 33ae6d4d2b..6067c2562a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RenderTag.js @@ -2,7 +2,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ import { unwrap_optional } from '../../../../utils/ast.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {AST.RenderTag} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js index c6f4ba1ed3..ba9fcc7377 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SlotElement.js @@ -1,7 +1,7 @@ /** @import { BlockStatement, Expression, ExpressionStatement, Literal, Property } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_attribute_value } from './shared/element.js'; import { memoize_expression } from './shared/utils.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js index f28f8c8a59..a82645cd7a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SnippetBlock.js @@ -3,7 +3,7 @@ /** @import { ComponentContext } from '../types' */ import { dev } from '../../../../state.js'; import { extract_paths } from '../../../../utils/ast.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { get_value } from './shared/declarations.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js index 9228df9703..b279b5badd 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteBoundary.js @@ -2,7 +2,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ import { dev } from '../../../../state.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {AST.SvelteBoundary} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js index 35aac662cc..ee597dd043 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteElement.js @@ -3,7 +3,7 @@ /** @import { ComponentContext } from '../types' */ import { dev, locator } from '../../../../state.js'; import { is_text_attribute } from '../../../../utils/ast.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { determine_namespace_for_children } from '../../utils.js'; import { build_attribute_value, build_set_attributes, build_set_class } from './shared/element.js'; import { build_render_statement } from './shared/utils.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteHead.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteHead.js index 25fcff0631..0701c37c48 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteHead.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/SvelteHead.js @@ -1,7 +1,7 @@ /** @import { BlockStatement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {AST.SvelteHead} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/TitleElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/TitleElement.js index 72cc57b068..7bfdaf1850 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/TitleElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/TitleElement.js @@ -1,6 +1,6 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_template_chunk } from './shared/utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/TransitionDirective.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/TransitionDirective.js index e331f36472..41340c1290 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/TransitionDirective.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/TransitionDirective.js @@ -2,7 +2,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ import { TRANSITION_GLOBAL, TRANSITION_IN, TRANSITION_OUT } from '../../../../../constants.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { parse_directive_name } from './shared/utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/UpdateExpression.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/UpdateExpression.js index 63c03b0eb6..96be119b84 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/UpdateExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/UpdateExpression.js @@ -1,7 +1,7 @@ /** @import { AssignmentExpression, Expression, UpdateExpression } from 'estree' */ /** @import { Context } from '../types' */ import { object } from '../../../../utils/ast.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { validate_mutation } from './shared/utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/UseDirective.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/UseDirective.js index be9eb2d516..b95f2fc3ef 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/UseDirective.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/UseDirective.js @@ -1,7 +1,7 @@ /** @import { Expression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { parse_directive_name } from './shared/utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js index 3a914fb560..84044e4ded 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js @@ -3,7 +3,7 @@ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */ import { dev } from '../../../../state.js'; import { extract_paths } from '../../../../utils/ast.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import * as assert from '../../../../utils/assert.js'; import { get_rune } from '../../../scope.js'; import { get_prop_source, is_prop_source, is_state_source, should_proxy } from '../utils.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js index 2ea68e206e..c4071c67fe 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js @@ -3,7 +3,7 @@ /** @import { ComponentContext } from '../../types.js' */ import { dev, is_ignored } from '../../../../../state.js'; import { get_attribute_chunks, object } from '../../../../../utils/ast.js'; -import * as b from '../../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_bind_this, memoize_expression, validate_binding } from '../shared/utils.js'; import { build_attribute_value } from '../shared/element.js'; import { build_event_handler } from './events.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/declarations.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/declarations.js index a13ecfed2c..f6bb26daac 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/declarations.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/declarations.js @@ -1,7 +1,7 @@ /** @import { Identifier } from 'estree' */ /** @import { ComponentContext, Context } from '../../types' */ import { is_state_source } from '../../utils.js'; -import * as b from '../../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * Turns `foo` into `$.get(foo)` diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js index 6026445a9a..a093a0bf4a 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js @@ -5,7 +5,7 @@ import { escape_html } from '../../../../../../escaping.js'; import { normalize_attribute } from '../../../../../../utils.js'; import { is_ignored } from '../../../../../state.js'; import { is_event_attribute } from '../../../../../utils/ast.js'; -import * as b from '../../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_class_directives_object, build_style_directives_object } from '../RegularElement.js'; import { build_template_chunk, get_expression_id } from './utils.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/events.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/events.js index 2667a96f6a..d252bd5474 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/events.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/events.js @@ -3,7 +3,7 @@ /** @import { ComponentContext } from '../../types' */ import { is_capture_event, is_passive_event } from '../../../../../../utils.js'; import { dev, locator } from '../../../../../state.js'; -import * as b from '../../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {AST.Attribute} node diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js index f076d7c11e..c91e2b3b44 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/fragment.js @@ -3,7 +3,7 @@ /** @import { ComponentContext } from '../../types' */ import { cannot_be_set_statically } from '../../../../../../utils.js'; import { is_event_attribute, is_text_attribute } from '../../../../../utils/ast.js'; -import * as b from '../../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { is_custom_element_node } from '../../../../nodes.js'; import { build_template_chunk } from './utils.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/special_element.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/special_element.js index 558bc4fee7..c878f2fc07 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/special_element.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/special_element.js @@ -1,7 +1,7 @@ /** @import { Expression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../../types' */ -import * as b from '../../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index 55362d75af..380cf6cd02 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -3,7 +3,7 @@ /** @import { ComponentClientTransformState, Context } from '../../types' */ import { walk } from 'zimmerframe'; import { object } from '../../../../../utils/ast.js'; -import * as b from '../../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { sanitize_template_string } from '../../../../../utils/sanitize_template_string.js'; import { regex_is_valid_identifier } from '../../../../patterns.js'; import is_reference from 'is-reference'; diff --git a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js index f746e90fe2..e7896991d9 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/transform-server.js @@ -5,7 +5,7 @@ import { walk } from 'zimmerframe'; import { set_scope } from '../../scope.js'; import { extract_identifiers } from '../../../utils/ast.js'; -import * as b from '../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { dev, filename } from '../../../state.js'; import { render_stylesheet } from '../css/index.js'; import { AssignmentExpression } from './visitors/AssignmentExpression.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js index 6364063b3b..071a12f9bc 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/AssignmentExpression.js @@ -1,7 +1,7 @@ /** @import { AssignmentExpression, AssignmentOperator, Expression, Pattern } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { Context, ServerTransformState } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_assignment_value } from '../../../../utils/ast.js'; import { visit_assignment_expression } from '../../shared/assignments.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js index 2aa534d257..35e431f43d 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/AwaitBlock.js @@ -1,7 +1,7 @@ /** @import { BlockStatement, Expression, Pattern } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { block_close } from './shared/utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js index a425bc5ec4..5bcbdee9fb 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/CallExpression.js @@ -1,7 +1,7 @@ /** @import { CallExpression, Expression } from 'estree' */ /** @import { Context } from '../types.js' */ import { is_ignored } from '../../../../state.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { get_rune } from '../../../scope.js'; import { transform_inspect_rune } from '../../utils.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/ClassBody.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/ClassBody.js index 365084a284..c0ebdeae08 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/ClassBody.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/ClassBody.js @@ -2,7 +2,7 @@ /** @import { Context } from '../types.js' */ /** @import { StateField } from '../../client/types.js' */ import { dev } from '../../../../state.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { get_rune } from '../../../scope.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/Component.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/Component.js index 01bf50fafa..503b380c50 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/Component.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/Component.js @@ -1,6 +1,6 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_inline_component } from './shared/component.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js index 5f6aea8338..a8e4e575cc 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/ConstTag.js @@ -1,7 +1,7 @@ /** @import { Expression, Pattern } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {AST.ConstTag} node diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/DebugTag.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/DebugTag.js index 6fe4e0b82b..31b53fd3eb 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/DebugTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/DebugTag.js @@ -1,7 +1,7 @@ /** @import { Expression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {AST.DebugTag} node diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js index 7c7ff0ea37..ac6c9891a7 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/EachBlock.js @@ -2,7 +2,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ import { BLOCK_OPEN_ELSE } from '../../../../../internal/server/hydration.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { block_close, block_open } from './shared/utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/ExpressionStatement.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/ExpressionStatement.js index 00d0dba5da..f77e19aec2 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/ExpressionStatement.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/ExpressionStatement.js @@ -1,6 +1,6 @@ /** @import { ExpressionStatement } from 'estree' */ /** @import { Context } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { get_rune } from '../../../scope.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/Fragment.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/Fragment.js index a293b98e7e..a1d25980c4 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/Fragment.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/Fragment.js @@ -1,7 +1,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext, ComponentServerTransformState } from '../types.js' */ import { clean_nodes, infer_namespace } from '../../utils.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { empty_comment, process_children, build_template } from './shared/utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/HtmlTag.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/HtmlTag.js index 0d551a884a..9e857a9308 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/HtmlTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/HtmlTag.js @@ -1,7 +1,7 @@ /** @import { Expression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {AST.HtmlTag} node diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/Identifier.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/Identifier.js index b8c2699d54..fa887650b3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/Identifier.js @@ -1,7 +1,7 @@ /** @import { Identifier, Node } from 'estree' */ /** @import { Context } from '../types.js' */ import is_reference from 'is-reference'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_getter } from './shared/utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js index 0fbc3e9fcb..eb51c941f5 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/IfBlock.js @@ -2,7 +2,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ import { BLOCK_OPEN_ELSE } from '../../../../../internal/server/hydration.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { block_close, block_open } from './shared/utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/LabeledStatement.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/LabeledStatement.js index 2b394e94e3..83c828b839 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/LabeledStatement.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/LabeledStatement.js @@ -1,6 +1,6 @@ /** @import { ExpressionStatement, LabeledStatement } from 'estree' */ /** @import { Context } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {LabeledStatement} node diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/MemberExpression.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/MemberExpression.js index 527c8cf6ed..73631395e6 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/MemberExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/MemberExpression.js @@ -1,6 +1,6 @@ /** @import { MemberExpression } from 'estree' */ /** @import { Context } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {MemberExpression} node diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/PropertyDefinition.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/PropertyDefinition.js index 04751a19d1..c9225bb8da 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/PropertyDefinition.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/PropertyDefinition.js @@ -1,6 +1,6 @@ /** @import { Expression, PropertyDefinition } from 'estree' */ /** @import { Context } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { get_rune } from '../../../scope.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js index af50695efa..5901cb4c50 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/RegularElement.js @@ -4,7 +4,7 @@ /** @import { Scope } from '../../../scope.js' */ import { is_void } from '../../../../../utils.js'; import { dev, locator } from '../../../../state.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { clean_nodes, determine_namespace_for_children } from '../../utils.js'; import { build_element_attributes } from './shared/element.js'; import { process_children, build_template } from './shared/utils.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/RenderTag.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/RenderTag.js index ebf8c3be1c..dd2ede3ba3 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/RenderTag.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/RenderTag.js @@ -2,7 +2,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ import { unwrap_optional } from '../../../../utils/ast.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { empty_comment } from './shared/utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SlotElement.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SlotElement.js index e7925071cd..fee7cb6e02 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SlotElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SlotElement.js @@ -1,7 +1,7 @@ /** @import { BlockStatement, Expression, Literal, Property } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { empty_comment, build_attribute_value } from './shared/utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js index a67fcc8885..5118679b34 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js @@ -2,7 +2,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ import { dev } from '../../../../state.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {AST.SnippetBlock} node diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js index 0d54feee11..734740c1b1 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteBoundary.js @@ -2,7 +2,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types' */ import { BLOCK_CLOSE, BLOCK_OPEN } from '../../../../../internal/server/hydration.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {AST.SvelteBoundary} node diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteElement.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteElement.js index 9f6faa33c8..fd16219860 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteElement.js @@ -3,7 +3,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ import { dev, locator } from '../../../../state.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { determine_namespace_for_children } from '../../utils.js'; import { build_element_attributes } from './shared/element.js'; import { build_template } from './shared/utils.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteHead.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteHead.js index 7da7d8355c..7d064ffbf5 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteHead.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteHead.js @@ -1,7 +1,7 @@ /** @import { BlockStatement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {AST.SvelteHead} node diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteSelf.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteSelf.js index fbedcff283..bb0ecb21b2 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteSelf.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SvelteSelf.js @@ -1,6 +1,6 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { build_inline_component } from './shared/component.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/TitleElement.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/TitleElement.js index 8fd1973453..c42df4c646 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/TitleElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/TitleElement.js @@ -1,6 +1,6 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { process_children, build_template } from './shared/utils.js'; /** diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/UpdateExpression.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/UpdateExpression.js index ae78e14c13..8a2f874e22 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/UpdateExpression.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/UpdateExpression.js @@ -1,6 +1,6 @@ /** @import { UpdateExpression } from 'estree' */ /** @import { Context } from '../types.js' */ -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @param {UpdateExpression} node diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js index a9c9777335..1f2bd3e2b1 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js @@ -3,7 +3,7 @@ /** @import { Context } from '../types.js' */ /** @import { Scope } from '../../../scope.js' */ import { build_fallback, extract_paths } from '../../../../utils/ast.js'; -import * as b from '../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { get_rune } from '../../../scope.js'; import { walk } from 'zimmerframe'; diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js index f4b3dd1b09..9bccf9e05e 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/component.js @@ -2,7 +2,7 @@ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../../types.js' */ import { empty_comment, build_attribute_value } from './utils.js'; -import * as b from '../../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { is_element_node } from '../../../../nodes.js'; import { dev } from '../../../../../state.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js index 78c8342864..b0bcb8fd6f 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/element.js @@ -9,7 +9,7 @@ import { is_custom_element_node } from '../../../../nodes.js'; import { regex_starts_with_newline } from '../../../../patterns.js'; -import * as b from '../../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { ELEMENT_IS_NAMESPACED, ELEMENT_PRESERVE_ATTRIBUTE_CASE diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js index 0353b55357..8fcf8efa68 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js @@ -8,7 +8,7 @@ import { BLOCK_OPEN, EMPTY_COMMENT } from '../../../../../../internal/server/hydration.js'; -import * as b from '../../../../../utils/builders.js'; +import * as b from '#compiler/builders'; import { sanitize_template_string } from '../../../../../utils/sanitize_template_string.js'; import { regex_whitespaces_strict } from '../../../../patterns.js'; diff --git a/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js b/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js index e8e02b8f58..3e6bb0c4c6 100644 --- a/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js +++ b/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js @@ -2,7 +2,7 @@ /** @import { Context as ClientContext } from '../client/types.js' */ /** @import { Context as ServerContext } from '../server/types.js' */ import { extract_paths, is_expression_async } from '../../../utils/ast.js'; -import * as b from '../../../utils/builders.js'; +import * as b from '#compiler/builders'; /** * @template {ClientContext | ServerContext} Context diff --git a/packages/svelte/src/compiler/phases/3-transform/utils.js b/packages/svelte/src/compiler/phases/3-transform/utils.js index 46872fbfcf..5aa40c8abb 100644 --- a/packages/svelte/src/compiler/phases/3-transform/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/utils.js @@ -8,7 +8,7 @@ import { regex_starts_with_newline, regex_starts_with_whitespaces } from '../patterns.js'; -import * as b from '../../utils/builders.js'; +import * as b from '#compiler/builders'; import * as e from '../../errors.js'; import { walk } from 'zimmerframe'; import { extract_identifiers } from '../../utils/ast.js'; diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 51b38a6454..570d5e22d9 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -4,7 +4,7 @@ import is_reference from 'is-reference'; import { walk } from 'zimmerframe'; import { create_expression_metadata } from './nodes.js'; -import * as b from '../utils/builders.js'; +import * as b from '#compiler/builders'; import * as e from '../errors.js'; import { extract_identifiers, diff --git a/packages/svelte/src/compiler/private.d.ts b/packages/svelte/src/compiler/private.d.ts new file mode 100644 index 0000000000..7ac1089373 --- /dev/null +++ b/packages/svelte/src/compiler/private.d.ts @@ -0,0 +1,2 @@ +export * from './types/index'; +export * from './index'; diff --git a/packages/svelte/src/compiler/utils/ast.js b/packages/svelte/src/compiler/utils/ast.js index 0a24d2ff0d..108f4eff64 100644 --- a/packages/svelte/src/compiler/utils/ast.js +++ b/packages/svelte/src/compiler/utils/ast.js @@ -1,7 +1,7 @@ /** @import { AST } from '#compiler' */ /** @import * as ESTree from 'estree' */ import { walk } from 'zimmerframe'; -import * as b from '../utils/builders.js'; +import * as b from '#compiler/builders'; /** * Gets the left-most identifier of a member expression or identifier. diff --git a/packages/svelte/src/internal/client/dev/console-log.js b/packages/svelte/src/internal/client/dev/console-log.js index a578ecea45..d314359ef6 100644 --- a/packages/svelte/src/internal/client/dev/console-log.js +++ b/packages/svelte/src/internal/client/dev/console-log.js @@ -1,4 +1,4 @@ -import { STATE_SYMBOL } from '../constants.js'; +import { STATE_SYMBOL } from '#client/constants'; import { snapshot } from '../../shared/clone.js'; import * as w from '../warnings.js'; import { untrack } from '../runtime.js'; diff --git a/packages/svelte/src/internal/client/dev/debug.js b/packages/svelte/src/internal/client/dev/debug.js index f449dfa2cc..fbde87a2d7 100644 --- a/packages/svelte/src/internal/client/dev/debug.js +++ b/packages/svelte/src/internal/client/dev/debug.js @@ -10,7 +10,7 @@ import { MAYBE_DIRTY, RENDER_EFFECT, ROOT_EFFECT -} from '../constants.js'; +} from '#client/constants'; /** * diff --git a/packages/svelte/src/internal/client/dev/hmr.js b/packages/svelte/src/internal/client/dev/hmr.js index ee5e08c0b1..27e2643d16 100644 --- a/packages/svelte/src/internal/client/dev/hmr.js +++ b/packages/svelte/src/internal/client/dev/hmr.js @@ -1,6 +1,6 @@ /** @import { Source, Effect, TemplateNode } from '#client' */ import { FILENAME, HMR } from '../../../constants.js'; -import { EFFECT_TRANSPARENT } from '../constants.js'; +import { EFFECT_TRANSPARENT } from '#client/constants'; import { hydrate_node, hydrating } from '../dom/hydration.js'; import { block, branch, destroy_effect } from '../reactivity/effects.js'; import { source } from '../reactivity/sources.js'; diff --git a/packages/svelte/src/internal/client/dev/ownership.js b/packages/svelte/src/internal/client/dev/ownership.js index 5a8af6d522..19d2cdb343 100644 --- a/packages/svelte/src/internal/client/dev/ownership.js +++ b/packages/svelte/src/internal/client/dev/ownership.js @@ -1,7 +1,7 @@ /** @typedef {{ file: string, line: number, column: number }} Location */ import { get_descriptor } from '../../shared/utils.js'; -import { LEGACY_PROPS, STATE_SYMBOL } from '../constants.js'; +import { LEGACY_PROPS, STATE_SYMBOL } from '#client/constants'; import { FILENAME } from '../../../constants.js'; import { component_context } from '../context.js'; import * as w from '../warnings.js'; diff --git a/packages/svelte/src/internal/client/dev/tracing.js b/packages/svelte/src/internal/client/dev/tracing.js index 0f2ce747d8..ad80e75c3d 100644 --- a/packages/svelte/src/internal/client/dev/tracing.js +++ b/packages/svelte/src/internal/client/dev/tracing.js @@ -2,7 +2,7 @@ import { UNINITIALIZED } from '../../../constants.js'; import { snapshot } from '../../shared/clone.js'; import { define_property } from '../../shared/utils.js'; -import { DERIVED, STATE_SYMBOL } from '../constants.js'; +import { DERIVED, STATE_SYMBOL } from '#client/constants'; import { effect_tracking } from '../reactivity/effects.js'; import { active_reaction, captured_signals, set_captured_signals, untrack } from '../runtime.js'; diff --git a/packages/svelte/src/internal/client/dom/blocks/boundary.js b/packages/svelte/src/internal/client/dom/blocks/boundary.js index c1ca7a9600..53060017b9 100644 --- a/packages/svelte/src/internal/client/dom/blocks/boundary.js +++ b/packages/svelte/src/internal/client/dom/blocks/boundary.js @@ -1,6 +1,6 @@ /** @import { Effect, TemplateNode, } from '#client' */ -import { BOUNDARY_EFFECT, EFFECT_TRANSPARENT } from '../../constants.js'; +import { BOUNDARY_EFFECT, EFFECT_TRANSPARENT } from '#client/constants'; import { component_context, set_component_context } from '../../context.js'; import { block, branch, destroy_effect, pause_effect } from '../../reactivity/effects.js'; import { diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 228832fcf5..92c953b541 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -33,7 +33,7 @@ import { } from '../../reactivity/effects.js'; import { source, mutable_source, internal_set } from '../../reactivity/sources.js'; import { array_from, is_array } from '../../../shared/utils.js'; -import { INERT } from '../../constants.js'; +import { INERT } from '#client/constants'; import { queue_micro_task } from '../task.js'; import { active_effect, get } from '../../runtime.js'; import { DEV } from 'esm-env'; diff --git a/packages/svelte/src/internal/client/dom/blocks/if.js b/packages/svelte/src/internal/client/dom/blocks/if.js index 423c436fe4..925abb9d9d 100644 --- a/packages/svelte/src/internal/client/dom/blocks/if.js +++ b/packages/svelte/src/internal/client/dom/blocks/if.js @@ -1,5 +1,5 @@ /** @import { Effect, TemplateNode } from '#client' */ -import { EFFECT_TRANSPARENT } from '../../constants.js'; +import { EFFECT_TRANSPARENT } from '#client/constants'; import { hydrate_next, hydrate_node, diff --git a/packages/svelte/src/internal/client/dom/blocks/snippet.js b/packages/svelte/src/internal/client/dom/blocks/snippet.js index a48153900f..c6dce26bfe 100644 --- a/packages/svelte/src/internal/client/dom/blocks/snippet.js +++ b/packages/svelte/src/internal/client/dom/blocks/snippet.js @@ -1,7 +1,7 @@ /** @import { Snippet } from 'svelte' */ /** @import { Effect, TemplateNode } from '#client' */ /** @import { Getters } from '#shared' */ -import { EFFECT_TRANSPARENT } from '../../constants.js'; +import { EFFECT_TRANSPARENT } from '#client/constants'; import { branch, block, destroy_effect, teardown } from '../../reactivity/effects.js'; import { dev_current_component_function, diff --git a/packages/svelte/src/internal/client/dom/blocks/svelte-component.js b/packages/svelte/src/internal/client/dom/blocks/svelte-component.js index 72157eaa40..ad21436505 100644 --- a/packages/svelte/src/internal/client/dom/blocks/svelte-component.js +++ b/packages/svelte/src/internal/client/dom/blocks/svelte-component.js @@ -1,5 +1,5 @@ /** @import { TemplateNode, Dom, Effect } from '#client' */ -import { EFFECT_TRANSPARENT } from '../../constants.js'; +import { EFFECT_TRANSPARENT } from '#client/constants'; import { block, branch, pause_effect } from '../../reactivity/effects.js'; import { hydrate_next, hydrate_node, hydrating } from '../hydration.js'; diff --git a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js index 18641300e5..43f669e844 100644 --- a/packages/svelte/src/internal/client/dom/blocks/svelte-element.js +++ b/packages/svelte/src/internal/client/dom/blocks/svelte-element.js @@ -20,7 +20,7 @@ import { current_each_item, set_current_each_item } from './each.js'; import { active_effect } from '../../runtime.js'; import { component_context } from '../../context.js'; import { DEV } from 'esm-env'; -import { EFFECT_TRANSPARENT } from '../../constants.js'; +import { EFFECT_TRANSPARENT } from '#client/constants'; import { assign_nodes } from '../template.js'; import { is_raw_text_element } from '../../../../utils.js'; diff --git a/packages/svelte/src/internal/client/dom/blocks/svelte-head.js b/packages/svelte/src/internal/client/dom/blocks/svelte-head.js index e3e3eacad7..db2a0c4ef1 100644 --- a/packages/svelte/src/internal/client/dom/blocks/svelte-head.js +++ b/packages/svelte/src/internal/client/dom/blocks/svelte-head.js @@ -2,7 +2,7 @@ import { hydrate_node, hydrating, set_hydrate_node, set_hydrating } from '../hydration.js'; import { create_text, get_first_child, get_next_sibling } from '../operations.js'; import { block } from '../../reactivity/effects.js'; -import { HEAD_EFFECT } from '../../constants.js'; +import { HEAD_EFFECT } from '#client/constants'; import { HYDRATION_START } from '../../../../constants.js'; /** diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index 5a5d5d7c9b..f63f55cc6e 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -4,7 +4,7 @@ import { get_descriptors, get_prototype_of } from '../../../shared/utils.js'; import { create_event, delegate } from './events.js'; import { add_form_reset_listener, autofocus } from './misc.js'; import * as w from '../../warnings.js'; -import { LOADING_ATTR_SYMBOL } from '../../constants.js'; +import { LOADING_ATTR_SYMBOL } from '#client/constants'; import { queue_idle_task } from '../task.js'; import { is_capture_event, is_delegated, normalize_attribute } from '../../../../utils.js'; import { diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/this.js b/packages/svelte/src/internal/client/dom/elements/bindings/this.js index 56b0a56e71..e9bbcedc6f 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/this.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/this.js @@ -1,4 +1,4 @@ -import { STATE_SYMBOL } from '../../../constants.js'; +import { STATE_SYMBOL } from '#client/constants'; import { effect, render_effect } from '../../../reactivity/effects.js'; import { untrack } from '../../../runtime.js'; import { queue_micro_task } from '../../task.js'; diff --git a/packages/svelte/src/internal/client/dom/elements/transitions.js b/packages/svelte/src/internal/client/dom/elements/transitions.js index fbc1da95df..cc895cbccb 100644 --- a/packages/svelte/src/internal/client/dom/elements/transitions.js +++ b/packages/svelte/src/internal/client/dom/elements/transitions.js @@ -12,7 +12,7 @@ import { loop } from '../../loop.js'; import { should_intro } from '../../render.js'; import { current_each_item } from '../blocks/each.js'; import { TRANSITION_GLOBAL, TRANSITION_IN, TRANSITION_OUT } from '../../../../constants.js'; -import { BLOCK_EFFECT, EFFECT_RAN, EFFECT_TRANSPARENT } from '../../constants.js'; +import { BLOCK_EFFECT, EFFECT_RAN, EFFECT_TRANSPARENT } from '#client/constants'; import { queue_micro_task } from '../task.js'; import { without_reactive_context } from './bindings/shared.js'; diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index 5e0aa3dbc3..d690790e3a 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -9,7 +9,7 @@ import { object_prototype } from '../shared/utils.js'; import { state as source, set } from './reactivity/sources.js'; -import { STATE_SYMBOL } from './constants.js'; +import { STATE_SYMBOL } from '#client/constants'; import { UNINITIALIZED } from '../../constants.js'; import * as e from './errors.js'; import { get_stack } from './dev/tracing.js'; diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index c9a8f7674a..21780be862 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -1,6 +1,6 @@ /** @import { Derived, Effect } from '#client' */ import { DEV } from 'esm-env'; -import { CLEAN, DERIVED, DIRTY, EFFECT_HAS_DERIVED, MAYBE_DIRTY, UNOWNED } from '../constants.js'; +import { CLEAN, DERIVED, DIRTY, EFFECT_HAS_DERIVED, MAYBE_DIRTY, UNOWNED } from '#client/constants'; import { active_reaction, active_effect, diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 76b014c916..36be1ecd04 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -33,7 +33,7 @@ import { MAYBE_DIRTY, EFFECT_HAS_DERIVED, BOUNDARY_EFFECT -} from '../constants.js'; +} from '#client/constants'; import { set } from './sources.js'; import * as e from '../errors.js'; import { DEV } from 'esm-env'; diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index bd85b14df0..8bfd8f9e25 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -13,7 +13,7 @@ import { derived, derived_safe_equal } from './deriveds.js'; import { get, captured_signals, untrack } from '../runtime.js'; import { safe_equals } from './equality.js'; import * as e from '../errors.js'; -import { LEGACY_DERIVED_PROP, LEGACY_PROPS, STATE_SYMBOL } from '../constants.js'; +import { LEGACY_DERIVED_PROP, LEGACY_PROPS, STATE_SYMBOL } from '#client/constants'; import { proxy } from '../proxy.js'; import { capture_store_binding } from './store.js'; import { legacy_mode_flag } from '../../flags/index.js'; diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js index 69a41338c0..9d2ad2baee 100644 --- a/packages/svelte/src/internal/client/reactivity/sources.js +++ b/packages/svelte/src/internal/client/reactivity/sources.js @@ -28,7 +28,7 @@ import { MAYBE_DIRTY, BLOCK_EFFECT, ROOT_EFFECT -} from '../constants.js'; +} from '#client/constants'; import * as e from '../errors.js'; import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js'; import { get_stack } from '../dev/tracing.js'; diff --git a/packages/svelte/tsconfig.json b/packages/svelte/tsconfig.json index 76005add13..bab587ace3 100644 --- a/packages/svelte/tsconfig.json +++ b/packages/svelte/tsconfig.json @@ -24,11 +24,7 @@ "svelte/motion": ["./src/motion/public.d.ts"], "svelte/server": ["./src/server/index.d.ts"], "svelte/store": ["./src/store/public.d.ts"], - "svelte/reactivity": ["./src/reactivity/index-client.js"], - "#compiler": ["./src/compiler/types/index.d.ts"], - "#client": ["./src/internal/client/types.d.ts"], - "#server": ["./src/internal/server/types.d.ts"], - "#shared": ["./src/internal/shared/types.d.ts"] + "svelte/reactivity": ["./src/reactivity/index-client.js"] } }, "include": [ From f72bb786516c41e6b6ff6ae513b5feabd93bb0ef Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 17 Apr 2025 14:55:31 -0400 Subject: [PATCH 0361/1429] chore: add missing changeset for #15785 (#15787) --- .changeset/quick-cougars-fly.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/quick-cougars-fly.md diff --git a/.changeset/quick-cougars-fly.md b/.changeset/quick-cougars-fly.md new file mode 100644 index 0000000000..b29260da59 --- /dev/null +++ b/.changeset/quick-cougars-fly.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: use pkg.imports for common modules From 707682eaa94b16fdc7adef20b5742f31ba78cbbe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 15:05:23 -0400 Subject: [PATCH 0362/1429] Version Packages (#15788) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/quick-cougars-fly.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/quick-cougars-fly.md diff --git a/.changeset/quick-cougars-fly.md b/.changeset/quick-cougars-fly.md deleted file mode 100644 index b29260da59..0000000000 --- a/.changeset/quick-cougars-fly.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: use pkg.imports for common modules diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 4ffcb263ab..e5681a3ceb 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.27.2 + +### Patch Changes + +- chore: use pkg.imports for common modules ([#15787](https://github.com/sveltejs/svelte/pull/15787)) + ## 5.27.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index e03c9ee083..dc6c57b1a2 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.27.1", + "version": "5.27.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b0424e8232..62624e866c 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.27.1'; +export const VERSION = '5.27.2'; export const PUBLIC_VERSION = '5'; From 7f473f618d76eb634845c807664442fc65032662 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Thu, 17 Apr 2025 23:51:46 +0200 Subject: [PATCH 0363/1429] fix: allow accessing snippet in script tag (#15789) * fix: allow accessing snippet in script tag * branches are identical * update test * rename test * fix validation (we were mutating the wrong array) * tidy up * remove submodule * changeset --------- Co-authored-by: Rich Harris --- .changeset/few-horses-wink.md | 5 ++++ .../server/visitors/SnippetBlock.js | 30 ++++++++----------- .../snippet-access-in-script/_config.js | 7 +++++ .../samples/snippet-access-in-script/fn.js | 3 ++ .../snippet-access-in-script/main.svelte | 10 +++++++ .../samples/snippet-invalid-call/_config.js | 9 ++++++ .../samples/snippet-invalid-call/main.svelte | 7 +++++ .../_expected/server/index.svelte.js | 4 +-- 8 files changed, 55 insertions(+), 20 deletions(-) create mode 100644 .changeset/few-horses-wink.md create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-access-in-script/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-access-in-script/fn.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-access-in-script/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-invalid-call/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/snippet-invalid-call/main.svelte diff --git a/.changeset/few-horses-wink.md b/.changeset/few-horses-wink.md new file mode 100644 index 0000000000..8ffa3640c0 --- /dev/null +++ b/.changeset/few-horses-wink.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: use function declaration for snippets in server output to avoid TDZ violation diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js index 5118679b34..238485e665 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/SnippetBlock.js @@ -1,4 +1,4 @@ -/** @import { ArrowFunctionExpression, BlockStatement, CallExpression } from 'estree' */ +/** @import { BlockStatement } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { ComponentContext } from '../types.js' */ import { dev } from '../../../../state.js'; @@ -9,27 +9,21 @@ import * as b from '#compiler/builders'; * @param {ComponentContext} context */ export function SnippetBlock(node, context) { - const body = /** @type {BlockStatement} */ (context.visit(node.body)); + let fn = b.function_declaration( + node.expression, + [b.id('$$payload'), ...node.parameters], + /** @type {BlockStatement} */ (context.visit(node.body)) + ); - if (dev) { - body.body.unshift(b.stmt(b.call('$.validate_snippet_args', b.id('$$payload')))); - } + // @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone + fn.___snippet = true; - /** @type {ArrowFunctionExpression | CallExpression} */ - let fn = b.arrow([b.id('$$payload'), ...node.parameters], body); + const statements = node.metadata.can_hoist ? context.state.hoisted : context.state.init; if (dev) { - fn = b.call('$.prevent_snippet_stringification', fn); + fn.body.body.unshift(b.stmt(b.call('$.validate_snippet_args', b.id('$$payload')))); + statements.push(b.stmt(b.call('$.prevent_snippet_stringification', fn.id))); } - const declaration = b.declaration('const', [b.declarator(node.expression, fn)]); - - // @ts-expect-error - TODO remove this hack once $$render_inner for legacy bindings is gone - fn.___snippet = true; - - if (node.metadata.can_hoist) { - context.state.hoisted.push(declaration); - } else { - context.state.init.push(declaration); - } + statements.push(fn); } diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-access-in-script/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-access-in-script/_config.js new file mode 100644 index 0000000000..ed0ead960b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-access-in-script/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-access-in-script/fn.js b/packages/svelte/tests/runtime-runes/samples/snippet-access-in-script/fn.js new file mode 100644 index 0000000000..9e9a48c60c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-access-in-script/fn.js @@ -0,0 +1,3 @@ +export function fn(snippet) { + return snippet; +} diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-access-in-script/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-access-in-script/main.svelte new file mode 100644 index 0000000000..cc73aa31db --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-access-in-script/main.svelte @@ -0,0 +1,10 @@ + + +{#snippet test()} + {variable} +{/snippet} \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-invalid-call/_config.js b/packages/svelte/tests/runtime-runes/samples/snippet-invalid-call/_config.js new file mode 100644 index 0000000000..7e72fd751a --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-invalid-call/_config.js @@ -0,0 +1,9 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + + error: 'invalid_snippet_arguments' +}); diff --git a/packages/svelte/tests/runtime-runes/samples/snippet-invalid-call/main.svelte b/packages/svelte/tests/runtime-runes/samples/snippet-invalid-call/main.svelte new file mode 100644 index 0000000000..3c47db3f96 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/snippet-invalid-call/main.svelte @@ -0,0 +1,7 @@ + + +{#snippet test()} +

hello

+{/snippet} diff --git a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js index 04bfbf6ae4..cadae2cf15 100644 --- a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/server/index.svelte.js @@ -1,9 +1,9 @@ import * as $ from 'svelte/internal/server'; import TextInput from './Child.svelte'; -const snippet = ($$payload) => { +function snippet($$payload) { $$payload.out += `Something`; -}; +} export default function Bind_component_snippet($$payload) { let value = ''; From 5b7af5e28979d50f610cfd000fc2e67a9d8a5101 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 21:11:04 -0400 Subject: [PATCH 0364/1429] Version Packages (#15790) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/few-horses-wink.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/few-horses-wink.md diff --git a/.changeset/few-horses-wink.md b/.changeset/few-horses-wink.md deleted file mode 100644 index 8ffa3640c0..0000000000 --- a/.changeset/few-horses-wink.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: use function declaration for snippets in server output to avoid TDZ violation diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index e5681a3ceb..709b829fcd 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.27.3 + +### Patch Changes + +- fix: use function declaration for snippets in server output to avoid TDZ violation ([#15789](https://github.com/sveltejs/svelte/pull/15789)) + ## 5.27.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index dc6c57b1a2..efead5604c 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.27.2", + "version": "5.27.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 62624e866c..c64eded9c4 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.27.2'; +export const VERSION = '5.27.3'; export const PUBLIC_VERSION = '5'; From d0dcc0b79534f377349e7f6aa4a97c7b55f51864 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Fri, 18 Apr 2025 08:19:37 -0700 Subject: [PATCH 0365/1429] fix: improve partial evaluation (#15781) * init * remove console log * Update packages/svelte/src/compiler/phases/scope.js * fix each indices * dedupe * Update packages/svelte/src/compiler/phases/scope.js * always break * fix formatting * Apply suggestions from code review * compactify * compactify * reuse values * add more globals, template literals, try functions and (some) member expressions * remove console logs * remove function handling, tweak failing test * changeset * try putting static stuff in the template * nevermind * unused * simplify * Update packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js * YAGNI * simplify and fix (should use cooked, not raw) * unused * changeset --------- Co-authored-by: Rich Harris Co-authored-by: Rich Harris --- .changeset/serious-adults-sit.md | 5 + .../client/visitors/shared/utils.js | 10 +- packages/svelte/src/compiler/phases/scope.js | 210 ++++++++++++++++-- .../samples/each-index-non-null/_config.js | 3 + .../_expected/client/index.svelte.js | 19 ++ .../_expected/server/index.svelte.js | 13 ++ .../samples/each-index-non-null/index.svelte | 3 + .../purity/_expected/client/index.svelte.js | 2 +- .../purity/_expected/server/index.svelte.js | 2 +- 9 files changed, 243 insertions(+), 24 deletions(-) create mode 100644 .changeset/serious-adults-sit.md create mode 100644 packages/svelte/tests/snapshot/samples/each-index-non-null/_config.js create mode 100644 packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/client/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/server/index.svelte.js create mode 100644 packages/svelte/tests/snapshot/samples/each-index-non-null/index.svelte diff --git a/.changeset/serious-adults-sit.md b/.changeset/serious-adults-sit.md new file mode 100644 index 0000000000..8c98a7c366 --- /dev/null +++ b/.changeset/serious-adults-sit.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: partially evaluate more expressions diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index 380cf6cd02..bc79b76043 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -69,11 +69,17 @@ export function build_template_chunk( node.metadata.expression ); - has_state ||= node.metadata.expression.has_state; + const evaluated = state.scope.evaluate(value); + + has_state ||= node.metadata.expression.has_state && !evaluated.is_known; if (values.length === 1) { // If we have a single expression, then pass that in directly to possibly avoid doing // extra work in the template_effect (instead we do the work in set_text). + if (evaluated.is_known) { + value = b.literal(evaluated.value); + } + return { value, has_state }; } @@ -89,8 +95,6 @@ export function build_template_chunk( } } - const evaluated = state.scope.evaluate(value); - if (evaluated.is_known) { quasi.value.cooked += evaluated.value + ''; } else { diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 570d5e22d9..8297f174d3 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -1,4 +1,4 @@ -/** @import { ArrowFunctionExpression, BinaryOperator, ClassDeclaration, Expression, FunctionDeclaration, FunctionExpression, Identifier, ImportDeclaration, MemberExpression, LogicalOperator, Node, Pattern, UnaryOperator, VariableDeclarator } from 'estree' */ +/** @import { ArrowFunctionExpression, BinaryOperator, ClassDeclaration, Expression, FunctionDeclaration, FunctionExpression, Identifier, ImportDeclaration, MemberExpression, LogicalOperator, Node, Pattern, UnaryOperator, VariableDeclarator, Super } from 'estree' */ /** @import { Context, Visitor } from 'zimmerframe' */ /** @import { AST, BindingKind, DeclarationKind } from '#compiler' */ import is_reference from 'is-reference'; @@ -18,8 +18,71 @@ import { validate_identifier_name } from './2-analyze/visitors/shared/utils.js'; const UNKNOWN = Symbol('unknown'); /** Includes `BigInt` */ -const NUMBER = Symbol('number'); -const STRING = Symbol('string'); +export const NUMBER = Symbol('number'); +export const STRING = Symbol('string'); + +/** @type {Record} */ +const globals = { + BigInt: [NUMBER, BigInt], + 'Math.min': [NUMBER, Math.min], + 'Math.max': [NUMBER, Math.max], + 'Math.random': [NUMBER], + 'Math.floor': [NUMBER, Math.floor], + // @ts-expect-error + 'Math.f16round': [NUMBER, Math.f16round], + 'Math.round': [NUMBER, Math.round], + 'Math.abs': [NUMBER, Math.abs], + 'Math.acos': [NUMBER, Math.acos], + 'Math.asin': [NUMBER, Math.asin], + 'Math.atan': [NUMBER, Math.atan], + 'Math.atan2': [NUMBER, Math.atan2], + 'Math.ceil': [NUMBER, Math.ceil], + 'Math.cos': [NUMBER, Math.cos], + 'Math.sin': [NUMBER, Math.sin], + 'Math.tan': [NUMBER, Math.tan], + 'Math.exp': [NUMBER, Math.exp], + 'Math.log': [NUMBER, Math.log], + 'Math.pow': [NUMBER, Math.pow], + 'Math.sqrt': [NUMBER, Math.sqrt], + 'Math.clz32': [NUMBER, Math.clz32], + 'Math.imul': [NUMBER, Math.imul], + 'Math.sign': [NUMBER, Math.sign], + 'Math.log10': [NUMBER, Math.log10], + 'Math.log2': [NUMBER, Math.log2], + 'Math.log1p': [NUMBER, Math.log1p], + 'Math.expm1': [NUMBER, Math.expm1], + 'Math.cosh': [NUMBER, Math.cosh], + 'Math.sinh': [NUMBER, Math.sinh], + 'Math.tanh': [NUMBER, Math.tanh], + 'Math.acosh': [NUMBER, Math.acosh], + 'Math.asinh': [NUMBER, Math.asinh], + 'Math.atanh': [NUMBER, Math.atanh], + 'Math.trunc': [NUMBER, Math.trunc], + 'Math.fround': [NUMBER, Math.fround], + 'Math.cbrt': [NUMBER, Math.cbrt], + Number: [NUMBER, Number], + 'Number.isInteger': [NUMBER, Number.isInteger], + 'Number.isFinite': [NUMBER, Number.isFinite], + 'Number.isNaN': [NUMBER, Number.isNaN], + 'Number.isSafeInteger': [NUMBER, Number.isSafeInteger], + 'Number.parseFloat': [NUMBER, Number.parseFloat], + 'Number.parseInt': [NUMBER, Number.parseInt], + String: [STRING, String], + 'String.fromCharCode': [STRING, String.fromCharCode], + 'String.fromCodePoint': [STRING, String.fromCodePoint] +}; + +/** @type {Record} */ +const global_constants = { + 'Math.PI': Math.PI, + 'Math.E': Math.E, + 'Math.LN10': Math.LN10, + 'Math.LN2': Math.LN2, + 'Math.LOG10E': Math.LOG10E, + 'Math.LOG2E': Math.LOG2E, + 'Math.SQRT2': Math.SQRT2, + 'Math.SQRT1_2': Math.SQRT1_2 +}; export class Binding { /** @type {Scope} */ @@ -107,7 +170,7 @@ export class Binding { class Evaluation { /** @type {Set} */ - values = new Set(); + values; /** * True if there is exactly one possible value @@ -147,8 +210,11 @@ class Evaluation { * * @param {Scope} scope * @param {Expression} expression + * @param {Set} values */ - constructor(scope, expression) { + constructor(scope, expression, values) { + this.values = values; + switch (expression.type) { case 'Literal': { this.values.add(expression.value); @@ -172,15 +238,18 @@ class Evaluation { binding.kind === 'rest_prop' || binding.kind === 'bindable_prop'; - if (!binding.updated && binding.initial !== null && !is_prop) { - const evaluation = binding.scope.evaluate(/** @type {Expression} */ (binding.initial)); - for (const value of evaluation.values) { - this.values.add(value); - } + if (binding.initial?.type === 'EachBlock' && binding.initial.index === expression.name) { + this.values.add(NUMBER); break; } - // TODO each index is always defined + if (!binding.updated && binding.initial !== null && !is_prop) { + binding.scope.evaluate(/** @type {Expression} */ (binding.initial), this.values); + break; + } + } else if (expression.name === 'undefined') { + this.values.add(undefined); + break; } // TODO glean what we can from reassignments @@ -336,6 +405,101 @@ class Evaluation { break; } + case 'CallExpression': { + const keypath = get_global_keypath(expression.callee, scope); + + if (keypath) { + if (is_rune(keypath)) { + const arg = /** @type {Expression | undefined} */ (expression.arguments[0]); + + switch (keypath) { + case '$state': + case '$state.raw': + case '$derived': + if (arg) { + scope.evaluate(arg, this.values); + } else { + this.values.add(undefined); + } + break; + + case '$props.id': + this.values.add(STRING); + break; + + case '$effect.tracking': + this.values.add(false); + this.values.add(true); + break; + + case '$derived.by': + if (arg?.type === 'ArrowFunctionExpression' && arg.body.type !== 'BlockStatement') { + scope.evaluate(arg.body, this.values); + break; + } + + this.values.add(UNKNOWN); + break; + + default: { + this.values.add(UNKNOWN); + } + } + + break; + } + + if ( + Object.hasOwn(globals, keypath) && + expression.arguments.every((arg) => arg.type !== 'SpreadElement') + ) { + const [type, fn] = globals[keypath]; + const values = expression.arguments.map((arg) => scope.evaluate(arg)); + + if (fn && values.every((e) => e.is_known)) { + this.values.add(fn(...values.map((e) => e.value))); + } else { + this.values.add(type); + } + + break; + } + } + + this.values.add(UNKNOWN); + break; + } + + case 'TemplateLiteral': { + let result = expression.quasis[0].value.cooked; + + for (let i = 0; i < expression.expressions.length; i += 1) { + const e = scope.evaluate(expression.expressions[i]); + + if (e.is_known) { + result += e.value + expression.quasis[i + 1].value.cooked; + } else { + this.values.add(STRING); + break; + } + } + + this.values.add(result); + break; + } + + case 'MemberExpression': { + const keypath = get_global_keypath(expression, scope); + + if (keypath && Object.hasOwn(global_constants, keypath)) { + this.values.add(global_constants[keypath]); + break; + } + + this.values.add(UNKNOWN); + break; + } + default: { this.values.add(UNKNOWN); } @@ -548,10 +712,10 @@ export class Scope { * Only call this once scope has been fully generated in a first pass, * else this evaluates on incomplete data and may yield wrong results. * @param {Expression} expression - * @param {Set} values + * @param {Set} [values] */ evaluate(expression, values = new Set()) { - return new Evaluation(this, expression); + return new Evaluation(this, expression, values); } } @@ -1115,7 +1279,19 @@ export function get_rune(node, scope) { if (!node) return null; if (node.type !== 'CallExpression') return null; - let n = node.callee; + const keypath = get_global_keypath(node.callee, scope); + + if (!keypath || !is_rune(keypath)) return null; + return keypath; +} + +/** + * Returns the name of the rune if the given expression is a `CallExpression` using a rune. + * @param {Expression | Super} node + * @param {Scope} scope + */ +function get_global_keypath(node, scope) { + let n = node; let joined = ''; @@ -1133,12 +1309,8 @@ export function get_rune(node, scope) { if (n.type !== 'Identifier') return null; - joined = n.name + joined; - - if (!is_rune(joined)) return null; - const binding = scope.get(n.name); if (binding !== null) return null; // rune name, but references a variable or store - return joined; + return n.name + joined; } diff --git a/packages/svelte/tests/snapshot/samples/each-index-non-null/_config.js b/packages/svelte/tests/snapshot/samples/each-index-non-null/_config.js new file mode 100644 index 0000000000..f47bee71df --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/each-index-non-null/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({}); diff --git a/packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/client/index.svelte.js new file mode 100644 index 0000000000..3d46a679b8 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/client/index.svelte.js @@ -0,0 +1,19 @@ +import 'svelte/internal/disclose-version'; +import 'svelte/internal/flags/legacy'; +import * as $ from 'svelte/internal/client'; + +var root_1 = $.template(`

`); + +export default function Each_index_non_null($$anchor) { + var fragment = $.comment(); + var node = $.first_child(fragment); + + $.each(node, 0, () => Array(10), $.index, ($$anchor, $$item, i) => { + var p = root_1(); + + p.textContent = `index: ${i}`; + $.append($$anchor, p); + }); + + $.append($$anchor, fragment); +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/server/index.svelte.js new file mode 100644 index 0000000000..3431e36833 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/server/index.svelte.js @@ -0,0 +1,13 @@ +import * as $ from 'svelte/internal/server'; + +export default function Each_index_non_null($$payload) { + const each_array = $.ensure_array_like(Array(10)); + + $$payload.out += ``; + + for (let i = 0, $$length = each_array.length; i < $$length; i++) { + $$payload.out += `

index: ${$.escape(i)}

`; + } + + $$payload.out += ``; +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/each-index-non-null/index.svelte b/packages/svelte/tests/snapshot/samples/each-index-non-null/index.svelte new file mode 100644 index 0000000000..03bfc9e372 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/each-index-non-null/index.svelte @@ -0,0 +1,3 @@ +{#each Array(10), i} +

index: {i}

+{/each} diff --git a/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js index 940ed8f9e8..5bc9766acf 100644 --- a/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js @@ -8,7 +8,7 @@ export default function Purity($$anchor) { var fragment = root(); var p = $.first_child(fragment); - p.textContent = Math.max(0, Math.min(0, 100)); + p.textContent = 0; var p_1 = $.sibling(p, 2); diff --git a/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js index 588332407a..9457378c0d 100644 --- a/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/purity/_expected/server/index.svelte.js @@ -1,7 +1,7 @@ import * as $ from 'svelte/internal/server'; export default function Purity($$payload) { - $$payload.out += `

${$.escape(Math.max(0, Math.min(0, 100)))}

${$.escape(location.href)}

`; + $$payload.out += `

0

${$.escape(location.href)}

`; Child($$payload, { prop: encodeURIComponent('hello') }); $$payload.out += ``; } \ No newline at end of file From 6fe5977b3d0bfd86d7b8e47f9e5374ffa31fba56 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 11:27:19 -0400 Subject: [PATCH 0366/1429] Version Packages (#15798) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/serious-adults-sit.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/serious-adults-sit.md diff --git a/.changeset/serious-adults-sit.md b/.changeset/serious-adults-sit.md deleted file mode 100644 index 8c98a7c366..0000000000 --- a/.changeset/serious-adults-sit.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: partially evaluate more expressions diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 709b829fcd..8c01c71099 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.28.0 + +### Minor Changes + +- feat: partially evaluate more expressions ([#15781](https://github.com/sveltejs/svelte/pull/15781)) + ## 5.27.3 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index efead5604c..784b14ea26 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.27.3", + "version": "5.28.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index c64eded9c4..4418204eca 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.27.3'; +export const VERSION = '5.28.0'; export const PUBLIC_VERSION = '5'; From d8c6afde944a61c648c6b6f416f5ccafde272d44 Mon Sep 17 00:00:00 2001 From: 7nik Date: Fri, 18 Apr 2025 18:48:27 +0300 Subject: [PATCH 0367/1429] fix: emit error on wrong placement of the `:global` block selector (#15794) Co-authored-by: 7nik --- .changeset/pretty-beers-care.md | 5 +++++ .../docs/98-reference/.generated/compile-errors.md | 6 ++++++ packages/svelte/messages/compile-errors/style.md | 4 ++++ packages/svelte/src/compiler/errors.js | 9 +++++++++ .../src/compiler/phases/2-analyze/css/css-analyze.js | 6 +++++- .../samples/css-global-block-in-pseudoclass/_config.js | 9 +++++++++ .../samples/css-global-block-in-pseudoclass/main.svelte | 4 ++++ 7 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 .changeset/pretty-beers-care.md create mode 100644 packages/svelte/tests/compiler-errors/samples/css-global-block-in-pseudoclass/_config.js create mode 100644 packages/svelte/tests/compiler-errors/samples/css-global-block-in-pseudoclass/main.svelte diff --git a/.changeset/pretty-beers-care.md b/.changeset/pretty-beers-care.md new file mode 100644 index 0000000000..821bad41c4 --- /dev/null +++ b/.changeset/pretty-beers-care.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: emit error on wrong placement of the `:global` block selector diff --git a/documentation/docs/98-reference/.generated/compile-errors.md b/documentation/docs/98-reference/.generated/compile-errors.md index 6196a85ade..e8669ead53 100644 --- a/documentation/docs/98-reference/.generated/compile-errors.md +++ b/documentation/docs/98-reference/.generated/compile-errors.md @@ -274,6 +274,12 @@ A `:global` selector cannot modify an existing selector A `:global` selector can only be modified if it is a descendant of other selectors ``` +### css_global_block_invalid_placement + +``` +A `:global` selector cannot be inside a pseudoclass +``` + ### css_global_invalid_placement ``` diff --git a/packages/svelte/messages/compile-errors/style.md b/packages/svelte/messages/compile-errors/style.md index f08a2156a3..272efbccce 100644 --- a/packages/svelte/messages/compile-errors/style.md +++ b/packages/svelte/messages/compile-errors/style.md @@ -50,6 +50,10 @@ x y { > A `:global` selector can only be modified if it is a descendant of other selectors +## css_global_block_invalid_placement + +> A `:global` selector cannot be inside a pseudoclass + ## css_global_invalid_placement > `:global(...)` can be at the start or end of a selector sequence, but not in the middle diff --git a/packages/svelte/src/compiler/errors.js b/packages/svelte/src/compiler/errors.js index aa328764e1..c99f597468 100644 --- a/packages/svelte/src/compiler/errors.js +++ b/packages/svelte/src/compiler/errors.js @@ -581,6 +581,15 @@ export function css_global_block_invalid_modifier_start(node) { e(node, 'css_global_block_invalid_modifier_start', `A \`:global\` selector can only be modified if it is a descendant of other selectors\nhttps://svelte.dev/e/css_global_block_invalid_modifier_start`); } +/** + * A `:global` selector cannot be inside a pseudoclass + * @param {null | number | NodeLike} node + * @returns {never} + */ +export function css_global_block_invalid_placement(node) { + e(node, 'css_global_block_invalid_placement', `A \`:global\` selector cannot be inside a pseudoclass\nhttps://svelte.dev/e/css_global_block_invalid_placement`); +} + /** * `:global(...)` can be at the start or end of a selector sequence, but not in the middle * @param {null | number | NodeLike} node diff --git a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js index 2dc3435648..d6052c9c3e 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js +++ b/packages/svelte/src/compiler/phases/2-analyze/css/css-analyze.js @@ -68,8 +68,12 @@ const css_visitors = { const global = node.children.find(is_global); if (global) { - const idx = node.children.indexOf(global); + const is_nested = context.path.at(-2)?.type === 'PseudoClassSelector'; + if (is_nested && !global.selectors[0].args) { + e.css_global_block_invalid_placement(global.selectors[0]); + } + const idx = node.children.indexOf(global); if (global.selectors[0].args !== null && idx !== 0 && idx !== node.children.length - 1) { // ensure `:global(...)` is not used in the middle of a selector (but multiple `global(...)` in sequence are ok) for (let i = idx + 1; i < node.children.length; i++) { diff --git a/packages/svelte/tests/compiler-errors/samples/css-global-block-in-pseudoclass/_config.js b/packages/svelte/tests/compiler-errors/samples/css-global-block-in-pseudoclass/_config.js new file mode 100644 index 0000000000..c987725d74 --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/css-global-block-in-pseudoclass/_config.js @@ -0,0 +1,9 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'css_global_block_invalid_placement', + message: 'A `:global` selector cannot be inside a pseudoclass', + position: [28, 35] + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/css-global-block-in-pseudoclass/main.svelte b/packages/svelte/tests/compiler-errors/samples/css-global-block-in-pseudoclass/main.svelte new file mode 100644 index 0000000000..53060df97d --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/css-global-block-in-pseudoclass/main.svelte @@ -0,0 +1,4 @@ + From 3d808840298dcd7fbb40dfa90700082911e514e7 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Fri, 18 Apr 2025 17:50:38 +0200 Subject: [PATCH 0368/1429] fix: `update_version` after `delete` if `source` is `undefined` and `prop` in `target` (#15796) * fix: `update_version` after `delete` if `source` is `undefined` and `prop` in `target` * chore: remove submodule * tweak test --------- Co-authored-by: Rich Harris --- .changeset/great-colts-film.md | 5 +++++ packages/svelte/src/internal/client/proxy.js | 1 + .../samples/delete-proxy-key/_config.js | 13 +++++++++++++ .../samples/delete-proxy-key/main.svelte | 13 +++++++++++++ 4 files changed, 32 insertions(+) create mode 100644 .changeset/great-colts-film.md create mode 100644 packages/svelte/tests/runtime-runes/samples/delete-proxy-key/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/delete-proxy-key/main.svelte diff --git a/.changeset/great-colts-film.md b/.changeset/great-colts-film.md new file mode 100644 index 0000000000..273509c107 --- /dev/null +++ b/.changeset/great-colts-film.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: `update_version` after `delete` if `source` is `undefined` and `prop` in `target` diff --git a/packages/svelte/src/internal/client/proxy.js b/packages/svelte/src/internal/client/proxy.js index d690790e3a..fd5706eaf2 100644 --- a/packages/svelte/src/internal/client/proxy.js +++ b/packages/svelte/src/internal/client/proxy.js @@ -100,6 +100,7 @@ export function proxy(value) { prop, with_parent(() => source(UNINITIALIZED, stack)) ); + update_version(version); } } else { // When working with arrays, we need to also ensure we update the length when removing diff --git a/packages/svelte/tests/runtime-runes/samples/delete-proxy-key/_config.js b/packages/svelte/tests/runtime-runes/samples/delete-proxy-key/_config.js new file mode 100644 index 0000000000..23141a01cd --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/delete-proxy-key/_config.js @@ -0,0 +1,13 @@ +import { test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + html: `

test

`, + + async test({ assert, target }) { + const btn = target.querySelector('button'); + + flushSync(() => btn?.click()); + assert.htmlEqual(target.innerHTML, ''); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/delete-proxy-key/main.svelte b/packages/svelte/tests/runtime-runes/samples/delete-proxy-key/main.svelte new file mode 100644 index 0000000000..d3c519c401 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/delete-proxy-key/main.svelte @@ -0,0 +1,13 @@ + + + + +{#each keys as key} +

{key}

+{/each} From b2d5787a09bffefb716d72706460d8b66e96c862 Mon Sep 17 00:00:00 2001 From: zeroberry <98147817+zeroberry@users.noreply.github.com> Date: Sat, 19 Apr 2025 01:16:25 +0900 Subject: [PATCH 0369/1429] fix: ensure `` properly removes error content in production mode (#15793) * fix: ensure properly removes error content in production mode * add changeset --- .changeset/eleven-roses-speak.md | 5 ++ .../svelte/src/internal/client/runtime.js | 79 ++++++++----------- .../samples/error-boundary-22/Child.svelte | 3 + .../samples/error-boundary-22/_config.js | 11 +++ .../samples/error-boundary-22/main.svelte | 15 ++++ 5 files changed, 69 insertions(+), 44 deletions(-) create mode 100644 .changeset/eleven-roses-speak.md create mode 100644 packages/svelte/tests/runtime-runes/samples/error-boundary-22/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/error-boundary-22/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/error-boundary-22/main.svelte diff --git a/.changeset/eleven-roses-speak.md b/.changeset/eleven-roses-speak.md new file mode 100644 index 0000000000..0d906f3b5f --- /dev/null +++ b/.changeset/eleven-roses-speak.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: ensure properly removes error content in production mode diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 7595aa4a19..fccea3e856 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -291,31 +291,21 @@ export function handle_error(error, effect, previous_effect, component_context) is_throwing_error = true; } - if ( - !DEV || - component_context === null || - !(error instanceof Error) || - handled_errors.has(error) - ) { - propagate_error(error, effect); - return; - } - - handled_errors.add(error); + if (DEV && component_context !== null && error instanceof Error && !handled_errors.has(error)) { + handled_errors.add(error); - const component_stack = []; + const component_stack = []; - const effect_name = effect.fn?.name; + const effect_name = effect.fn?.name; - if (effect_name) { - component_stack.push(effect_name); - } + if (effect_name) { + component_stack.push(effect_name); + } - /** @type {ComponentContext | null} */ - let current_context = component_context; + /** @type {ComponentContext | null} */ + let current_context = component_context; - while (current_context !== null) { - if (DEV) { + while (current_context !== null) { /** @type {string} */ var filename = current_context.function?.[FILENAME]; @@ -323,35 +313,36 @@ export function handle_error(error, effect, previous_effect, component_context) const file = filename.split('/').pop(); component_stack.push(file); } + + current_context = current_context.p; } - current_context = current_context.p; - } + const indent = is_firefox ? ' ' : '\t'; + define_property(error, 'message', { + value: + error.message + `\n${component_stack.map((name) => `\n${indent}in ${name}`).join('')}\n` + }); + define_property(error, 'component_stack', { + value: component_stack + }); - const indent = is_firefox ? ' ' : '\t'; - define_property(error, 'message', { - value: error.message + `\n${component_stack.map((name) => `\n${indent}in ${name}`).join('')}\n` - }); - define_property(error, 'component_stack', { - value: component_stack - }); - - const stack = error.stack; - - // Filter out internal files from callstack - if (stack) { - const lines = stack.split('\n'); - const new_lines = []; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - if (line.includes('svelte/src/internal')) { - continue; + const stack = error.stack; + + // Filter out internal files from callstack + if (stack) { + const lines = stack.split('\n'); + const new_lines = []; + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (line.includes('svelte/src/internal')) { + continue; + } + new_lines.push(line); } - new_lines.push(line); + define_property(error, 'stack', { + value: new_lines.join('\n') + }); } - define_property(error, 'stack', { - value: new_lines.join('\n') - }); } propagate_error(error, effect); diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-22/Child.svelte b/packages/svelte/tests/runtime-runes/samples/error-boundary-22/Child.svelte new file mode 100644 index 0000000000..ea60542af9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-22/Child.svelte @@ -0,0 +1,3 @@ + diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-22/_config.js b/packages/svelte/tests/runtime-runes/samples/error-boundary-22/_config.js new file mode 100644 index 0000000000..c6be4a8cfd --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-22/_config.js @@ -0,0 +1,11 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + mode: ['client'], + test({ assert, target }) { + flushSync(); + + assert.htmlEqual(target.innerHTML, '

error occurred

'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/error-boundary-22/main.svelte b/packages/svelte/tests/runtime-runes/samples/error-boundary-22/main.svelte new file mode 100644 index 0000000000..39b2fb2eb2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/error-boundary-22/main.svelte @@ -0,0 +1,15 @@ + + + +

This should be removed

+ + {#if true} + + {/if} + + {#snippet failed()} +

error occurred

+ {/snippet} +
From e40e9eb1a444426eb5ab27bf39cbda6225283574 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 12:19:20 -0400 Subject: [PATCH 0370/1429] Version Packages (#15800) * Version Packages * Update packages/svelte/CHANGELOG.md --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Rich Harris --- .changeset/eleven-roses-speak.md | 5 ----- .changeset/great-colts-film.md | 5 ----- .changeset/pretty-beers-care.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 .changeset/eleven-roses-speak.md delete mode 100644 .changeset/great-colts-film.md delete mode 100644 .changeset/pretty-beers-care.md diff --git a/.changeset/eleven-roses-speak.md b/.changeset/eleven-roses-speak.md deleted file mode 100644 index 0d906f3b5f..0000000000 --- a/.changeset/eleven-roses-speak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: ensure properly removes error content in production mode diff --git a/.changeset/great-colts-film.md b/.changeset/great-colts-film.md deleted file mode 100644 index 273509c107..0000000000 --- a/.changeset/great-colts-film.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: `update_version` after `delete` if `source` is `undefined` and `prop` in `target` diff --git a/.changeset/pretty-beers-care.md b/.changeset/pretty-beers-care.md deleted file mode 100644 index 821bad41c4..0000000000 --- a/.changeset/pretty-beers-care.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: emit error on wrong placement of the `:global` block selector diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 8c01c71099..cb3b356cc7 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.28.1 + +### Patch Changes + +- fix: ensure `` properly removes error content in production mode ([#15793](https://github.com/sveltejs/svelte/pull/15793)) + +- fix: `update_version` after `delete` if `source` is `undefined` and `prop` in `target` ([#15796](https://github.com/sveltejs/svelte/pull/15796)) + +- fix: emit error on wrong placement of the `:global` block selector ([#15794](https://github.com/sveltejs/svelte/pull/15794)) + ## 5.28.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 784b14ea26..c91fbdd7d3 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.28.0", + "version": "5.28.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 4418204eca..a19a773cd1 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.28.0'; +export const VERSION = '5.28.1'; export const PUBLIC_VERSION = '5'; From a1adf2be6bae6032ab475b0efa76a720643583a2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 19 Apr 2025 17:14:28 -0400 Subject: [PATCH 0371/1429] chore: squelch test console output (#15807) * squelch some console spam * another * more * more * more * last one --- .../samples/ondestroy-prop-access/_config.js | 2 -- .../svelte/tests/runtime-legacy/shared.ts | 10 ++++----- .../_config.js | 10 ++------- .../event-handler-invalid-values/_config.js | 18 ++++----------- .../event-handler-invalid-warning/_config.js | 10 ++------- .../samples/inspect-exception/_config.js | 3 ++- .../samples/inspect-trace-null/_config.js | 6 ++++- .../main.svelte | 4 ++-- packages/svelte/tests/signals/test.ts | 22 ++++++++++++++++--- 9 files changed, 41 insertions(+), 44 deletions(-) diff --git a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/_config.js b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/_config.js index 2ffb7e653f..4f75e82aae 100644 --- a/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/ondestroy-prop-access/_config.js @@ -36,8 +36,6 @@ export default test({ btn1.click(); }); - console.warn(logs); - // the five components guarded by `count < 2` unmount and log assert.deepEqual(logs, [1, true, 1, true, 1, true, 1, true, 1, true]); diff --git a/packages/svelte/tests/runtime-legacy/shared.ts b/packages/svelte/tests/runtime-legacy/shared.ts index fc748ce6b2..14b6cff841 100644 --- a/packages/svelte/tests/runtime-legacy/shared.ts +++ b/packages/svelte/tests/runtime-legacy/shared.ts @@ -351,7 +351,7 @@ async function run_test_variant( // @ts-expect-error globalThis.__svelte.uid = 1; - if (manual_hydrate) { + if (manual_hydrate && variant === 'hydrate') { hydrate_fn = () => { instance = hydrate(mod.default, { target, @@ -469,10 +469,6 @@ async function run_test_variant( throw err; } } finally { - console.log = console_log; - console.warn = console_warn; - console.error = console_error; - config.after_test?.(); // Free up the microtask queue @@ -486,6 +482,10 @@ async function run_test_variant( process.on('unhandledRejection', listener); }); } + + console.log = console_log; + console.warn = console_warn; + console.error = console_error; } } diff --git a/packages/svelte/tests/runtime-runes/samples/event-handler-component-invalid-warning/_config.js b/packages/svelte/tests/runtime-runes/samples/event-handler-component-invalid-warning/_config.js index 01ac3c9ad0..e771010669 100644 --- a/packages/svelte/tests/runtime-runes/samples/event-handler-component-invalid-warning/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/event-handler-component-invalid-warning/_config.js @@ -7,12 +7,8 @@ export default test({ dev: true }, - test({ assert, target, warnings }) { - /** @type {any} */ - let error; - + test({ assert, target, warnings, errors }) { const handler = (/** @type {any}} */ e) => { - error = e.error; e.stopImmediatePropagation(); }; @@ -20,9 +16,7 @@ export default test({ target.querySelector('button')?.click(); - assert.throws(() => { - throw error; - }, /state_unsafe_mutation/); + assert.include(errors[0], 'state_unsafe_mutation'); window.removeEventListener('error', handler, true); diff --git a/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/_config.js b/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/_config.js index d53812d4c3..7ca12af774 100644 --- a/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-values/_config.js @@ -1,4 +1,3 @@ -import { assertType } from 'vitest'; import { test } from '../../test'; export default test({ @@ -8,12 +7,8 @@ export default test({ dev: true }, - test({ assert, target, warnings, logs }) { - /** @type {any} */ - let error = null; - + test({ assert, target, warnings, logs, errors }) { const handler = (/** @type {any} */ e) => { - error = e.error; e.stopImmediatePropagation(); }; @@ -23,16 +18,12 @@ export default test({ b1.click(); assert.deepEqual(logs, []); - assert.equal(error, null); - - error = null; - logs.length = 0; + assert.deepEqual(errors, []); b2.click(); assert.deepEqual(logs, ['clicked']); - assert.equal(error, null); + assert.deepEqual(errors, []); - error = null; logs.length = 0; b3.click(); @@ -40,8 +31,7 @@ export default test({ assert.deepEqual(warnings, [ '`click` handler at main.svelte:10:17 should be a function. Did you mean to add a leading `() =>`?' ]); - assert.isNotNull(error); - assert.match(error.message, /is not a function/); + assert.include(errors[0], 'is not a function'); window.removeEventListener('error', handler, true); } diff --git a/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-warning/_config.js b/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-warning/_config.js index 423351a4c7..a0c792360e 100644 --- a/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-warning/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/event-handler-invalid-warning/_config.js @@ -7,12 +7,8 @@ export default test({ dev: true }, - test({ assert, target, warnings }) { - /** @type {any} */ - let error; - + test({ assert, target, warnings, errors }) { const handler = (/** @type {any} */ e) => { - error = e.error; e.stopImmediatePropagation(); }; @@ -20,9 +16,7 @@ export default test({ target.querySelector('button')?.click(); - assert.throws(() => { - throw error; - }, /state_unsafe_mutation/); + assert.include(errors[0], 'state_unsafe_mutation'); window.removeEventListener('error', handler, true); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-exception/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-exception/_config.js index 57caf6e08d..e155ff236a 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-exception/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-exception/_config.js @@ -6,11 +6,12 @@ export default test({ dev: true }, - async test({ assert, target, logs }) { + async test({ assert, target, logs, errors }) { const b1 = target.querySelector('button'); b1?.click(); flushSync(); + assert.ok(errors.length > 0); assert.deepEqual(logs, ['init', 'a', 'init', 'b']); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/inspect-trace-null/_config.js b/packages/svelte/tests/runtime-runes/samples/inspect-trace-null/_config.js index e779a835c2..7982e8c1c6 100644 --- a/packages/svelte/tests/runtime-runes/samples/inspect-trace-null/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/inspect-trace-null/_config.js @@ -1,8 +1,12 @@ +import { assert } from 'vitest'; import { test } from '../../test'; export default test({ compileOptions: { dev: true }, - test() {} + + test({ logs }) { + assert.ok(logs.length > 0); + } }); diff --git a/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/main.svelte b/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/main.svelte index e909d77fd6..9f3a56a9ed 100644 --- a/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/unhoist-function-accessing-snippet/main.svelte @@ -1,6 +1,6 @@ @@ -9,4 +9,4 @@ {#snippet snip()} snippet {x} -{/snippet} \ No newline at end of file +{/snippet} diff --git a/packages/svelte/tests/signals/test.ts b/packages/svelte/tests/signals/test.ts index 3a427e9392..8421ae4a7c 100644 --- a/packages/svelte/tests/signals/test.ts +++ b/packages/svelte/tests/signals/test.ts @@ -15,6 +15,7 @@ import { derived } from '../../src/internal/client/reactivity/deriveds'; import { snapshot } from '../../src/internal/shared/clone.js'; import { SvelteSet } from '../../src/reactivity/set'; import { DESTROYED } from '../../src/internal/client/constants'; +import { noop } from 'svelte/internal/client'; /** * @param runes runes mode @@ -469,6 +470,9 @@ describe('signals', () => { test('schedules rerun when writing to signal before reading it', (runes) => { if (!runes) return () => {}; + const error = console.error; + console.error = noop; + const value = state({ count: 0 }); user_effect(() => { set(value, { count: 0 }); @@ -482,14 +486,19 @@ describe('signals', () => { } catch (e: any) { assert.include(e.message, 'effect_update_depth_exceeded'); errored = true; + } finally { + assert.equal(errored, true); + console.error = error; } - assert.equal(errored, true); }; }); test('schedules rerun when updating deeply nested value', (runes) => { if (!runes) return () => {}; + const error = console.error; + console.error = noop; + const value = proxy({ a: { b: { c: 0 } } }); user_effect(() => { value.a.b.c += 1; @@ -502,14 +511,19 @@ describe('signals', () => { } catch (e: any) { assert.include(e.message, 'effect_update_depth_exceeded'); errored = true; + } finally { + assert.equal(errored, true); + console.error = error; } - assert.equal(errored, true); }; }); test('schedules rerun when writing to signal before reading it', (runes) => { if (!runes) return () => {}; + const error = console.error; + console.error = noop; + const value = proxy({ arr: [] }); user_effect(() => { value.arr = []; @@ -523,8 +537,10 @@ describe('signals', () => { } catch (e: any) { assert.include(e.message, 'effect_update_depth_exceeded'); errored = true; + } finally { + assert.equal(errored, true); + console.error = error; } - assert.equal(errored, true); }; }); From bfb969a6cccb92180180416641e48889eab730a6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 21 Apr 2025 05:56:42 -0400 Subject: [PATCH 0372/1429] chore: simplify `process_effects` (#15809) --- packages/svelte/src/internal/client/runtime.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index fccea3e856..7a926bf624 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -780,19 +780,12 @@ function process_effects(root) { } else if (is_branch) { effect.f ^= CLEAN; } else { - // Ensure we set the effect to be the active reaction - // to ensure that unowned deriveds are correctly tracked - // because we're flushing the current effect - var previous_active_reaction = active_reaction; try { - active_reaction = effect; if (check_dirtiness(effect)) { update_effect(effect); } } catch (error) { handle_error(error, effect, null, effect.ctx); - } finally { - active_reaction = previous_active_reaction; } } From dfd742d532f578f47bc3e414e18e61af030381f7 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:03:02 +0200 Subject: [PATCH 0373/1429] fix: don't mark selector lists inside `:global` with multiple items as unused (#15817) Regression from #15762 Fixes #15816 --- .changeset/wild-actors-retire.md | 5 +++++ .../src/compiler/phases/3-transform/css/index.js | 7 ++++--- .../tests/css/samples/global-block/_config.js | 16 ++++++++-------- .../tests/css/samples/global-block/expected.css | 4 ++++ .../tests/css/samples/global-block/input.svelte | 4 ++++ 5 files changed, 25 insertions(+), 11 deletions(-) create mode 100644 .changeset/wild-actors-retire.md diff --git a/.changeset/wild-actors-retire.md b/.changeset/wild-actors-retire.md new file mode 100644 index 0000000000..d01a1c169b --- /dev/null +++ b/.changeset/wild-actors-retire.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't mark selector lists inside `:global` with multiple items as unused diff --git a/packages/svelte/src/compiler/phases/3-transform/css/index.js b/packages/svelte/src/compiler/phases/3-transform/css/index.js index 9f1142cce9..cee7ab2791 100644 --- a/packages/svelte/src/compiler/phases/3-transform/css/index.js +++ b/packages/svelte/src/compiler/phases/3-transform/css/index.js @@ -196,9 +196,12 @@ const visitors = { next(); }, SelectorList(node, { state, next, path }) { + const parent = path.at(-1); + // Only add comments if we're not inside a complex selector that itself is unused or a global block if ( - (!is_in_global_block(path) || node.children.length > 1) && + (!is_in_global_block(path) || + (node.children.length > 1 && parent?.type === 'Rule' && parent.metadata.is_global_block)) && !path.find((n) => n.type === 'ComplexSelector' && !n.metadata.used) ) { const children = node.children; @@ -260,7 +263,6 @@ const visitors = { // if this selector list belongs to a rule, require a specificity bump for the // first scoped selector but only if we're at the top level - let parent = path.at(-1); if (parent?.type === 'Rule') { specificity = { bumped: false }; @@ -376,7 +378,6 @@ const visitors = { }; /** - * * @param {Array} path */ function is_in_global_block(path) { diff --git a/packages/svelte/tests/css/samples/global-block/_config.js b/packages/svelte/tests/css/samples/global-block/_config.js index a8b11a73ec..18a56e9a97 100644 --- a/packages/svelte/tests/css/samples/global-block/_config.js +++ b/packages/svelte/tests/css/samples/global-block/_config.js @@ -7,28 +7,28 @@ export default test({ code: 'css_unused_selector', message: 'Unused CSS selector ".unused :global"', start: { - line: 69, + line: 73, column: 1, - character: 917 + character: 964 }, end: { - line: 69, + line: 73, column: 16, - character: 932 + character: 979 } }, { code: 'css_unused_selector', message: 'Unused CSS selector "unused :global"', start: { - line: 100, + line: 104, column: 29, - character: 1223 + character: 1270 }, end: { - line: 100, + line: 104, column: 43, - character: 1237 + character: 1284 } } ] diff --git a/packages/svelte/tests/css/samples/global-block/expected.css b/packages/svelte/tests/css/samples/global-block/expected.css index 12f9a75032..be1838fd98 100644 --- a/packages/svelte/tests/css/samples/global-block/expected.css +++ b/packages/svelte/tests/css/samples/global-block/expected.css @@ -3,6 +3,10 @@ .x { color: green; } + + .a, .selector, .list { + color: green; + } /*}*/ div.svelte-xyz { diff --git a/packages/svelte/tests/css/samples/global-block/input.svelte b/packages/svelte/tests/css/samples/global-block/input.svelte index ee05205d67..86d438031a 100644 --- a/packages/svelte/tests/css/samples/global-block/input.svelte +++ b/packages/svelte/tests/css/samples/global-block/input.svelte @@ -5,6 +5,10 @@ .x { color: green; } + + .a, .selector, .list { + color: green; + } } div :global { From 018996c1677e17857c404df33c8035a1a0c8b42e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:17:51 +0200 Subject: [PATCH 0374/1429] Version Packages (#15818) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/wild-actors-retire.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/wild-actors-retire.md diff --git a/.changeset/wild-actors-retire.md b/.changeset/wild-actors-retire.md deleted file mode 100644 index d01a1c169b..0000000000 --- a/.changeset/wild-actors-retire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: don't mark selector lists inside `:global` with multiple items as unused diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index cb3b356cc7..72cd00bc6a 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.28.2 + +### Patch Changes + +- fix: don't mark selector lists inside `:global` with multiple items as unused ([#15817](https://github.com/sveltejs/svelte/pull/15817)) + ## 5.28.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index c91fbdd7d3..ff71429d2f 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.28.1", + "version": "5.28.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index a19a773cd1..a3a9979d65 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.28.1'; +export const VERSION = '5.28.2'; export const PUBLIC_VERSION = '5'; From 1c2fc210232c16d2eb0fdb20d9bac3cef2b87897 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Wed, 23 Apr 2025 16:49:11 +0200 Subject: [PATCH 0375/1429] fix: allow characters in the supplementary special-purpose plane (#15823) fixes #15821 Source: https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Special-purpose_Plane --- .changeset/wild-bulldogs-move.md | 5 +++++ packages/svelte/src/compiler/phases/1-parse/utils/html.js | 7 +++++++ 2 files changed, 12 insertions(+) create mode 100644 .changeset/wild-bulldogs-move.md diff --git a/.changeset/wild-bulldogs-move.md b/.changeset/wild-bulldogs-move.md new file mode 100644 index 0000000000..c3c5580f77 --- /dev/null +++ b/.changeset/wild-bulldogs-move.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow characters in the supplementary special-purpose plane diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/html.js b/packages/svelte/src/compiler/phases/1-parse/utils/html.js index a68acb996f..a0c2a5b06f 100644 --- a/packages/svelte/src/compiler/phases/1-parse/utils/html.js +++ b/packages/svelte/src/compiler/phases/1-parse/utils/html.js @@ -72,6 +72,8 @@ const NUL = 0; // to replace them ourselves // // Source: http://en.wikipedia.org/wiki/Character_encodings_in_HTML#Illegal_characters +// Also see: https://en.wikipedia.org/wiki/Plane_(Unicode) +// Also see: https://html.spec.whatwg.org/multipage/parsing.html#preprocessing-the-input-stream /** @param {number} code */ function validate_code(code) { @@ -116,5 +118,10 @@ function validate_code(code) { return code; } + // supplementary special-purpose plane 0xe0000 - 0xe07f and 0xe0100 - 0xe01ef + if ((code >= 917504 && code <= 917631) || (code >= 917760 && code <= 917999)) { + return code; + } + return NUL; } From c051a6eb1e61bf6c72842ba3c04900b07c54a5fe Mon Sep 17 00:00:00 2001 From: Lars Francke Date: Wed, 30 Apr 2025 08:53:22 +0200 Subject: [PATCH 0376/1429] fix: improve error message for migration errors when slot would be renamed (#15841) Co-authored-by: Paolo Ricciuti --- .changeset/strong-coins-peel.md | 5 +++++ packages/svelte/src/compiler/migrate/index.js | 4 ++-- .../impossible-migrate-$derived-derived-var-3/output.svelte | 4 ++-- .../impossible-migrate-slot-change-name/output.svelte | 4 ++-- .../impossible-migrate-slot-non-identifier/output.svelte | 4 ++-- 5 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 .changeset/strong-coins-peel.md diff --git a/.changeset/strong-coins-peel.md b/.changeset/strong-coins-peel.md new file mode 100644 index 0000000000..013e8e44a1 --- /dev/null +++ b/.changeset/strong-coins-peel.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: improve error message for migration errors when slot would be renamed diff --git a/packages/svelte/src/compiler/migrate/index.js b/packages/svelte/src/compiler/migrate/index.js index 75a9a64905..2d5a4dcd9e 100644 --- a/packages/svelte/src/compiler/migrate/index.js +++ b/packages/svelte/src/compiler/migrate/index.js @@ -1307,7 +1307,7 @@ const template = { name = state.scope.generate(slot_name); if (name !== slot_name) { throw new MigrationError( - 'This migration would change the name of a slot making the component unusable' + `This migration would change the name of a slot (${slot_name} to ${name}) making the component unusable` ); } } @@ -1880,7 +1880,7 @@ function handle_identifier(node, state, path) { let new_name = state.scope.generate(name); if (new_name !== name) { throw new MigrationError( - 'This migration would change the name of a slot making the component unusable' + `This migration would change the name of a slot (${name} to ${new_name}) making the component unusable` ); } } diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte index 9e4f086aed..26012e1115 100644 --- a/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-$derived-derived-var-3/output.svelte @@ -1,7 +1,7 @@ - + - \ No newline at end of file + diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-slot-change-name/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-slot-change-name/output.svelte index 2b6838a1d6..328966b63b 100644 --- a/packages/svelte/tests/migrate/samples/impossible-migrate-slot-change-name/output.svelte +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-slot-change-name/output.svelte @@ -1,6 +1,6 @@ - + - \ No newline at end of file + diff --git a/packages/svelte/tests/migrate/samples/impossible-migrate-slot-non-identifier/output.svelte b/packages/svelte/tests/migrate/samples/impossible-migrate-slot-non-identifier/output.svelte index 6e5ab10310..1e763577df 100644 --- a/packages/svelte/tests/migrate/samples/impossible-migrate-slot-non-identifier/output.svelte +++ b/packages/svelte/tests/migrate/samples/impossible-migrate-slot-non-identifier/output.svelte @@ -1,2 +1,2 @@ - - \ No newline at end of file + + From 0ace76d8f1ea5402087e202152630cd50a5d757a Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Sat, 3 May 2025 05:43:27 -0700 Subject: [PATCH 0377/1429] docs: clarify `$inspect.trace` docs (#15858) * docs: clarify `$inspect.trace` docs * Update 07-$inspect.md * Update documentation/docs/02-runes/07-$inspect.md --------- Co-authored-by: Rich Harris --- documentation/docs/02-runes/07-$inspect.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/docs/02-runes/07-$inspect.md b/documentation/docs/02-runes/07-$inspect.md index ff3d64757b..13ac8b79a3 100644 --- a/documentation/docs/02-runes/07-$inspect.md +++ b/documentation/docs/02-runes/07-$inspect.md @@ -52,6 +52,7 @@ This rune, added in 5.14, causes the surrounding function to be _traced_ in deve import { doSomeWork } from './elsewhere'; $effect(() => { + +++// $inspect.trace must be the first statement of a function body+++ +++$inspect.trace();+++ doSomeWork(); }); From 8b7bea162449877be637dea44caa256928be0b19 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 12 May 2025 14:19:47 -0400 Subject: [PATCH 0378/1429] chore: better `bench:compare` script (#15894) * chore: make bench:compare results more legible * fix * fix * fix --- benchmarking/compare/index.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/benchmarking/compare/index.js b/benchmarking/compare/index.js index 9d8d279c35..8f38686a29 100644 --- a/benchmarking/compare/index.js +++ b/benchmarking/compare/index.js @@ -67,19 +67,29 @@ for (let i = 0; i < results[0].length; i += 1) { for (const metric of ['time', 'gc_time']) { const times = results.map((result) => +result[i][metric]); let min = Infinity; + let max = -Infinity; let min_index = -1; for (let b = 0; b < times.length; b += 1) { - if (times[b] < min) { - min = times[b]; + const time = times[b]; + + if (time < min) { + min = time; min_index = b; } + + if (time > max) { + max = time; + } } if (min !== 0) { - console.group(`${metric}: fastest is ${branches[min_index]}`); + console.group(`${metric}: fastest is ${char(min_index)} (${branches[min_index]})`); times.forEach((time, b) => { - console.log(`${branches[b]}: ${time.toFixed(2)}ms (${((time / min) * 100).toFixed(2)}%)`); + const SIZE = 20; + const n = Math.round(SIZE * (time / max)); + + console.log(`${char(b)}: ${'◼'.repeat(n)}${' '.repeat(SIZE - n)} ${time.toFixed(2)}ms`); }); console.groupEnd(); } @@ -87,3 +97,7 @@ for (let i = 0; i < results[0].length; i += 1) { console.groupEnd(); } + +function char(i) { + return String.fromCharCode(97 + i); +} From b6164128b3ea823876fc9d14e8251befac6ff7bd Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 12 May 2025 15:51:05 -0400 Subject: [PATCH 0379/1429] chore: avoid microtasks when flushing sync (alternative) (#15895) * chore: avoid microtasks when flushing sync * chore: avoid microtasks when flushing sync * chore: avoid microtasks when flushing sync * tweak * WIP * another * more * tweak * fix * fix * belt and braces * Revert "belt and braces" This reverts commit f8de1cf47ade629347ae7087cbb7e5f5099d0758. --------- Co-authored-by: Dominic Gannaway --- .changeset/polite-melons-tickle.md | 5 +++++ packages/svelte/src/internal/client/runtime.js | 2 ++ 2 files changed, 7 insertions(+) create mode 100644 .changeset/polite-melons-tickle.md diff --git a/.changeset/polite-melons-tickle.md b/.changeset/polite-melons-tickle.md new file mode 100644 index 0000000000..42967ea936 --- /dev/null +++ b/.changeset/polite-melons-tickle.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +chore: avoid microtasks when flushing sync diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 7a926bf624..2375dc0a63 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -823,6 +823,8 @@ export function flushSync(fn) { if (fn) { is_flushing = true; flush_queued_root_effects(); + + is_flushing = true; result = fn(); } From 9e49be4d307dd179eab6312a80f0c0f7bc0040b4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 16:03:49 -0400 Subject: [PATCH 0380/1429] Version Packages (#15825) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/polite-melons-tickle.md | 5 ----- .changeset/strong-coins-peel.md | 5 ----- .changeset/wild-bulldogs-move.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 .changeset/polite-melons-tickle.md delete mode 100644 .changeset/strong-coins-peel.md delete mode 100644 .changeset/wild-bulldogs-move.md diff --git a/.changeset/polite-melons-tickle.md b/.changeset/polite-melons-tickle.md deleted file mode 100644 index 42967ea936..0000000000 --- a/.changeset/polite-melons-tickle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: avoid microtasks when flushing sync diff --git a/.changeset/strong-coins-peel.md b/.changeset/strong-coins-peel.md deleted file mode 100644 index 013e8e44a1..0000000000 --- a/.changeset/strong-coins-peel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: improve error message for migration errors when slot would be renamed diff --git a/.changeset/wild-bulldogs-move.md b/.changeset/wild-bulldogs-move.md deleted file mode 100644 index c3c5580f77..0000000000 --- a/.changeset/wild-bulldogs-move.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: allow characters in the supplementary special-purpose plane diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 72cd00bc6a..8650ba0e5b 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.28.3 + +### Patch Changes + +- chore: avoid microtasks when flushing sync ([#15895](https://github.com/sveltejs/svelte/pull/15895)) + +- fix: improve error message for migration errors when slot would be renamed ([#15841](https://github.com/sveltejs/svelte/pull/15841)) + +- fix: allow characters in the supplementary special-purpose plane ([#15823](https://github.com/sveltejs/svelte/pull/15823)) + ## 5.28.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index ff71429d2f..85eef9c2eb 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.28.2", + "version": "5.28.3", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index a3a9979d65..b072c6432c 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.28.2'; +export const VERSION = '5.28.3'; export const PUBLIC_VERSION = '5'; From 1cab761110b7268bd4ad98c86db419f594a0ac2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 16:10:30 -0400 Subject: [PATCH 0381/1429] chore(deps-dev): bump vite from 5.4.18 to 5.4.19 (#15897) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.18 to 5.4.19. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v5.4.19/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.4.19/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 5.4.19 dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- playgrounds/sandbox/package.json | 2 +- pnpm-lock.yaml | 289 ++++++++++++++++--------------- 2 files changed, 149 insertions(+), 142 deletions(-) diff --git a/playgrounds/sandbox/package.json b/playgrounds/sandbox/package.json index 5aee92ab17..3ab65ac4b5 100644 --- a/playgrounds/sandbox/package.json +++ b/playgrounds/sandbox/package.json @@ -18,7 +18,7 @@ "polka": "^1.0.0-next.25", "svelte": "workspace:*", "tinyglobby": "^0.2.12", - "vite": "^5.4.18", + "vite": "^5.4.19", "vite-plugin-inspect": "^0.8.4" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3518b0e57e..3d5db0fd8e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,7 +152,7 @@ importers: devDependencies: '@sveltejs/vite-plugin-svelte': specifier: ^4.0.0-next.6 - version: 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) polka: specifier: ^1.0.0-next.25 version: 1.0.0-next.25 @@ -163,11 +163,11 @@ importers: specifier: ^0.2.12 version: 0.2.12 vite: - specifier: ^5.4.18 - version: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + specifier: ^5.4.19 + version: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vite-plugin-inspect: specifier: ^0.8.4 - version: 0.8.4(rollup@4.39.0)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + version: 0.8.4(rollup@4.40.2)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) packages: @@ -408,8 +408,8 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/eslint-utils@4.5.1': - resolution: {integrity: sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==} + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -551,8 +551,8 @@ packages: cpu: [arm] os: [android] - '@rollup/rollup-android-arm-eabi@4.39.0': - resolution: {integrity: sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==} + '@rollup/rollup-android-arm-eabi@4.40.2': + resolution: {integrity: sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==} cpu: [arm] os: [android] @@ -561,8 +561,8 @@ packages: cpu: [arm64] os: [android] - '@rollup/rollup-android-arm64@4.39.0': - resolution: {integrity: sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==} + '@rollup/rollup-android-arm64@4.40.2': + resolution: {integrity: sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==} cpu: [arm64] os: [android] @@ -571,8 +571,8 @@ packages: cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-arm64@4.39.0': - resolution: {integrity: sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==} + '@rollup/rollup-darwin-arm64@4.40.2': + resolution: {integrity: sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==} cpu: [arm64] os: [darwin] @@ -581,18 +581,18 @@ packages: cpu: [x64] os: [darwin] - '@rollup/rollup-darwin-x64@4.39.0': - resolution: {integrity: sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==} + '@rollup/rollup-darwin-x64@4.40.2': + resolution: {integrity: sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.39.0': - resolution: {integrity: sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==} + '@rollup/rollup-freebsd-arm64@4.40.2': + resolution: {integrity: sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.39.0': - resolution: {integrity: sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==} + '@rollup/rollup-freebsd-x64@4.40.2': + resolution: {integrity: sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==} cpu: [x64] os: [freebsd] @@ -601,8 +601,8 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-gnueabihf@4.39.0': - resolution: {integrity: sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==} + '@rollup/rollup-linux-arm-gnueabihf@4.40.2': + resolution: {integrity: sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==} cpu: [arm] os: [linux] @@ -611,8 +611,8 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.39.0': - resolution: {integrity: sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==} + '@rollup/rollup-linux-arm-musleabihf@4.40.2': + resolution: {integrity: sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==} cpu: [arm] os: [linux] @@ -621,8 +621,8 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.39.0': - resolution: {integrity: sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==} + '@rollup/rollup-linux-arm64-gnu@4.40.2': + resolution: {integrity: sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==} cpu: [arm64] os: [linux] @@ -631,13 +631,13 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.39.0': - resolution: {integrity: sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==} + '@rollup/rollup-linux-arm64-musl@4.40.2': + resolution: {integrity: sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.39.0': - resolution: {integrity: sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==} + '@rollup/rollup-linux-loongarch64-gnu@4.40.2': + resolution: {integrity: sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==} cpu: [loong64] os: [linux] @@ -646,8 +646,8 @@ packages: cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': - resolution: {integrity: sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==} + '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': + resolution: {integrity: sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==} cpu: [ppc64] os: [linux] @@ -656,13 +656,13 @@ packages: cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.39.0': - resolution: {integrity: sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==} + '@rollup/rollup-linux-riscv64-gnu@4.40.2': + resolution: {integrity: sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.39.0': - resolution: {integrity: sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==} + '@rollup/rollup-linux-riscv64-musl@4.40.2': + resolution: {integrity: sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==} cpu: [riscv64] os: [linux] @@ -671,8 +671,8 @@ packages: cpu: [s390x] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.39.0': - resolution: {integrity: sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==} + '@rollup/rollup-linux-s390x-gnu@4.40.2': + resolution: {integrity: sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==} cpu: [s390x] os: [linux] @@ -681,8 +681,8 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.39.0': - resolution: {integrity: sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==} + '@rollup/rollup-linux-x64-gnu@4.40.2': + resolution: {integrity: sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==} cpu: [x64] os: [linux] @@ -691,8 +691,8 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.39.0': - resolution: {integrity: sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==} + '@rollup/rollup-linux-x64-musl@4.40.2': + resolution: {integrity: sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==} cpu: [x64] os: [linux] @@ -701,8 +701,8 @@ packages: cpu: [arm64] os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.39.0': - resolution: {integrity: sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==} + '@rollup/rollup-win32-arm64-msvc@4.40.2': + resolution: {integrity: sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==} cpu: [arm64] os: [win32] @@ -711,8 +711,8 @@ packages: cpu: [ia32] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.39.0': - resolution: {integrity: sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==} + '@rollup/rollup-win32-ia32-msvc@4.40.2': + resolution: {integrity: sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==} cpu: [ia32] os: [win32] @@ -721,8 +721,8 @@ packages: cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.39.0': - resolution: {integrity: sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==} + '@rollup/rollup-win32-x64-msvc@4.40.2': + resolution: {integrity: sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==} cpu: [x64] os: [win32] @@ -816,8 +816,8 @@ packages: resolution: {integrity: sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.29.1': - resolution: {integrity: sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==} + '@typescript-eslint/scope-manager@8.32.1': + resolution: {integrity: sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/type-utils@8.26.0': @@ -831,8 +831,8 @@ packages: resolution: {integrity: sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.29.1': - resolution: {integrity: sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==} + '@typescript-eslint/types@8.32.1': + resolution: {integrity: sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.26.0': @@ -841,8 +841,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/typescript-estree@8.29.1': - resolution: {integrity: sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==} + '@typescript-eslint/typescript-estree@8.32.1': + resolution: {integrity: sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' @@ -854,8 +854,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@8.29.1': - resolution: {integrity: sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==} + '@typescript-eslint/utils@8.32.1': + resolution: {integrity: sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -865,8 +865,8 @@ packages: resolution: {integrity: sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.29.1': - resolution: {integrity: sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==} + '@typescript-eslint/visitor-keys@8.32.1': + resolution: {integrity: sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitest/coverage-v8@2.0.5': @@ -1974,8 +1974,8 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rollup@4.39.0: - resolution: {integrity: sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==} + rollup@4.40.2: + resolution: {integrity: sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2013,6 +2013,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -2310,8 +2315,8 @@ packages: terser: optional: true - vite@5.4.18: - resolution: {integrity: sha512-1oDcnEp3lVyHCuQ2YFelM4Alm2o91xNoMncRm1U7S+JdYfYOvbiGZ3/CxGttrOu2M/KcGz7cRC2DoNUA6urmMA==} + vite@5.4.19: + resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -2711,7 +2716,7 @@ snapshots: eslint: 9.9.1 eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.5.1(eslint@9.9.1)': + '@eslint-community/eslint-utils@4.7.0(eslint@9.9.1)': dependencies: eslint: 9.9.1 eslint-visitor-keys: 3.4.3 @@ -2860,120 +2865,120 @@ snapshots: optionalDependencies: rollup: 4.22.4 - '@rollup/pluginutils@5.1.0(rollup@4.39.0)': + '@rollup/pluginutils@5.1.0(rollup@4.40.2)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 2.3.1 optionalDependencies: - rollup: 4.39.0 + rollup: 4.40.2 '@rollup/rollup-android-arm-eabi@4.22.4': optional: true - '@rollup/rollup-android-arm-eabi@4.39.0': + '@rollup/rollup-android-arm-eabi@4.40.2': optional: true '@rollup/rollup-android-arm64@4.22.4': optional: true - '@rollup/rollup-android-arm64@4.39.0': + '@rollup/rollup-android-arm64@4.40.2': optional: true '@rollup/rollup-darwin-arm64@4.22.4': optional: true - '@rollup/rollup-darwin-arm64@4.39.0': + '@rollup/rollup-darwin-arm64@4.40.2': optional: true '@rollup/rollup-darwin-x64@4.22.4': optional: true - '@rollup/rollup-darwin-x64@4.39.0': + '@rollup/rollup-darwin-x64@4.40.2': optional: true - '@rollup/rollup-freebsd-arm64@4.39.0': + '@rollup/rollup-freebsd-arm64@4.40.2': optional: true - '@rollup/rollup-freebsd-x64@4.39.0': + '@rollup/rollup-freebsd-x64@4.40.2': optional: true '@rollup/rollup-linux-arm-gnueabihf@4.22.4': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.39.0': + '@rollup/rollup-linux-arm-gnueabihf@4.40.2': optional: true '@rollup/rollup-linux-arm-musleabihf@4.22.4': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.39.0': + '@rollup/rollup-linux-arm-musleabihf@4.40.2': optional: true '@rollup/rollup-linux-arm64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-arm64-gnu@4.39.0': + '@rollup/rollup-linux-arm64-gnu@4.40.2': optional: true '@rollup/rollup-linux-arm64-musl@4.22.4': optional: true - '@rollup/rollup-linux-arm64-musl@4.39.0': + '@rollup/rollup-linux-arm64-musl@4.40.2': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.39.0': + '@rollup/rollup-linux-loongarch64-gnu@4.40.2': optional: true '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.39.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.40.2': optional: true '@rollup/rollup-linux-riscv64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.39.0': + '@rollup/rollup-linux-riscv64-gnu@4.40.2': optional: true - '@rollup/rollup-linux-riscv64-musl@4.39.0': + '@rollup/rollup-linux-riscv64-musl@4.40.2': optional: true '@rollup/rollup-linux-s390x-gnu@4.22.4': optional: true - '@rollup/rollup-linux-s390x-gnu@4.39.0': + '@rollup/rollup-linux-s390x-gnu@4.40.2': optional: true '@rollup/rollup-linux-x64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-x64-gnu@4.39.0': + '@rollup/rollup-linux-x64-gnu@4.40.2': optional: true '@rollup/rollup-linux-x64-musl@4.22.4': optional: true - '@rollup/rollup-linux-x64-musl@4.39.0': + '@rollup/rollup-linux-x64-musl@4.40.2': optional: true '@rollup/rollup-win32-arm64-msvc@4.22.4': optional: true - '@rollup/rollup-win32-arm64-msvc@4.39.0': + '@rollup/rollup-win32-arm64-msvc@4.40.2': optional: true '@rollup/rollup-win32-ia32-msvc@4.22.4': optional: true - '@rollup/rollup-win32-ia32-msvc@4.39.0': + '@rollup/rollup-win32-ia32-msvc@4.40.2': optional: true '@rollup/rollup-win32-x64-msvc@4.22.4': optional: true - '@rollup/rollup-win32-x64-msvc@4.39.0': + '@rollup/rollup-win32-x64-msvc@4.40.2': optional: true '@stylistic/eslint-plugin-js@1.8.0(eslint@9.9.1)': @@ -3000,25 +3005,25 @@ snapshots: typescript: 5.5.4 typescript-eslint: 8.26.0(eslint@9.9.1)(typescript@5.5.4) - '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@sveltejs/vite-plugin-svelte-inspector@3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@sveltejs/vite-plugin-svelte': 4.0.0-next.6(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.4.0 svelte: link:packages/svelte - vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': + '@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + '@sveltejs/vite-plugin-svelte-inspector': 3.0.0-next.2(@sveltejs/vite-plugin-svelte@4.0.0-next.6(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)))(svelte@packages+svelte)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 svelte: link:packages/svelte - vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) - vitefu: 0.2.5(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) + vite: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vitefu: 0.2.5(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)) transitivePeerDependencies: - supports-color @@ -3088,10 +3093,10 @@ snapshots: '@typescript-eslint/types': 8.26.0 '@typescript-eslint/visitor-keys': 8.26.0 - '@typescript-eslint/scope-manager@8.29.1': + '@typescript-eslint/scope-manager@8.32.1': dependencies: - '@typescript-eslint/types': 8.29.1 - '@typescript-eslint/visitor-keys': 8.29.1 + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/visitor-keys': 8.32.1 '@typescript-eslint/type-utils@8.26.0(eslint@9.9.1)(typescript@5.5.4)': dependencies: @@ -3106,7 +3111,7 @@ snapshots: '@typescript-eslint/types@8.26.0': {} - '@typescript-eslint/types@8.29.1': {} + '@typescript-eslint/types@8.32.1': {} '@typescript-eslint/typescript-estree@8.26.0(typescript@5.5.4)': dependencies: @@ -3122,15 +3127,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.29.1(typescript@5.5.4)': + '@typescript-eslint/typescript-estree@8.32.1(typescript@5.5.4)': dependencies: - '@typescript-eslint/types': 8.29.1 - '@typescript-eslint/visitor-keys': 8.29.1 + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/visitor-keys': 8.32.1 debug: 4.4.0 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.7.1 + semver: 7.7.2 ts-api-utils: 2.1.0(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: @@ -3147,12 +3152,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.29.1(eslint@9.9.1)(typescript@5.5.4)': + '@typescript-eslint/utils@8.32.1(eslint@9.9.1)(typescript@5.5.4)': dependencies: - '@eslint-community/eslint-utils': 4.5.1(eslint@9.9.1) - '@typescript-eslint/scope-manager': 8.29.1 - '@typescript-eslint/types': 8.29.1 - '@typescript-eslint/typescript-estree': 8.29.1(typescript@5.5.4) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.9.1) + '@typescript-eslint/scope-manager': 8.32.1 + '@typescript-eslint/types': 8.32.1 + '@typescript-eslint/typescript-estree': 8.32.1(typescript@5.5.4) eslint: 9.9.1 typescript: 5.5.4 transitivePeerDependencies: @@ -3163,9 +3168,9 @@ snapshots: '@typescript-eslint/types': 8.26.0 eslint-visitor-keys: 4.2.0 - '@typescript-eslint/visitor-keys@8.29.1': + '@typescript-eslint/visitor-keys@8.32.1': dependencies: - '@typescript-eslint/types': 8.29.1 + '@typescript-eslint/types': 8.32.1 eslint-visitor-keys: 4.2.0 '@vitest/coverage-v8@2.0.5(vitest@2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0))': @@ -3491,7 +3496,7 @@ snapshots: eslint-compat-utils@0.5.1(eslint@9.9.1): dependencies: eslint: 9.9.1 - semver: 7.7.1 + semver: 7.7.2 eslint-config-prettier@9.1.0(eslint@9.9.1): dependencies: @@ -3499,7 +3504,7 @@ snapshots: eslint-plugin-es-x@7.8.0(eslint@9.9.1): dependencies: - '@eslint-community/eslint-utils': 4.5.1(eslint@9.9.1) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.9.1) '@eslint-community/regexpp': 4.12.1 eslint: 9.9.1 eslint-compat-utils: 0.5.1(eslint@9.9.1) @@ -3508,8 +3513,8 @@ snapshots: eslint-plugin-n@17.16.1(eslint@9.9.1)(typescript@5.5.4): dependencies: - '@eslint-community/eslint-utils': 4.5.1(eslint@9.9.1) - '@typescript-eslint/utils': 8.29.1(eslint@9.9.1)(typescript@5.5.4) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.9.1) + '@typescript-eslint/utils': 8.32.1(eslint@9.9.1)(typescript@5.5.4) enhanced-resolve: 5.18.1 eslint: 9.9.1 eslint-plugin-es-x: 7.8.0(eslint@9.9.1) @@ -3517,7 +3522,7 @@ snapshots: globals: 15.15.0 ignore: 5.3.2 minimatch: 9.0.5 - semver: 7.7.1 + semver: 7.7.2 ts-declaration-location: 1.0.7(typescript@5.5.4) transitivePeerDependencies: - supports-color @@ -3525,7 +3530,7 @@ snapshots: eslint-plugin-svelte@2.38.0(eslint@9.9.1)(svelte@packages+svelte): dependencies: - '@eslint-community/eslint-utils': 4.5.1(eslint@9.9.1) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.9.1) '@jridgewell/sourcemap-codec': 1.5.0 debug: 4.4.0 eslint: 9.9.1 @@ -3536,7 +3541,7 @@ snapshots: postcss-load-config: 3.1.4(postcss@8.5.3) postcss-safe-parser: 6.0.0(postcss@8.5.3) postcss-selector-parser: 6.1.2 - semver: 7.7.1 + semver: 7.7.2 svelte-eslint-parser: 0.43.0(svelte@packages+svelte) optionalDependencies: svelte: link:packages/svelte @@ -4292,30 +4297,30 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.22.4 fsevents: 2.3.3 - rollup@4.39.0: + rollup@4.40.2: dependencies: '@types/estree': 1.0.7 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.39.0 - '@rollup/rollup-android-arm64': 4.39.0 - '@rollup/rollup-darwin-arm64': 4.39.0 - '@rollup/rollup-darwin-x64': 4.39.0 - '@rollup/rollup-freebsd-arm64': 4.39.0 - '@rollup/rollup-freebsd-x64': 4.39.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.39.0 - '@rollup/rollup-linux-arm-musleabihf': 4.39.0 - '@rollup/rollup-linux-arm64-gnu': 4.39.0 - '@rollup/rollup-linux-arm64-musl': 4.39.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.39.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.39.0 - '@rollup/rollup-linux-riscv64-gnu': 4.39.0 - '@rollup/rollup-linux-riscv64-musl': 4.39.0 - '@rollup/rollup-linux-s390x-gnu': 4.39.0 - '@rollup/rollup-linux-x64-gnu': 4.39.0 - '@rollup/rollup-linux-x64-musl': 4.39.0 - '@rollup/rollup-win32-arm64-msvc': 4.39.0 - '@rollup/rollup-win32-ia32-msvc': 4.39.0 - '@rollup/rollup-win32-x64-msvc': 4.39.0 + '@rollup/rollup-android-arm-eabi': 4.40.2 + '@rollup/rollup-android-arm64': 4.40.2 + '@rollup/rollup-darwin-arm64': 4.40.2 + '@rollup/rollup-darwin-x64': 4.40.2 + '@rollup/rollup-freebsd-arm64': 4.40.2 + '@rollup/rollup-freebsd-x64': 4.40.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.40.2 + '@rollup/rollup-linux-arm-musleabihf': 4.40.2 + '@rollup/rollup-linux-arm64-gnu': 4.40.2 + '@rollup/rollup-linux-arm64-musl': 4.40.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.40.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.40.2 + '@rollup/rollup-linux-riscv64-gnu': 4.40.2 + '@rollup/rollup-linux-riscv64-musl': 4.40.2 + '@rollup/rollup-linux-s390x-gnu': 4.40.2 + '@rollup/rollup-linux-x64-gnu': 4.40.2 + '@rollup/rollup-linux-x64-musl': 4.40.2 + '@rollup/rollup-win32-arm64-msvc': 4.40.2 + '@rollup/rollup-win32-ia32-msvc': 4.40.2 + '@rollup/rollup-win32-x64-msvc': 4.40.2 fsevents: 2.3.3 rrweb-cssom@0.7.1: {} @@ -4347,6 +4352,8 @@ snapshots: semver@7.7.1: {} + semver@7.7.2: {} + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -4562,7 +4569,7 @@ snapshots: debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 1.1.2 - vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - '@types/node' - less @@ -4574,10 +4581,10 @@ snapshots: - supports-color - terser - vite-plugin-inspect@0.8.4(rollup@4.39.0)(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vite-plugin-inspect@0.8.4(rollup@4.40.2)(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): dependencies: '@antfu/utils': 0.7.8 - '@rollup/pluginutils': 5.1.0(rollup@4.39.0) + '@rollup/pluginutils': 5.1.0(rollup@4.40.2) debug: 4.4.0 error-stack-parser-es: 0.1.1 fs-extra: 11.2.0 @@ -4585,7 +4592,7 @@ snapshots: perfect-debounce: 1.0.0 picocolors: 1.1.1 sirv: 2.0.4 - vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) transitivePeerDependencies: - rollup - supports-color @@ -4602,11 +4609,11 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): + vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: esbuild: 0.21.5 postcss: 8.5.3 - rollup: 4.39.0 + rollup: 4.40.2 optionalDependencies: '@types/node': 20.12.7 fsevents: 2.3.3 @@ -4614,9 +4621,9 @@ snapshots: sass: 1.70.0 terser: 5.27.0 - vitefu@0.2.5(vite@5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): + vitefu@0.2.5(vite@5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0)): optionalDependencies: - vite: 5.4.18(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) + vite: 5.4.19(@types/node@20.12.7)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0) vitest@2.1.9(@types/node@20.12.7)(jsdom@25.0.1)(lightningcss@1.23.0)(sass@1.70.0)(terser@5.27.0): dependencies: From df03af2e9d6e53be6cfb72257c42b0c9e5e2c050 Mon Sep 17 00:00:00 2001 From: 7nik Date: Mon, 12 May 2025 23:15:03 +0300 Subject: [PATCH 0382/1429] fix: emit right error for a shadowed invalid rune (#15892) Co-authored-by: 7nik --- .changeset/tall-cherries-fix.md | 5 +++++ .../src/compiler/phases/2-analyze/visitors/Identifier.js | 2 +- .../samples/invalid-rune-name-shadowed/_config.js | 8 ++++++++ .../samples/invalid-rune-name-shadowed/main.svelte.js | 5 +++++ 4 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 .changeset/tall-cherries-fix.md create mode 100644 packages/svelte/tests/compiler-errors/samples/invalid-rune-name-shadowed/_config.js create mode 100644 packages/svelte/tests/compiler-errors/samples/invalid-rune-name-shadowed/main.svelte.js diff --git a/.changeset/tall-cherries-fix.md b/.changeset/tall-cherries-fix.md new file mode 100644 index 0000000000..df25e02da9 --- /dev/null +++ b/.changeset/tall-cherries-fix.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: emit right error for a shadowed invalid rune diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js index efbbe6cfa2..89a81127c1 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Identifier.js @@ -39,7 +39,7 @@ export function Identifier(node, context) { if ( is_rune(node.name) && context.state.scope.get(node.name) === null && - context.state.scope.get(node.name.slice(1)) === null + context.state.scope.get(node.name.slice(1))?.kind !== 'store_sub' ) { /** @type {Expression} */ let current = node; diff --git a/packages/svelte/tests/compiler-errors/samples/invalid-rune-name-shadowed/_config.js b/packages/svelte/tests/compiler-errors/samples/invalid-rune-name-shadowed/_config.js new file mode 100644 index 0000000000..a24996677c --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/invalid-rune-name-shadowed/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + error: { + code: 'rune_invalid_name', + message: '`$state.foo` is not a valid rune' + } +}); diff --git a/packages/svelte/tests/compiler-errors/samples/invalid-rune-name-shadowed/main.svelte.js b/packages/svelte/tests/compiler-errors/samples/invalid-rune-name-shadowed/main.svelte.js new file mode 100644 index 0000000000..966f7bc63b --- /dev/null +++ b/packages/svelte/tests/compiler-errors/samples/invalid-rune-name-shadowed/main.svelte.js @@ -0,0 +1,5 @@ +class State { + value = $state.foo(); +} + +export const state = new State(); From eebb940b31285f300edd8a4a8c05aeab6a7b3d6e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 12 May 2025 17:07:06 -0400 Subject: [PATCH 0383/1429] fix: prevent invalid BigInt calls from blowing up at compile time (#15900) --- .changeset/little-worms-wonder.md | 5 +++++ packages/svelte/src/compiler/phases/scope.js | 2 +- .../tests/runtime-runes/samples/bigint-invalid/_config.js | 7 +++++++ .../tests/runtime-runes/samples/bigint-invalid/main.svelte | 5 +++++ 4 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 .changeset/little-worms-wonder.md create mode 100644 packages/svelte/tests/runtime-runes/samples/bigint-invalid/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/bigint-invalid/main.svelte diff --git a/.changeset/little-worms-wonder.md b/.changeset/little-worms-wonder.md new file mode 100644 index 0000000000..5482d7d5ce --- /dev/null +++ b/.changeset/little-worms-wonder.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: prevent invalid BigInt calls from blowing up at compile time diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 8297f174d3..752bdac9d8 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -23,7 +23,7 @@ export const STRING = Symbol('string'); /** @type {Record} */ const globals = { - BigInt: [NUMBER, BigInt], + BigInt: [NUMBER], 'Math.min': [NUMBER, Math.min], 'Math.max': [NUMBER, Math.max], 'Math.random': [NUMBER], diff --git a/packages/svelte/tests/runtime-runes/samples/bigint-invalid/_config.js b/packages/svelte/tests/runtime-runes/samples/bigint-invalid/_config.js new file mode 100644 index 0000000000..810ac338a5 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/bigint-invalid/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + // check that this is a runtime error, not a compile time error + // caused by over-eager partial-evaluation + error: 'Cannot convert invalid to a BigInt' +}); diff --git a/packages/svelte/tests/runtime-runes/samples/bigint-invalid/main.svelte b/packages/svelte/tests/runtime-runes/samples/bigint-invalid/main.svelte new file mode 100644 index 0000000000..126528bad2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/bigint-invalid/main.svelte @@ -0,0 +1,5 @@ + + +

{invalid}

From 8a351944a3445a10ea4a035edf5255b3701e2c30 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 12 May 2025 17:07:46 -0400 Subject: [PATCH 0384/1429] chore: tweak flushSync implementation (#15898) --- packages/svelte/src/internal/client/runtime.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js index 2375dc0a63..d790c0ad14 100644 --- a/packages/svelte/src/internal/client/runtime.js +++ b/packages/svelte/src/internal/client/runtime.js @@ -828,15 +828,16 @@ export function flushSync(fn) { result = fn(); } - flush_tasks(); + while (true) { + flush_tasks(); + + if (queued_root_effects.length === 0) { + return /** @type {T} */ (result); + } - while (queued_root_effects.length > 0) { is_flushing = true; flush_queued_root_effects(); - flush_tasks(); } - - return /** @type {T} */ (result); } /** From 895337cf4786fea4131d6fd2fdc79c6369a2a088 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 12 May 2025 17:12:48 -0400 Subject: [PATCH 0385/1429] fix: treat nullish expression as empty string (#15901) * fix: treat nullish expression as empty string * fix --- .changeset/blue-badgers-play.md | 5 +++++ .../phases/3-transform/client/visitors/shared/utils.js | 4 ++-- .../runtime-runes/samples/nullish-empty-string/_config.js | 5 +++++ .../runtime-runes/samples/nullish-empty-string/main.svelte | 1 + .../snapshot/samples/purity/_expected/client/index.svelte.js | 2 +- 5 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 .changeset/blue-badgers-play.md create mode 100644 packages/svelte/tests/runtime-runes/samples/nullish-empty-string/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/nullish-empty-string/main.svelte diff --git a/.changeset/blue-badgers-play.md b/.changeset/blue-badgers-play.md new file mode 100644 index 0000000000..e07d393568 --- /dev/null +++ b/.changeset/blue-badgers-play.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: treat nullish expression as empty string diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index bc79b76043..fb3f8df74b 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -77,7 +77,7 @@ export function build_template_chunk( // If we have a single expression, then pass that in directly to possibly avoid doing // extra work in the template_effect (instead we do the work in set_text). if (evaluated.is_known) { - value = b.literal(evaluated.value); + value = b.literal((evaluated.value ?? '') + ''); } return { value, has_state }; @@ -96,7 +96,7 @@ export function build_template_chunk( } if (evaluated.is_known) { - quasi.value.cooked += evaluated.value + ''; + quasi.value.cooked += (evaluated.value ?? '') + ''; } else { if (!evaluated.is_defined) { // add `?? ''` where necessary diff --git a/packages/svelte/tests/runtime-runes/samples/nullish-empty-string/_config.js b/packages/svelte/tests/runtime-runes/samples/nullish-empty-string/_config.js new file mode 100644 index 0000000000..84e97e9735 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/nullish-empty-string/_config.js @@ -0,0 +1,5 @@ +import { test } from '../../test'; + +export default test({ + html: '[]' +}); diff --git a/packages/svelte/tests/runtime-runes/samples/nullish-empty-string/main.svelte b/packages/svelte/tests/runtime-runes/samples/nullish-empty-string/main.svelte new file mode 100644 index 0000000000..efe39b91fe --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/nullish-empty-string/main.svelte @@ -0,0 +1 @@ +[{undefined ?? null}] diff --git a/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js index 5bc9766acf..f661dbc01d 100644 --- a/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js @@ -8,7 +8,7 @@ export default function Purity($$anchor) { var fragment = root(); var p = $.first_child(fragment); - p.textContent = 0; + p.textContent = '0'; var p_1 = $.sibling(p, 2); From 92cdeadadfa85ac4d70f8649440655e6bb902dbd Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 12 May 2025 18:52:51 -0400 Subject: [PATCH 0386/1429] chore: ts-ignore Math.f16round (#15902) --- packages/svelte/src/compiler/phases/scope.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 752bdac9d8..75a26d487b 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -28,7 +28,7 @@ const globals = { 'Math.max': [NUMBER, Math.max], 'Math.random': [NUMBER], 'Math.floor': [NUMBER, Math.floor], - // @ts-expect-error + // @ts-ignore 'Math.f16round': [NUMBER, Math.f16round], 'Math.round': [NUMBER, Math.round], 'Math.abs': [NUMBER, Math.abs], From aa041a9e65cffc551f70a1499f51c223ef4c2289 Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Mon, 12 May 2025 16:27:55 -0700 Subject: [PATCH 0387/1429] fix: warn on bidirectional control characters, fix various issues with template expressions (#15893) * fix: warn on bidirectional control characters * check evaluated values as well, fix minor issue * fix failing tests * lint * fix * shrink warning code * use validator test suite rather than snapshot (which should be used sparingly as it creates more git noise) * show ranges during parsing, and warn on all occurrences rather than just the first * fix lint * move check into Text visitor so it happens in expected order * unused * add svelte-ignore test * ignore control characters following a svelte-ignore comment * tweak message * no need to test evaluations, since we are already testing the literals that they are composed of --------- Co-authored-by: Rich Harris --- .changeset/rare-crews-collect.md | 5 ++ .../.generated/compile-warnings.md | 8 +++ .../svelte/messages/compile-warnings/misc.md | 6 +++ .../src/compiler/phases/2-analyze/index.js | 4 ++ .../phases/2-analyze/visitors/Literal.js | 14 ++++++ .../2-analyze/visitors/TemplateElement.js | 12 +++++ .../phases/2-analyze/visitors/Text.js | 38 ++++++++++++-- .../svelte/src/compiler/phases/patterns.js | 2 + packages/svelte/src/compiler/warnings.js | 9 ++++ .../input.svelte | 8 +++ .../warnings.json | 50 +++++++++++++++++++ 11 files changed, 153 insertions(+), 3 deletions(-) create mode 100644 .changeset/rare-crews-collect.md create mode 100644 packages/svelte/src/compiler/phases/2-analyze/visitors/Literal.js create mode 100644 packages/svelte/src/compiler/phases/2-analyze/visitors/TemplateElement.js create mode 100644 packages/svelte/tests/validator/samples/bidirectional-control-characters/input.svelte create mode 100644 packages/svelte/tests/validator/samples/bidirectional-control-characters/warnings.json diff --git a/.changeset/rare-crews-collect.md b/.changeset/rare-crews-collect.md new file mode 100644 index 0000000000..a4ffe09324 --- /dev/null +++ b/.changeset/rare-crews-collect.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: warn on bidirectional control characters diff --git a/documentation/docs/98-reference/.generated/compile-warnings.md b/documentation/docs/98-reference/.generated/compile-warnings.md index 0e94cbadb2..7069f90206 100644 --- a/documentation/docs/98-reference/.generated/compile-warnings.md +++ b/documentation/docs/98-reference/.generated/compile-warnings.md @@ -586,6 +586,14 @@ Attributes should not contain ':' characters to prevent ambiguity with Svelte di Quoted attributes on components and custom elements will be stringified in a future version of Svelte. If this isn't what you want, remove the quotes ``` +### bidirectional_control_characters + +``` +A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences +``` + +Bidirectional control characters can alter the direction in which text appears to be in. For example, via control characters, you can make `defabc` look like `abcdef`. As a result, if you were to unknowingly copy and paste some code that has these control characters, they may alter the behavior of your code in ways you did not intend. See [trojansource.codes](https://trojansource.codes/) for more information. + ### bind_invalid_each_rest ``` diff --git a/packages/svelte/messages/compile-warnings/misc.md b/packages/svelte/messages/compile-warnings/misc.md index 3b977db1be..29343dd28a 100644 --- a/packages/svelte/messages/compile-warnings/misc.md +++ b/packages/svelte/messages/compile-warnings/misc.md @@ -1,3 +1,9 @@ +## bidirectional_control_characters + +> A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences + +Bidirectional control characters can alter the direction in which text appears to be in. For example, via control characters, you can make `defabc` look like `abcdef`. As a result, if you were to unknowingly copy and paste some code that has these control characters, they may alter the behavior of your code in ways you did not intend. See [trojansource.codes](https://trojansource.codes/) for more information. + ## legacy_code > `%code%` is no longer valid — please use `%suggestion%` instead diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 2e36a89649..766b317d06 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -43,6 +43,7 @@ import { ImportDeclaration } from './visitors/ImportDeclaration.js'; import { KeyBlock } from './visitors/KeyBlock.js'; import { LabeledStatement } from './visitors/LabeledStatement.js'; import { LetDirective } from './visitors/LetDirective.js'; +import { Literal } from './visitors/Literal.js'; import { MemberExpression } from './visitors/MemberExpression.js'; import { NewExpression } from './visitors/NewExpression.js'; import { OnDirective } from './visitors/OnDirective.js'; @@ -63,6 +64,7 @@ import { SvelteSelf } from './visitors/SvelteSelf.js'; import { SvelteWindow } from './visitors/SvelteWindow.js'; import { SvelteBoundary } from './visitors/SvelteBoundary.js'; import { TaggedTemplateExpression } from './visitors/TaggedTemplateExpression.js'; +import { TemplateElement } from './visitors/TemplateElement.js'; import { Text } from './visitors/Text.js'; import { TitleElement } from './visitors/TitleElement.js'; import { TransitionDirective } from './visitors/TransitionDirective.js'; @@ -156,6 +158,7 @@ const visitors = { KeyBlock, LabeledStatement, LetDirective, + Literal, MemberExpression, NewExpression, OnDirective, @@ -176,6 +179,7 @@ const visitors = { SvelteWindow, SvelteBoundary, TaggedTemplateExpression, + TemplateElement, Text, TransitionDirective, TitleElement, diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Literal.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Literal.js new file mode 100644 index 0000000000..58684ba71c --- /dev/null +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Literal.js @@ -0,0 +1,14 @@ +/** @import { Literal } from 'estree' */ +import * as w from '../../../warnings.js'; +import { regex_bidirectional_control_characters } from '../../patterns.js'; + +/** + * @param {Literal} node + */ +export function Literal(node) { + if (typeof node.value === 'string') { + if (regex_bidirectional_control_characters.test(node.value)) { + w.bidirectional_control_characters(node); + } + } +} diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/TemplateElement.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/TemplateElement.js new file mode 100644 index 0000000000..978042bbc5 --- /dev/null +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/TemplateElement.js @@ -0,0 +1,12 @@ +/** @import { TemplateElement } from 'estree' */ +import * as w from '../../../warnings.js'; +import { regex_bidirectional_control_characters } from '../../patterns.js'; + +/** + * @param {TemplateElement} node + */ +export function TemplateElement(node) { + if (regex_bidirectional_control_characters.test(node.value.cooked ?? '')) { + w.bidirectional_control_characters(node); + } +} diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Text.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Text.js index 363a111b7d..a03421e8dd 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Text.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Text.js @@ -1,20 +1,52 @@ /** @import { AST } from '#compiler' */ /** @import { Context } from '../types' */ import { is_tag_valid_with_parent } from '../../../../html-tree-validation.js'; -import { regex_not_whitespace } from '../../patterns.js'; +import { regex_bidirectional_control_characters, regex_not_whitespace } from '../../patterns.js'; import * as e from '../../../errors.js'; +import * as w from '../../../warnings.js'; +import { extract_svelte_ignore } from '../../../utils/extract_svelte_ignore.js'; /** * @param {AST.Text} node * @param {Context} context */ export function Text(node, context) { - const in_template = context.path.at(-1)?.type === 'Fragment'; + const parent = /** @type {AST.SvelteNode} */ (context.path.at(-1)); - if (in_template && context.state.parent_element && regex_not_whitespace.test(node.data)) { + if ( + parent.type === 'Fragment' && + context.state.parent_element && + regex_not_whitespace.test(node.data) + ) { const message = is_tag_valid_with_parent('#text', context.state.parent_element); if (message) { e.node_invalid_placement(node, message); } } + + regex_bidirectional_control_characters.lastIndex = 0; + for (const match of node.data.matchAll(regex_bidirectional_control_characters)) { + let is_ignored = false; + + // if we have a svelte-ignore comment earlier in the text, bail + // (otherwise we can only use svelte-ignore on parent elements/blocks) + if (parent.type === 'Fragment') { + for (const child of parent.nodes) { + if (child === node) break; + + if (child.type === 'Comment') { + is_ignored ||= extract_svelte_ignore( + child.start + 4, + child.data, + context.state.analysis.runes + ).includes('bidirectional_control_characters'); + } + } + } + + if (!is_ignored) { + let start = match.index + node.start; + w.bidirectional_control_characters({ start, end: start + match[0].length }); + } + } } diff --git a/packages/svelte/src/compiler/phases/patterns.js b/packages/svelte/src/compiler/phases/patterns.js index bda299de9e..2bee717131 100644 --- a/packages/svelte/src/compiler/phases/patterns.js +++ b/packages/svelte/src/compiler/phases/patterns.js @@ -21,3 +21,5 @@ export const regex_invalid_identifier_chars = /(^[^a-zA-Z_$]|[^a-zA-Z0-9_$])/g; export const regex_starts_with_vowel = /^[aeiou]/; export const regex_heading_tags = /^h[1-6]$/; export const regex_illegal_attribute_character = /(^[0-9-.])|[\^$@%&#?!|()[\]{}^*+~;]/; +export const regex_bidirectional_control_characters = + /[\u202a\u202b\u202c\u202d\u202e\u2066\u2067\u2068\u2069]+/g; diff --git a/packages/svelte/src/compiler/warnings.js b/packages/svelte/src/compiler/warnings.js index e6fc8caba5..c281433213 100644 --- a/packages/svelte/src/compiler/warnings.js +++ b/packages/svelte/src/compiler/warnings.js @@ -86,6 +86,7 @@ export const codes = [ 'a11y_role_supports_aria_props_implicit', 'a11y_unknown_aria_attribute', 'a11y_unknown_role', + 'bidirectional_control_characters', 'legacy_code', 'unknown_code', 'options_deprecated_accessors', @@ -506,6 +507,14 @@ export function a11y_unknown_role(node, role, suggestion) { w(node, 'a11y_unknown_role', `${suggestion ? `Unknown role '${role}'. Did you mean '${suggestion}'?` : `Unknown role '${role}'`}\nhttps://svelte.dev/e/a11y_unknown_role`); } +/** + * A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences + * @param {null | NodeLike} node + */ +export function bidirectional_control_characters(node) { + w(node, 'bidirectional_control_characters', `A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences\nhttps://svelte.dev/e/bidirectional_control_characters`); +} + /** * `%code%` is no longer valid — please use `%suggestion%` instead * @param {null | NodeLike} node diff --git a/packages/svelte/tests/validator/samples/bidirectional-control-characters/input.svelte b/packages/svelte/tests/validator/samples/bidirectional-control-characters/input.svelte new file mode 100644 index 0000000000..21587e5f4f --- /dev/null +++ b/packages/svelte/tests/validator/samples/bidirectional-control-characters/input.svelte @@ -0,0 +1,8 @@ + +⁧⁦def⁩⁦abc⁩⁩ +

Hello, {name}!

+ + +⁧⁦def⁩⁦abc⁩⁩ diff --git a/packages/svelte/tests/validator/samples/bidirectional-control-characters/warnings.json b/packages/svelte/tests/validator/samples/bidirectional-control-characters/warnings.json new file mode 100644 index 0000000000..6e70193c6c --- /dev/null +++ b/packages/svelte/tests/validator/samples/bidirectional-control-characters/warnings.json @@ -0,0 +1,50 @@ +[ + { + "code": "bidirectional_control_characters", + "message": "A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences", + "start": { + "line": 2, + "column": 15 + }, + "end": { + "line": 2, + "column": 58 + } + }, + { + "code": "bidirectional_control_characters", + "message": "A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences", + "start": { + "line": 4, + "column": 0 + }, + "end": { + "line": 4, + "column": 2 + } + }, + { + "code": "bidirectional_control_characters", + "message": "A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences", + "start": { + "line": 4, + "column": 5 + }, + "end": { + "line": 4, + "column": 7 + } + }, + { + "code": "bidirectional_control_characters", + "message": "A bidirectional control character was detected in your code. These characters can be used to alter the visual direction of your code and could have unintended consequences", + "start": { + "line": 4, + "column": 10 + }, + "end": { + "line": 4, + "column": 12 + } + } +] From f2796dda0e4dc1e3677bc87cf434ac8c2163c4c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 19:51:16 -0400 Subject: [PATCH 0388/1429] Version Packages (#15899) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/blue-badgers-play.md | 5 ----- .changeset/little-worms-wonder.md | 5 ----- .changeset/rare-crews-collect.md | 5 ----- .changeset/tall-cherries-fix.md | 5 ----- packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 7 files changed, 14 insertions(+), 22 deletions(-) delete mode 100644 .changeset/blue-badgers-play.md delete mode 100644 .changeset/little-worms-wonder.md delete mode 100644 .changeset/rare-crews-collect.md delete mode 100644 .changeset/tall-cherries-fix.md diff --git a/.changeset/blue-badgers-play.md b/.changeset/blue-badgers-play.md deleted file mode 100644 index e07d393568..0000000000 --- a/.changeset/blue-badgers-play.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: treat nullish expression as empty string diff --git a/.changeset/little-worms-wonder.md b/.changeset/little-worms-wonder.md deleted file mode 100644 index 5482d7d5ce..0000000000 --- a/.changeset/little-worms-wonder.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: prevent invalid BigInt calls from blowing up at compile time diff --git a/.changeset/rare-crews-collect.md b/.changeset/rare-crews-collect.md deleted file mode 100644 index a4ffe09324..0000000000 --- a/.changeset/rare-crews-collect.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: warn on bidirectional control characters diff --git a/.changeset/tall-cherries-fix.md b/.changeset/tall-cherries-fix.md deleted file mode 100644 index df25e02da9..0000000000 --- a/.changeset/tall-cherries-fix.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: emit right error for a shadowed invalid rune diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 8650ba0e5b..95378823f5 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.28.4 + +### Patch Changes + +- fix: treat nullish expression as empty string ([#15901](https://github.com/sveltejs/svelte/pull/15901)) + +- fix: prevent invalid BigInt calls from blowing up at compile time ([#15900](https://github.com/sveltejs/svelte/pull/15900)) + +- fix: warn on bidirectional control characters ([#15893](https://github.com/sveltejs/svelte/pull/15893)) + +- fix: emit right error for a shadowed invalid rune ([#15892](https://github.com/sveltejs/svelte/pull/15892)) + ## 5.28.3 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 85eef9c2eb..92d44ec155 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.28.3", + "version": "5.28.4", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b072c6432c..d933675d36 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.28.3'; +export const VERSION = '5.28.4'; export const PUBLIC_VERSION = '5'; From 326b32932cf81ba01005b2757268321460f477cb Mon Sep 17 00:00:00 2001 From: 7nik Date: Tue, 13 May 2025 03:04:48 +0300 Subject: [PATCH 0389/1429] fix: more frequently update `bind:buffered` to actual value (#15874) * fix: more frequently update `bind:buffered` to actual value * small tweak --------- Co-authored-by: 7nik Co-authored-by: Rich Harris --- .changeset/short-impalas-exist.md | 5 +++++ .../client/dom/elements/bindings/media.js | 18 +++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 .changeset/short-impalas-exist.md diff --git a/.changeset/short-impalas-exist.md b/.changeset/short-impalas-exist.md new file mode 100644 index 0000000000..a1f9120c8f --- /dev/null +++ b/.changeset/short-impalas-exist.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: more frequently update `bind:buffered` to actual value diff --git a/packages/svelte/src/internal/client/dom/elements/bindings/media.js b/packages/svelte/src/internal/client/dom/elements/bindings/media.js index 30a8dac1af..f96ee05989 100644 --- a/packages/svelte/src/internal/client/dom/elements/bindings/media.js +++ b/packages/svelte/src/internal/client/dom/elements/bindings/media.js @@ -62,7 +62,23 @@ export function bind_current_time(media, get, set = get) { * @param {(array: Array<{ start: number; end: number }>) => void} set */ export function bind_buffered(media, set) { - listen(media, ['loadedmetadata', 'progress'], () => set(time_ranges_to_array(media.buffered))); + /** @type {{ start: number; end: number; }[]} */ + var current; + + // `buffered` can update without emitting any event, so we check it on various events. + // By specs, `buffered` always returns a new object, so we have to compare deeply. + listen(media, ['loadedmetadata', 'progress', 'timeupdate', 'seeking'], () => { + var ranges = media.buffered; + + if ( + !current || + current.length !== ranges.length || + current.some((range, i) => ranges.start(i) !== range.start || ranges.end(i) !== range.end) + ) { + current = time_ranges_to_array(ranges); + set(current); + } + }); } /** From 3bb1af9677ad145a5a048311090b3bada54aef12 Mon Sep 17 00:00:00 2001 From: 7nik Date: Tue, 13 May 2025 03:05:15 +0300 Subject: [PATCH 0390/1429] fix: proxify the value in assignment shorthands to the private field (#15862) Co-authored-by: 7nik --- .changeset/shaggy-toys-arrive.md | 5 ++++ packages/svelte/src/compiler/utils/ast.js | 4 ++- .../_config.js | 20 +++++++++++++ .../main.svelte | 28 +++++++++++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 .changeset/shaggy-toys-arrive.md create mode 100644 packages/svelte/tests/runtime-runes/samples/class-private-fields-assignment-shorthand/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/class-private-fields-assignment-shorthand/main.svelte diff --git a/.changeset/shaggy-toys-arrive.md b/.changeset/shaggy-toys-arrive.md new file mode 100644 index 0000000000..440fa042c9 --- /dev/null +++ b/.changeset/shaggy-toys-arrive.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: proxify the value in assignment shorthands to the private field diff --git a/packages/svelte/src/compiler/utils/ast.js b/packages/svelte/src/compiler/utils/ast.js index 108f4eff64..23a95a1026 100644 --- a/packages/svelte/src/compiler/utils/ast.js +++ b/packages/svelte/src/compiler/utils/ast.js @@ -580,5 +580,7 @@ export function build_assignment_value(operator, left, right) { return operator === '=' ? right : // turn something like x += 1 into x = x + 1 - b.binary(/** @type {ESTree.BinaryOperator} */ (operator.slice(0, -1)), left, right); + ['||=', '&&=', '??='].includes(operator) + ? b.logical(/** @type {ESTree.LogicalOperator} */ (operator.slice(0, -1)), left, right) + : b.binary(/** @type {ESTree.BinaryOperator} */ (operator.slice(0, -1)), left, right); } diff --git a/packages/svelte/tests/runtime-runes/samples/class-private-fields-assignment-shorthand/_config.js b/packages/svelte/tests/runtime-runes/samples/class-private-fields-assignment-shorthand/_config.js new file mode 100644 index 0000000000..0fdeabfe0b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-private-fields-assignment-shorthand/_config.js @@ -0,0 +1,20 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + async test({ assert, target }) { + const btn = target.querySelector('button'); + + btn?.click(); + flushSync(); + assert.htmlEqual( + target.innerHTML, + ` + +

a:1

+

b:2

+

c:3

+ ` + ); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-private-fields-assignment-shorthand/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-private-fields-assignment-shorthand/main.svelte new file mode 100644 index 0000000000..746f22b1e6 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-private-fields-assignment-shorthand/main.svelte @@ -0,0 +1,28 @@ + + + + +{#key 1}

a:{counter.a}

{/key} +{#key 2}

b:{counter.b}

{/key} +{#key 3}

c:{counter.c}

{/key} From 77bdf249ffcd6b08765d3bf05da577d1223f0366 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 20:29:01 -0400 Subject: [PATCH 0391/1429] Version Packages (#15903) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/shaggy-toys-arrive.md | 5 ----- .changeset/short-impalas-exist.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/shaggy-toys-arrive.md delete mode 100644 .changeset/short-impalas-exist.md diff --git a/.changeset/shaggy-toys-arrive.md b/.changeset/shaggy-toys-arrive.md deleted file mode 100644 index 440fa042c9..0000000000 --- a/.changeset/shaggy-toys-arrive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: proxify the value in assignment shorthands to the private field diff --git a/.changeset/short-impalas-exist.md b/.changeset/short-impalas-exist.md deleted file mode 100644 index a1f9120c8f..0000000000 --- a/.changeset/short-impalas-exist.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: more frequently update `bind:buffered` to actual value diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 95378823f5..4ea42a3da2 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.28.5 + +### Patch Changes + +- fix: proxify the value in assignment shorthands to the private field ([#15862](https://github.com/sveltejs/svelte/pull/15862)) + +- fix: more frequently update `bind:buffered` to actual value ([#15874](https://github.com/sveltejs/svelte/pull/15874)) + ## 5.28.4 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 92d44ec155..c3255e30cd 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.28.4", + "version": "5.28.5", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index d933675d36..3ba616e962 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.28.4'; +export const VERSION = '5.28.5'; export const PUBLIC_VERSION = '5'; From 2bc2ae0dddab74eecff606790e1ef01ea281d6ea Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 13 May 2025 02:57:23 +0200 Subject: [PATCH 0392/1429] fix: use `transform.read` for `ownership_validator.mutation` array (#15848) --- .changeset/gorgeous-experts-perform.md | 5 +++++ .../phases/3-transform/client/visitors/shared/utils.js | 7 +++++-- .../_config.js | 10 ++++++++++ .../main.svelte | 5 +++++ 4 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 .changeset/gorgeous-experts-perform.md create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-use-transform/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-use-transform/main.svelte diff --git a/.changeset/gorgeous-experts-perform.md b/.changeset/gorgeous-experts-perform.md new file mode 100644 index 0000000000..9c4673a341 --- /dev/null +++ b/.changeset/gorgeous-experts-perform.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: use `transform.read` for `ownership_validator.mutation` array diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js index fb3f8df74b..80b472ac37 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js @@ -324,15 +324,18 @@ export function validate_mutation(node, context, expression) { const state = /** @type {ComponentClientTransformState} */ (context.state); state.analysis.needs_mutation_validation = true; - /** @type {Array} */ + /** @type {Array} */ const path = []; while (left.type === 'MemberExpression') { if (left.property.type === 'Literal') { path.unshift(left.property); } else if (left.property.type === 'Identifier') { + const transform = Object.hasOwn(context.state.transform, left.property.name) + ? context.state.transform[left.property.name] + : null; if (left.computed) { - path.unshift(left.property); + path.unshift(transform?.read ? transform.read(left.property) : left.property); } else { path.unshift(b.literal(left.property.name)); } diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-use-transform/_config.js b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-use-transform/_config.js new file mode 100644 index 0000000000..95556f4737 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-use-transform/_config.js @@ -0,0 +1,10 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + async test({ assert, errors }) { + assert.deepEqual(errors, []); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-use-transform/main.svelte b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-use-transform/main.svelte new file mode 100644 index 0000000000..d834551221 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/ownership-invalid-mutation-use-transform/main.svelte @@ -0,0 +1,5 @@ + + From 944ef1dbcecf6277e42d3da9ca60dcbe2929aa38 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 13 May 2025 03:00:11 +0200 Subject: [PATCH 0393/1429] fix: don't spread away `$$slots` from `$props` is it's used (#15849) * fix: don't spread away `$$slots` from `$props` is it's used * beef up test * fix * Update .changeset/ten-plants-carry.md --------- Co-authored-by: Rich Harris --- .changeset/ten-plants-carry.md | 5 +++++ .../server/visitors/VariableDeclaration.js | 11 +++++++++-- .../samples/props-and-slots/Child.svelte | 9 +++++++++ .../runtime-runes/samples/props-and-slots/_config.js | 8 ++++++++ .../runtime-runes/samples/props-and-slots/main.svelte | 7 +++++++ 5 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 .changeset/ten-plants-carry.md create mode 100644 packages/svelte/tests/runtime-runes/samples/props-and-slots/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/props-and-slots/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/props-and-slots/main.svelte diff --git a/.changeset/ten-plants-carry.md b/.changeset/ten-plants-carry.md new file mode 100644 index 0000000000..91ae5889cf --- /dev/null +++ b/.changeset/ten-plants-carry.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: don't redeclare `$$slots` diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js index 1f2bd3e2b1..b76455b5c1 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js @@ -1,6 +1,7 @@ /** @import { VariableDeclaration, VariableDeclarator, Expression, CallExpression, Pattern, Identifier } from 'estree' */ /** @import { Binding } from '#compiler' */ /** @import { Context } from '../types.js' */ +/** @import { ComponentAnalysis } from '../../../types.js' */ /** @import { Scope } from '../../../scope.js' */ import { build_fallback, extract_paths } from '../../../../utils/ast.js'; import * as b from '#compiler/builders'; @@ -50,20 +51,26 @@ export function VariableDeclaration(node, context) { } } }); + + // if `$$slots` is declared separately, deconflict + const slots_name = /** @type {ComponentAnalysis} */ (context.state.analysis).uses_slots + ? b.id('$$slots_') + : b.id('$$slots'); + if (id.type === 'ObjectPattern' && has_rest) { // If a rest pattern is used within an object pattern, we need to ensure we don't expose $$slots or $$events id.properties.splice( id.properties.length - 1, 0, // @ts-ignore - b.prop('init', b.id('$$slots'), b.id('$$slots')), + b.prop('init', b.id('$$slots'), slots_name), b.prop('init', b.id('$$events'), b.id('$$events')) ); } else if (id.type === 'Identifier') { // If $props is referenced as an identifier, we need to ensure we don't expose $$slots or $$events as properties // on the identifier reference id = b.object_pattern([ - b.prop('init', b.id('$$slots'), b.id('$$slots')), + b.prop('init', b.id('$$slots'), slots_name), b.prop('init', b.id('$$events'), b.id('$$events')), b.rest(b.id(id.name)) ]); diff --git a/packages/svelte/tests/runtime-runes/samples/props-and-slots/Child.svelte b/packages/svelte/tests/runtime-runes/samples/props-and-slots/Child.svelte new file mode 100644 index 0000000000..a2e7d6d8a4 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-and-slots/Child.svelte @@ -0,0 +1,9 @@ + + +

{Object.keys(props)}

+ +{#if $$slots.foo} +

foo exists

+{/if} diff --git a/packages/svelte/tests/runtime-runes/samples/props-and-slots/_config.js b/packages/svelte/tests/runtime-runes/samples/props-and-slots/_config.js new file mode 100644 index 0000000000..3f6fb4143c --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-and-slots/_config.js @@ -0,0 +1,8 @@ +import { test } from '../../test'; + +export default test({ + html: ` +

a

+

foo exists

+ ` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/props-and-slots/main.svelte b/packages/svelte/tests/runtime-runes/samples/props-and-slots/main.svelte new file mode 100644 index 0000000000..3535da7132 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/props-and-slots/main.svelte @@ -0,0 +1,7 @@ + + + +
foo
+
From fbb1aecc2d617c6f0cf47240ac457b50baf490ef Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 21:23:34 -0400 Subject: [PATCH 0394/1429] Version Packages (#15904) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/gorgeous-experts-perform.md | 5 ----- .changeset/ten-plants-carry.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/gorgeous-experts-perform.md delete mode 100644 .changeset/ten-plants-carry.md diff --git a/.changeset/gorgeous-experts-perform.md b/.changeset/gorgeous-experts-perform.md deleted file mode 100644 index 9c4673a341..0000000000 --- a/.changeset/gorgeous-experts-perform.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: use `transform.read` for `ownership_validator.mutation` array diff --git a/.changeset/ten-plants-carry.md b/.changeset/ten-plants-carry.md deleted file mode 100644 index 91ae5889cf..0000000000 --- a/.changeset/ten-plants-carry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: don't redeclare `$$slots` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 4ea42a3da2..8df324d649 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.28.6 + +### Patch Changes + +- fix: use `transform.read` for `ownership_validator.mutation` array ([#15848](https://github.com/sveltejs/svelte/pull/15848)) + +- fix: don't redeclare `$slots` ([#15849](https://github.com/sveltejs/svelte/pull/15849)) + ## 5.28.5 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index c3255e30cd..dadba9d50c 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.28.5", + "version": "5.28.6", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 3ba616e962..a77849804c 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.28.5'; +export const VERSION = '5.28.6'; export const PUBLIC_VERSION = '5'; From d4fb9f4b100683cafc1f8d558c059c8691aab250 Mon Sep 17 00:00:00 2001 From: Ryan Atkinson Date: Mon, 12 May 2025 20:00:13 -0600 Subject: [PATCH 0395/1429] fix: remove unncessary guards that require CSP privilege when removing event attributes (#15846) * fix: remove unncessary guards that cause CSP violations when removing event attributes * add changeset --- .changeset/hot-seals-hang.md | 5 +++++ .../svelte/src/internal/client/dom/elements/events.js | 8 ++------ 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 .changeset/hot-seals-hang.md diff --git a/.changeset/hot-seals-hang.md b/.changeset/hot-seals-hang.md new file mode 100644 index 0000000000..184b89d506 --- /dev/null +++ b/.changeset/hot-seals-hang.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: remove unncessary guards that require CSP privilege when removing event attributes diff --git a/packages/svelte/src/internal/client/dom/elements/events.js b/packages/svelte/src/internal/client/dom/elements/events.js index 3374fe713f..c2b7fc7d83 100644 --- a/packages/svelte/src/internal/client/dom/elements/events.js +++ b/packages/svelte/src/internal/client/dom/elements/events.js @@ -26,12 +26,8 @@ export const root_event_handles = new Set(); export function replay_events(dom) { if (!hydrating) return; - if (dom.onload) { - dom.removeAttribute('onload'); - } - if (dom.onerror) { - dom.removeAttribute('onerror'); - } + dom.removeAttribute('onload'); + dom.removeAttribute('onerror'); // @ts-expect-error const event = dom.__e; if (event !== undefined) { From 8866851e2c3254d650732ee04c9cceb33f5a552c Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Mon, 12 May 2025 19:42:13 -0700 Subject: [PATCH 0396/1429] docs: add links to new CLI add-on documentation (#15835) Co-authored-by: Rich Harris --- documentation/docs/07-misc/02-testing.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/docs/07-misc/02-testing.md b/documentation/docs/07-misc/02-testing.md index 0842019039..64bf49d77a 100644 --- a/documentation/docs/07-misc/02-testing.md +++ b/documentation/docs/07-misc/02-testing.md @@ -6,9 +6,9 @@ Testing helps you write and maintain your code and guard against regressions. Te ## Unit and integration testing using Vitest -Unit tests allow you to test small isolated parts of your code. Integration tests allow you to test parts of your application to see if they work together. If you're using Vite (including via SvelteKit), we recommend using [Vitest](https://vitest.dev/). +Unit tests allow you to test small isolated parts of your code. Integration tests allow you to test parts of your application to see if they work together. If you're using Vite (including via SvelteKit), we recommend using [Vitest](https://vitest.dev/). You can use the Svelte CLI to [setup Vitest](/docs/cli/vitest) either during project creation or later on. -To get started, install Vitest: +To setup Vitest manually, first install it: ```bash npm install -D vitest @@ -254,9 +254,9 @@ When writing component tests that involve two-way bindings, context or snippet p E2E (short for 'end to end') tests allow you to test your full application through the eyes of the user. This section uses [Playwright](https://playwright.dev/) as an example, but you can also use other solutions like [Cypress](https://www.cypress.io/) or [NightwatchJS](https://nightwatchjs.org/). -To get started with Playwright, either install it via [the VS Code extension](https://playwright.dev/docs/getting-started-vscode), or install it from the command line using `npm init playwright`. It is also part of the setup CLI when you run `npx sv create`. +You can use the Svelte CLI to [setup Playwright](/docs/cli/playwright) either during project creation or later on. You can also [set it up with `npm init playwright`](https://playwright.dev/docs/intro). Additionally, you may also want to install an IDE plugin such as [the VS Code extension](https://playwright.dev/docs/getting-started-vscode) to be able to execute tests from inside your IDE. -After you've done that, you should have a `tests` folder and a Playwright config. You may need to adjust that config to tell Playwright what to do before running the tests - mainly starting your application at a certain port: +If you've run `npm init playwright` or are not using Vite, you may need to adjust the Playwright config to tell Playwright what to do before running the tests - mainly starting your application at a certain port. For example: ```js /// file: playwright.config.js From b6fe1cca3ce6170ce92c6e09d78c43f6be17f9fb Mon Sep 17 00:00:00 2001 From: Tristan <62366897+TitanCmd@users.noreply.github.com> Date: Tue, 13 May 2025 22:51:57 +1000 Subject: [PATCH 0397/1429] docs: remove dead link in 07-misc/99-faq.md (#15906) remove dead link from the docs in favor of pointing to our own docs --------- Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> --- documentation/docs/07-misc/99-faq.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/docs/07-misc/99-faq.md b/documentation/docs/07-misc/99-faq.md index ed5c6277c0..cf98cdd3c3 100644 --- a/documentation/docs/07-misc/99-faq.md +++ b/documentation/docs/07-misc/99-faq.md @@ -81,9 +81,10 @@ _End-to-End Tests_: To ensure your users are able to interact with your applicat Some resources for getting started with testing: +- [Svelte docs on testing](/docs/svelte/testing) +- [Setup Vitest using the Svelte CLI](/docs/cli/vitest) - [Svelte Testing Library](https://testing-library.com/docs/svelte-testing-library/example/) - [Svelte Component Testing in Cypress](https://docs.cypress.io/guides/component-testing/svelte/overview) -- [Example using vitest](https://github.com/vitest-dev/vitest/tree/main/examples/sveltekit) - [Example using uvu test runner with JSDOM](https://github.com/lukeed/uvu/tree/master/examples/svelte) - [Test Svelte components using Vitest & Playwright](https://davipon.hashnode.dev/test-svelte-component-using-vitest-playwright) - [Component testing with WebdriverIO](https://webdriver.io/docs/component-testing/svelte) From 8fc8bc79d1bd0b1c44b40135faf4211a5e648e5d Mon Sep 17 00:00:00 2001 From: 7nik Date: Tue, 13 May 2025 17:16:37 +0300 Subject: [PATCH 0398/1429] docs: add missing bindings (#15834) * add missing bindings docs * tweak * tweak --------- Co-authored-by: 7nik Co-authored-by: Rich Harris --- .../docs/03-template-syntax/11-bind.md | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/documentation/docs/03-template-syntax/11-bind.md b/documentation/docs/03-template-syntax/11-bind.md index c23f3b5232..0970ee384e 100644 --- a/documentation/docs/03-template-syntax/11-bind.md +++ b/documentation/docs/03-template-syntax/11-bind.md @@ -117,6 +117,29 @@ Since 5.6.0, if an `` has a `defaultChecked` attribute and is part of a f ``` +## `` + +Checkboxes can be in an [indeterminate](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/indeterminate) state, independently of whether they are checked or unchecked: + +```svelte + + +
+ + + {#if indeterminate} + waiting... + {:else if checked} + checked + {:else} + unchecked + {/if} +
+``` + ## `` Inputs that work together can use `bind:group`. @@ -227,6 +250,7 @@ You can give the `` a default value by adding a `selected` attribute to ``` +## `window` and `document` + +To bind to properties of `window` and `document`, see [``](svelte-window) and [``](svelte-document). + ## Contenteditable bindings Elements with the `contenteditable` attribute support the following bindings: @@ -278,6 +306,10 @@ All visible elements have the following readonly bindings, measured with a `Resi - [`clientHeight`](https://developer.mozilla.org/en-US/docs/Web/API/Element/clientHeight) - [`offsetWidth`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetWidth) - [`offsetHeight`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetHeight) +- [`contentRect`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/contentRect) +- [`contentBoxSize`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/contentBoxSize) +- [`borderBoxSize`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/borderBoxSize) +- [`devicePixelContentBoxSize`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry/devicePixelContentBoxSize) ```svelte
@@ -285,7 +317,7 @@ All visible elements have the following readonly bindings, measured with a `Resi
``` -> [!NOTE] `display: inline` elements do not have a width or height (except for elements with 'intrinsic' dimensions, like `` and ``), and cannot be observed with a `ResizeObserver`. You will need to change the `display` style of these elements to something else, such as `inline-block`. +> [!NOTE] `display: inline` elements do not have a width or height (except for elements with 'intrinsic' dimensions, like `` and ``), and cannot be observed with a `ResizeObserver`. You will need to change the `display` style of these elements to something else, such as `inline-block`. Note that CSS transformations do not trigger `ResizeObserver` callbacks. ## bind:this From 7826ebaccceeee5edda77b5d900d2c204590c6ff Mon Sep 17 00:00:00 2001 From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com> Date: Tue, 13 May 2025 09:06:12 -0700 Subject: [PATCH 0399/1429] fix: rewrite destructuring logic to handle iterators (#15813) * fix: wrap array destructuring in spread to avoid iterator edge case * spread at `tmp` declaration * completely rewrite destructuring handling * only wrap in iife if necessary * oops * minor style tweaks * separate visitors * tweak --------- Co-authored-by: Rich Harris --- .changeset/serious-moles-yell.md | 5 ++ .../client/visitors/VariableDeclaration.js | 69 ++++++++++--------- .../server/visitors/VariableDeclaration.js | 13 ++-- .../phases/3-transform/shared/assignments.js | 67 ++++++++++-------- packages/svelte/src/compiler/phases/scope.js | 5 +- packages/svelte/src/compiler/utils/ast.js | 46 ++++++++++++- .../_expected/client/index.svelte.js | 10 +-- 7 files changed, 141 insertions(+), 74 deletions(-) create mode 100644 .changeset/serious-moles-yell.md diff --git a/.changeset/serious-moles-yell.md b/.changeset/serious-moles-yell.md new file mode 100644 index 0000000000..08f33dc059 --- /dev/null +++ b/.changeset/serious-moles-yell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: rewrite destructuring logic to handle iterators diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js index 84044e4ded..84c205d163 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/VariableDeclaration.js @@ -2,7 +2,7 @@ /** @import { Binding } from '#compiler' */ /** @import { ComponentClientTransformState, ComponentContext } from '../types' */ import { dev } from '../../../../state.js'; -import { extract_paths } from '../../../../utils/ast.js'; +import { build_pattern, extract_paths } from '../../../../utils/ast.js'; import * as b from '#compiler/builders'; import * as assert from '../../../../utils/assert.js'; import { get_rune } from '../../../scope.js'; @@ -141,20 +141,20 @@ export function VariableDeclaration(node, context) { b.declarator(declarator.id, create_state_declarator(declarator.id, value)) ); } else { - const tmp = context.state.scope.generate('tmp'); - const paths = extract_paths(declarator.id); + const [pattern, replacements] = build_pattern(declarator.id, context.state.scope); declarations.push( - b.declarator(b.id(tmp), value), - ...paths.map((path) => { - const value = path.expression?.(b.id(tmp)); - const binding = context.state.scope.get(/** @type {Identifier} */ (path.node).name); - return b.declarator( - path.node, - binding?.kind === 'state' || binding?.kind === 'raw_state' - ? create_state_declarator(binding.node, value) - : value - ); - }) + b.declarator(pattern, value), + .../** @type {[Identifier, Identifier][]} */ ([...replacements]).map( + ([original, replacement]) => { + const binding = context.state.scope.get(original.name); + return b.declarator( + original, + binding?.kind === 'state' || binding?.kind === 'raw_state' + ? create_state_declarator(binding.node, replacement) + : replacement + ); + } + ) ); } @@ -170,8 +170,7 @@ export function VariableDeclaration(node, context) { ) ); } else { - const bindings = extract_paths(declarator.id); - + const [pattern, replacements] = build_pattern(declarator.id, context.state.scope); const init = /** @type {CallExpression} */ (declarator.init); /** @type {Identifier} */ @@ -189,10 +188,16 @@ export function VariableDeclaration(node, context) { ); } - for (let i = 0; i < bindings.length; i++) { - const binding = bindings[i]; + for (let i = 0; i < replacements.size; i++) { + const [original, replacement] = [...replacements][i]; declarations.push( - b.declarator(binding.node, b.call('$.derived', b.thunk(binding.expression(rhs)))) + b.declarator( + original, + b.call( + '$.derived', + b.arrow([], b.block([b.let(pattern, rhs), b.return(replacement)])) + ) + ) ); } } @@ -304,19 +309,19 @@ function create_state_declarators(declarator, { scope, analysis }, value) { ]; } - const tmp = scope.generate('tmp'); - const paths = extract_paths(declarator.id); + const [pattern, replacements] = build_pattern(declarator.id, scope); return [ - b.declarator(b.id(tmp), value), - ...paths.map((path) => { - const value = path.expression?.(b.id(tmp)); - const binding = scope.get(/** @type {Identifier} */ (path.node).name); - return b.declarator( - path.node, - binding?.kind === 'state' - ? b.call('$.mutable_source', value, analysis.immutable ? b.true : undefined) - : value - ); - }) + b.declarator(pattern, value), + .../** @type {[Identifier, Identifier][]} */ ([...replacements]).map( + ([original, replacement]) => { + const binding = scope.get(original.name); + return b.declarator( + original, + binding?.kind === 'state' + ? b.call('$.mutable_source', replacement, analysis.immutable ? b.true : undefined) + : replacement + ); + } + ) ]; } diff --git a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js index b76455b5c1..17bf516a22 100644 --- a/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js +++ b/packages/svelte/src/compiler/phases/3-transform/server/visitors/VariableDeclaration.js @@ -3,7 +3,7 @@ /** @import { Context } from '../types.js' */ /** @import { ComponentAnalysis } from '../../../types.js' */ /** @import { Scope } from '../../../scope.js' */ -import { build_fallback, extract_paths } from '../../../../utils/ast.js'; +import { build_pattern, build_fallback, extract_paths } from '../../../../utils/ast.js'; import * as b from '#compiler/builders'; import { get_rune } from '../../../scope.js'; import { walk } from 'zimmerframe'; @@ -188,13 +188,10 @@ function create_state_declarators(declarator, scope, value) { return [b.declarator(declarator.id, value)]; } - const tmp = scope.generate('tmp'); - const paths = extract_paths(declarator.id); + const [pattern, replacements] = build_pattern(declarator.id, scope); return [ - b.declarator(b.id(tmp), value), // TODO inject declarator for opts, so we can use it below - ...paths.map((path) => { - const value = path.expression?.(b.id(tmp)); - return b.declarator(path.node, value); - }) + b.declarator(pattern, value), + // TODO inject declarator for opts, so we can use it below + ...[...replacements].map(([original, replacement]) => b.declarator(original, replacement)) ]; } diff --git a/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js b/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js index 3e6bb0c4c6..85b0869a15 100644 --- a/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js +++ b/packages/svelte/src/compiler/phases/3-transform/shared/assignments.js @@ -1,7 +1,7 @@ -/** @import { AssignmentExpression, AssignmentOperator, Expression, Node, Pattern } from 'estree' */ +/** @import { AssignmentExpression, AssignmentOperator, Expression, Identifier, Node, Pattern } from 'estree' */ /** @import { Context as ClientContext } from '../client/types.js' */ /** @import { Context as ServerContext } from '../server/types.js' */ -import { extract_paths, is_expression_async } from '../../../utils/ast.js'; +import { build_pattern, is_expression_async } from '../../../utils/ast.js'; import * as b from '#compiler/builders'; /** @@ -23,21 +23,23 @@ export function visit_assignment_expression(node, context, build_assignment) { let changed = false; - const assignments = extract_paths(node.left).map((path) => { - const value = path.expression?.(rhs); + const [pattern, replacements] = build_pattern(node.left, context.state.scope); - let assignment = build_assignment('=', path.node, value, context); - if (assignment !== null) changed = true; - - return ( - assignment ?? - b.assignment( - '=', - /** @type {Pattern} */ (context.visit(path.node)), - /** @type {Expression} */ (context.visit(value)) - ) - ); - }); + const assignments = [ + b.let(pattern, rhs), + ...[...replacements].map(([original, replacement]) => { + let assignment = build_assignment(node.operator, original, replacement, context); + if (assignment !== null) changed = true; + return b.stmt( + assignment ?? + b.assignment( + node.operator, + /** @type {Identifier} */ (context.visit(original)), + /** @type {Expression} */ (context.visit(replacement)) + ) + ); + }) + ]; if (!changed) { // No change to output -> nothing to transform -> we can keep the original assignment @@ -45,25 +47,36 @@ export function visit_assignment_expression(node, context, build_assignment) { } const is_standalone = /** @type {Node} */ (context.path.at(-1)).type.endsWith('Statement'); - const sequence = b.sequence(assignments); + const block = b.block(assignments); if (!is_standalone) { // this is part of an expression, we need the sequence to end with the value - sequence.expressions.push(rhs); + block.body.push(b.return(rhs)); } - if (should_cache) { - // the right hand side is a complex expression, wrap in an IIFE to cache it - const iife = b.arrow([rhs], sequence); + if (is_standalone && !should_cache) { + return block; + } - const iife_is_async = - is_expression_async(value) || - assignments.some((assignment) => is_expression_async(assignment)); + const iife = b.arrow(should_cache ? [rhs] : [], block); - return iife_is_async ? b.await(b.call(b.async(iife), value)) : b.call(iife, value); - } + const iife_is_async = + is_expression_async(value) || + assignments.some( + (assignment) => + (assignment.type === 'ExpressionStatement' && + is_expression_async(assignment.expression)) || + (assignment.type === 'VariableDeclaration' && + assignment.declarations.some( + (declaration) => + is_expression_async(declaration.id) || + (declaration.init && is_expression_async(declaration.init)) + )) + ); - return sequence; + return iife_is_async + ? b.await(b.call(b.async(iife), should_cache ? value : undefined)) + : b.call(iife, should_cache ? value : undefined); } if (node.left.type !== 'Identifier' && node.left.type !== 'MemberExpression') { diff --git a/packages/svelte/src/compiler/phases/scope.js b/packages/svelte/src/compiler/phases/scope.js index 75a26d487b..f37dfab8d1 100644 --- a/packages/svelte/src/compiler/phases/scope.js +++ b/packages/svelte/src/compiler/phases/scope.js @@ -630,9 +630,10 @@ export class Scope { /** * @param {string} preferred_name + * @param {(name: string, counter: number) => string} [generator] * @returns {string} */ - generate(preferred_name) { + generate(preferred_name, generator = (name, counter) => `${name}_${counter}`) { if (this.#porous) { return /** @type {Scope} */ (this.parent).generate(preferred_name); } @@ -647,7 +648,7 @@ export class Scope { this.root.conflicts.has(name) || is_reserved(name) ) { - name = `${preferred_name}_${n++}`; + name = generator(preferred_name, n++); } this.references.set(name, []); diff --git a/packages/svelte/src/compiler/utils/ast.js b/packages/svelte/src/compiler/utils/ast.js index 23a95a1026..32ff5a37b3 100644 --- a/packages/svelte/src/compiler/utils/ast.js +++ b/packages/svelte/src/compiler/utils/ast.js @@ -1,7 +1,8 @@ -/** @import { AST } from '#compiler' */ +/** @import { AST, Scope } from '#compiler' */ /** @import * as ESTree from 'estree' */ import { walk } from 'zimmerframe'; import * as b from '#compiler/builders'; +import is_reference from 'is-reference'; /** * Gets the left-most identifier of a member expression or identifier. @@ -128,6 +129,49 @@ export function unwrap_pattern(pattern, nodes = []) { return nodes; } +/** + * @param {ESTree.Pattern} id + * @param {Scope} scope + * @returns {[ESTree.Pattern, Map]} + */ +export function build_pattern(id, scope) { + /** @type {Map} */ + const map = new Map(); + + /** @type {Map} */ + const names = new Map(); + + let counter = 0; + + for (const node of unwrap_pattern(id)) { + const name = scope.generate(`$$${++counter}`, (_, counter) => `$$${counter}`); + + map.set(node, b.id(name)); + + if (node.type === 'Identifier') { + names.set(node.name, name); + } + } + + const pattern = walk(id, null, { + Identifier(node, context) { + if (is_reference(node, /** @type {ESTree.Pattern} */ (context.path.at(-1)))) { + const name = names.get(node.name); + if (name) return b.id(name); + } + }, + + MemberExpression(node, context) { + const n = map.get(node); + if (n) return n; + + context.next(); + } + }); + + return [pattern, map]; +} + /** * Extracts all identifiers from a pattern. * @param {ESTree.Pattern} pattern diff --git a/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js index 47f297bce9..b2ef29ccaf 100644 --- a/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/destructured-assignments/_expected/client/index.svelte.js @@ -7,10 +7,12 @@ let c = 3; let d = 4; export function update(array) { - ( - $.set(a, array[0], true), - $.set(b, array[1], true) - ); + { + let [$$1, $$2] = array; + + $.set(a, $$1, true); + $.set(b, $$2, true); + }; [c, d] = array; } \ No newline at end of file From 7636031b0d73ec350e0addb2dbfa7a1e3ee413df Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 13:22:08 -0400 Subject: [PATCH 0400/1429] Version Packages (#15905) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/hot-seals-hang.md | 5 ----- .changeset/serious-moles-yell.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/hot-seals-hang.md delete mode 100644 .changeset/serious-moles-yell.md diff --git a/.changeset/hot-seals-hang.md b/.changeset/hot-seals-hang.md deleted file mode 100644 index 184b89d506..0000000000 --- a/.changeset/hot-seals-hang.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: remove unncessary guards that require CSP privilege when removing event attributes diff --git a/.changeset/serious-moles-yell.md b/.changeset/serious-moles-yell.md deleted file mode 100644 index 08f33dc059..0000000000 --- a/.changeset/serious-moles-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: rewrite destructuring logic to handle iterators diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 8df324d649..935d2b7cc6 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.28.7 + +### Patch Changes + +- fix: remove unncessary guards that require CSP privilege when removing event attributes ([#15846](https://github.com/sveltejs/svelte/pull/15846)) + +- fix: rewrite destructuring logic to handle iterators ([#15813](https://github.com/sveltejs/svelte/pull/15813)) + ## 5.28.6 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index dadba9d50c..ce257c687a 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.28.6", + "version": "5.28.7", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index a77849804c..d7c041762f 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.28.6'; +export const VERSION = '5.28.7'; export const PUBLIC_VERSION = '5'; From b93a617aa814a0e9154ae84b6767cd8ca02459a2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 14 May 2025 13:33:23 -0400 Subject: [PATCH 0401/1429] feat: attachments (#15000) * parse attachments * basic attachments working * working * rename to attach * fix * restrict which symbols are recognised as attachment keys * allow cleanup to be returned directly * changeset * fix * lint * remove createAttachmentKey/isAttachmentKey * fix spreading of symbol properties onto component * types * fix * update name * reserve ability to use sequence expressions in future * Update packages/svelte/src/internal/client/dom/elements/attachments.js Co-authored-by: Leonidaz * actually let's do this instead * expose createAttachmentKey * make room for `@attach` docs * add docs * failing test * fix * lock down * add missing reference docs * prevent conflicts * update docs * regenerate * fix link * add Attachment interface * beef up test * regenerate * tweak types * fix --------- Co-authored-by: Leonidaz --- .changeset/poor-days-pay.md | 5 + .../docs/03-template-syntax/09-@attach.md | 138 +++++++++++++++++ .../{09-@const.md => 10-@const.md} | 0 .../{10-@debug.md => 11-@debug.md} | 0 .../{11-bind.md => 12-bind.md} | 0 .../{12-use.md => 13-use.md} | 3 + .../{13-transition.md => 14-transition.md} | 0 .../{14-in-and-out.md => 15-in-and-out.md} | 0 .../{15-animate.md => 16-animate.md} | 0 .../98-reference/21-svelte-attachments.md | 5 + packages/svelte/elements.d.ts | 5 + packages/svelte/package.json | 4 + packages/svelte/scripts/generate-types.js | 1 + packages/svelte/src/attachments/index.js | 27 ++++ packages/svelte/src/attachments/public.d.ts | 12 ++ .../compiler/phases/1-parse/read/script.js | 2 +- .../src/compiler/phases/1-parse/read/style.js | 2 +- .../compiler/phases/1-parse/state/element.js | 23 ++- .../src/compiler/phases/2-analyze/index.js | 2 + .../phases/2-analyze/visitors/AttachTag.js | 13 ++ .../2-analyze/visitors/shared/component.js | 36 +++-- .../3-transform/client/transform-client.js | 2 + .../3-transform/client/visitors/AttachTag.js | 21 +++ .../client/visitors/RegularElement.js | 6 +- .../client/visitors/shared/component.js | 8 + .../svelte/src/compiler/types/template.d.ts | 13 +- packages/svelte/src/constants.js | 2 + .../client/dom/elements/attachments.js | 15 ++ .../client/dom/elements/attributes.js | 9 +- packages/svelte/src/internal/client/index.js | 2 + .../src/internal/client/reactivity/props.js | 6 + .../samples/attachments/input.svelte | 1 + .../samples/attachments/output.json | 141 ++++++++++++++++++ .../samples/attachment-basic/_config.js | 6 + .../samples/attachment-basic/main.svelte | 1 + .../attachment-component-spread/Child.svelte | 5 + .../attachment-component-spread/_config.js | 15 ++ .../attachment-component-spread/main.svelte | 30 ++++ .../samples/attachment-component/Child.svelte | 5 + .../samples/attachment-component/_config.js | 14 ++ .../samples/attachment-component/main.svelte | 15 ++ .../samples/attachment-reactive/_config.js | 14 ++ .../samples/attachment-reactive/main.svelte | 6 + .../samples/attachment-spread/_config.js | 8 + .../samples/attachment-spread/main.svelte | 9 ++ .../attachment-svelte-element/_config.js | 6 + .../attachment-svelte-element/main.svelte | 1 + packages/svelte/types/index.d.ts | 47 +++++- 48 files changed, 669 insertions(+), 17 deletions(-) create mode 100644 .changeset/poor-days-pay.md create mode 100644 documentation/docs/03-template-syntax/09-@attach.md rename documentation/docs/03-template-syntax/{09-@const.md => 10-@const.md} (100%) rename documentation/docs/03-template-syntax/{10-@debug.md => 11-@debug.md} (100%) rename documentation/docs/03-template-syntax/{11-bind.md => 12-bind.md} (100%) rename documentation/docs/03-template-syntax/{12-use.md => 13-use.md} (93%) rename documentation/docs/03-template-syntax/{13-transition.md => 14-transition.md} (100%) rename documentation/docs/03-template-syntax/{14-in-and-out.md => 15-in-and-out.md} (100%) rename documentation/docs/03-template-syntax/{15-animate.md => 16-animate.md} (100%) create mode 100644 documentation/docs/98-reference/21-svelte-attachments.md create mode 100644 packages/svelte/src/attachments/index.js create mode 100644 packages/svelte/src/attachments/public.d.ts create mode 100644 packages/svelte/src/compiler/phases/2-analyze/visitors/AttachTag.js create mode 100644 packages/svelte/src/compiler/phases/3-transform/client/visitors/AttachTag.js create mode 100644 packages/svelte/src/internal/client/dom/elements/attachments.js create mode 100644 packages/svelte/tests/parser-modern/samples/attachments/input.svelte create mode 100644 packages/svelte/tests/parser-modern/samples/attachments/output.json create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-basic/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-basic/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-component-spread/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-component-spread/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-component-spread/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-component/Child.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-component/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-component/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-reactive/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-reactive/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-spread/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-spread/main.svelte create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-svelte-element/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-svelte-element/main.svelte diff --git a/.changeset/poor-days-pay.md b/.changeset/poor-days-pay.md new file mode 100644 index 0000000000..8fbff10586 --- /dev/null +++ b/.changeset/poor-days-pay.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: attachments diff --git a/documentation/docs/03-template-syntax/09-@attach.md b/documentation/docs/03-template-syntax/09-@attach.md new file mode 100644 index 0000000000..c988386933 --- /dev/null +++ b/documentation/docs/03-template-syntax/09-@attach.md @@ -0,0 +1,138 @@ +--- +title: {@attach ...} +--- + +Attachments are functions that run when an element is mounted to the DOM. Optionally, they can return a function that is called when the element is later removed from the DOM. + +> [!NOTE] +> Attachments are available in Svelte 5.29 and newer. + +```svelte + + + +
...
+``` + +An element can have any number of attachments. + +## Attachment factories + +A useful pattern is for a function, such as `tooltip` in this example, to _return_ an attachment ([demo](/playground/untitled#H4sIAAAAAAAAE3VT0XLaMBD8lavbDiaNCUlbHhTItG_5h5AH2T5ArdBppDOEMv73SkbGJGnH47F9t3un3TsfMyO3mInsh2SW1Sa7zlZKo8_E0zHjg42pGAjxBPxp7cTvUHOMldLjv-IVGUbDoUw295VTlh-WZslqa8kxsLL2ACtHWxh175NffnQfAAGikSGxYQGfPEvGfPSIWtOH0TiBVo2pWJEBJtKhQp4YYzjG9JIdcuMM5IZqHMPioY8vOSA997zQoevf4a7heO7cdp34olRiTGr07OhwH1IdoO2A7dLMbwahZq6MbRhKZWqxk7rBxTGVbuHmhCgb5qDgmIx_J6XtHHukHTrYYqx_YpzYng8aO4RYayql7hU-1ZJl0akqHBE_D9KLolwL-Dibzc7iSln9XjtqTF1UpMkJ2EmXR-BgQErsN4pxIJKr0RVO1qrxAqaTO4fbc9bKulZm3cfDY3aZDgvFGErWjmzhN7KmfX5rXyDeX8Pt1mU-hXjdBOrtuB97vK4GPUtmJ41XcRMEGDLD8do0nJ73zhUhSlyRw0t3vPqD8cjfLs-axiFgNBrkUd9Ulp50c-GLxlXAVlJX-ffpZyiSn7H0eLCUySZQcQdXlxj4El0Yv_FZvIKElqqGTruVLhzu7VRKCh22_5toOyxsWqLwwzK-cCbYNdg-hy-p9D7sbiZWUnts_wLUOF3CJgQAAA==)): + +```svelte + + + + + + +``` + +Since the `tooltip(content)` expression runs inside an [effect]($effect), the attachment will be destroyed and recreated whenever `content` changes. + +## Inline attachments + +Attachments can also be created inline ([demo](/playground/untitled#H4sIAAAAAAAAE71WbW_aSBD-Kyt0VaBJyGKbqoUkOhdI68qGUkh6pPSDMY6xYwyH12Ab8d9vZtYE6DX38aQQe3fennlm1jvbUmTP3VKj9KcthO3MShelJz9041Ljx7YksiWKcAP2C0V9uazGazcUuDexY_d3-84iEm4kwE3pOnZW_lLcjqOx8OfLxUqwLVvafiTYjj2tFnN2Vr3yVvbUB4NqEJ81x9H11cEounbsaG3HaL_xp2J2s1WVHa5mru_NxMtyW6TAytKgwm5u2RYlYwF4YsEIVSrYDZMaVc8VLblXPlOmZ5UmxkP9P9ynJ9cR5fKxk7EIXQGQIV9wsXL_TtxY6JE_t4W_iO5wv_yURA6uWLhYLMuicrAdi_-2RAMCUGgTReUC8gUTB9mueC2WK1ckq4j9AhVytiPHDX_Fh_-PXBVvhcsdEHl7fSXZkeTHIgtdKp7c3UegUjRYjfM3hQ9ZjpOty407efbF5dyOnxssWYXlcWlqC7sBmDz3Kl575-k8bGIXvdMuvn7uKo_Zx3Ayv_Mnn-7FaH4X2Mo0m6gPyWObR5P5g2q0dc9q6fVeS8uMdifttRxvOg_DKf-ydkEHZBuj_ayZgeFZw472JfuoTb6niZPzyP78jTvtxdpUp-o0q6tWVl87c2dtBfrGan3Ip3Mn-hqkm9Ff3xbGp_6HLwqvWwOtDnFqZvAYmMPOxgyehTW8T7oZzy1fU8yhAXuW6La9xPJ5arW0fNLiWTfTamCnmsED2DlJd6BzM3DA1gBbPQVbDv444Qw6iTXgKfjxwC43B5QbyDzPgrXRNlAm0MZoW0nX5_B06Ak-Mc-k10L7kQe81M3gHvYAz0CvkTwAvC2IOdDT7kADDq0MdSHvxMp0XnAJeXyLrQCx8hTxj3J6L2Igbp5KDIRbSNw6YSPcuDfsI5ac8KI80yFWX0AeitHuox4-pa-BpoEvzKMOOSMfWDeBGIFXwP4gzOE9cu71kF_FEpgf8AF-eYq4wQZ5z8A_2HtUF_LRwjXEaYFvrBnkA7rg00L9pCfjJYjHxNzmG8qbeBlgjndBwT1ypyCG7gtPngcY-aTd8TBPM-h41vfiiX6hjsAT9g3yw4t9ReLGdR_rSjUEOfBDtQRcyKUhSI4cwG_SNlTiD3vou5XiO2IB_zniBhusJeanORnHPpLcU92oZ9F3RjUiTizkDnx2BPUv4KK6Qc9RHIwbTGPZ632vCzqjDHlxEFOK9l3C-Yx1UiQ_XDtgkjUkf0MjR59QJ5XiEqZ-geMZasBzmds9YIK-xadPfIkenTsPsWPP_YYHB2OkxXlIqT6DopYDXaOa-1i_jvwW0JkiPHhG8AwUsfpYV6gF4tFzeXYQD9ZDo76kHoV1l3r5MYa9WtG3VA-sPfYKxW5xhbiRvYm9IqhX8HwO8Ix0UL8471hLOtd16mPip4N5UR6AgRdnJ8dvCMip1vCjbw3khfFS6h9lI-jswjnHnpY16yPHWdGPGeHzMcdZTj1J_d3B_JVRjvnopCv5wD7RVdLDPqG4kscTTpQNfvPgbI3g_f-pS4--a3TGUynH_hvJb9QpDzXJg3fo3eyld1Xq3YHjmbn23lTh7sm1m3Gpwur8Df2umMq16vtlyqLF5cpdurb4zb12Gfu522Dv-HruR_IWpQGmuDdhGMILvNQQq8TdXbwyVB3NP6dT1angaKxyUxqlXuaNf40L8qKWg8-W0XV9weQdDYPXzX4YqsprvXlQpru5Dbf0kRIMSsZ-u8wvGPydeNxPTk-LFSvjlLQEY96Ex_XBXxWv_mroRp6Yoej8hmmV0wnNB7MlEK81j3dT2PXZGxnyRJKBpOyDAYkq7Pb2FsLupzips3KnoPVOY-esXFPes7csrewtYA8Eb5lli1k19qOyAAkMMLxyEsZbuW70i5MMnRR8HntxFvErXiZhguMfmL8gPOXmB3DC-E8aEafNVzVqqEGQXtdRUAcDvq6ioopSr-97tugAqvcyOar3iy3VnZLanbb1T1jZfrjxo2mp8WSHsbv7Bx1mHBBZDAAA)): + +```svelte + + + + { + const context = canvas.getContext('2d'); + + $effect(() => { + let frame = requestAnimationFrame(function loop(t) { + frame = requestAnimationFrame(loop); + paint(context, t); + }); + + return () => { + cancelAnimationFrame(frame); + }; + }); + }} +> +``` + +## Passing attachments to components + +When used on a component, `{@attach ...}` will create a prop whose key is a [`Symbol`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol). If the component then [spreads](/tutorial/svelte/spread-props) props onto an element, the element will receive those attachments. + +This allows you to create _wrapper components_ that augment elements ([demo](/playground/untitled#H4sIAAAAAAAAE3VUS3ObMBD-KxvajnFqsJM2PhA7TXrKob31FjITAbKtRkiMtDhJPfz3LiAMdpxhGJvdb1_fPnaeYjn3Iu-WIbJ04028lZDcetHDzsO3olbVApI74F1RhHbLJdayhFl-Sp5qhVwhufEWNjWiwJtYxSjyQhsEFEXxBiujcxg1_8O_dnQ9APwsEbVyiHDafjrvDZCgkiO4MLCEzxYZcn90z6XUZ6OxA61KlaIgV6i1pFC-sxjDrlbHaDiWRoGvdMbHsLzp5DES0mJnRxGaRBvcBHb7yFUTCQeunEWYcYtGv12TqgFUDbCK1WLaM6IWQhUlQiJUFm2ZLPly51xXMG0Rjoyd69C7UqqG2nu95QZyXvtvLVpri2-SN4hoLXXCZFfhQ8aQBU1VgdEaH_vSgyBZR_BpPp_vi0tY-rw2ulRZkGqpTQRbZvwa2BPgFC8bgbw31CbjJjAsE6WNYBZeGp7vtQXLMqHWnZx-5kM1TR5ycpkZXQR2wzL94l8Ur1C_3-g168SfQf1MyfRi3LW9fs77emJEw5QV9SREoLTq06tcczq7d6xEUcJX2vAhO1b843XK34e5unZEMBr15ekuKEusluWAF8lXhE2ZTP2r2RcIHJ-163FPKerCgYJLOB9i4GvNwviI5-gAQiFFBk3tBTOU3HFXEk0R8o86WvUD64aINhv5K3oRmpJXkw8uxMG6Hh6JY9X7OwGSqfUy9tDG3sHNoEi0d_d_fv9qndxRU0VClFqo3KVo3U655Hnt1PXB3Qra2Y2QGdEwgTAMCxopsoxOe6SD0gD8movDhT0LAnhqlE8gVCpLWnRoV7OJCkFAwEXitrYL1W7p7pbiE_P7XH6E_rihODm5s52XtiH9Ekaw0VgI9exadWL1uoEYjPtg2672k5szsxbKyWB2fdT0w5Y_0hcT8oXOlRetmLS8-g-6TLXXQgYAAA==)): + +```svelte + + + + + +``` + +```svelte + + + + + + +``` + +## Creating attachments programmatically + +To add attachments to an object that will be spread onto a component or element, use [`createAttachmentKey`](svelte-attachments#createAttachmentKey). diff --git a/documentation/docs/03-template-syntax/09-@const.md b/documentation/docs/03-template-syntax/10-@const.md similarity index 100% rename from documentation/docs/03-template-syntax/09-@const.md rename to documentation/docs/03-template-syntax/10-@const.md diff --git a/documentation/docs/03-template-syntax/10-@debug.md b/documentation/docs/03-template-syntax/11-@debug.md similarity index 100% rename from documentation/docs/03-template-syntax/10-@debug.md rename to documentation/docs/03-template-syntax/11-@debug.md diff --git a/documentation/docs/03-template-syntax/11-bind.md b/documentation/docs/03-template-syntax/12-bind.md similarity index 100% rename from documentation/docs/03-template-syntax/11-bind.md rename to documentation/docs/03-template-syntax/12-bind.md diff --git a/documentation/docs/03-template-syntax/12-use.md b/documentation/docs/03-template-syntax/13-use.md similarity index 93% rename from documentation/docs/03-template-syntax/12-use.md rename to documentation/docs/03-template-syntax/13-use.md index 45de023578..5f5321a1c0 100644 --- a/documentation/docs/03-template-syntax/12-use.md +++ b/documentation/docs/03-template-syntax/13-use.md @@ -2,6 +2,9 @@ title: use: --- +> [!NOTE] +> In Svelte 5.29 and newer, consider using [attachments](@attach) instead, as they are more flexible and composable. + Actions are functions that are called when an element is mounted. They are added with the `use:` directive, and will typically use an `$effect` so that they can reset any state when the element is unmounted: ```svelte diff --git a/documentation/docs/03-template-syntax/13-transition.md b/documentation/docs/03-template-syntax/14-transition.md similarity index 100% rename from documentation/docs/03-template-syntax/13-transition.md rename to documentation/docs/03-template-syntax/14-transition.md diff --git a/documentation/docs/03-template-syntax/14-in-and-out.md b/documentation/docs/03-template-syntax/15-in-and-out.md similarity index 100% rename from documentation/docs/03-template-syntax/14-in-and-out.md rename to documentation/docs/03-template-syntax/15-in-and-out.md diff --git a/documentation/docs/03-template-syntax/15-animate.md b/documentation/docs/03-template-syntax/16-animate.md similarity index 100% rename from documentation/docs/03-template-syntax/15-animate.md rename to documentation/docs/03-template-syntax/16-animate.md diff --git a/documentation/docs/98-reference/21-svelte-attachments.md b/documentation/docs/98-reference/21-svelte-attachments.md new file mode 100644 index 0000000000..e446bdddc2 --- /dev/null +++ b/documentation/docs/98-reference/21-svelte-attachments.md @@ -0,0 +1,5 @@ +--- +title: svelte/attachments +--- + +> MODULE: svelte/attachments diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts index 99d87b4c09..c637137365 100644 --- a/packages/svelte/elements.d.ts +++ b/packages/svelte/elements.d.ts @@ -31,6 +31,8 @@ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.8 +import type { Attachment } from 'svelte/attachments'; + // Note: We also allow `null` as a valid value because Svelte treats this the same as `undefined` type Booleanish = boolean | 'true' | 'false'; @@ -860,6 +862,9 @@ export interface HTMLAttributes extends AriaAttributes, D // allow any data- attribute [key: `data-${string}`]: any; + + // allow any attachment + [key: symbol]: Attachment; } export type HTMLAttributeAnchorTarget = '_self' | '_blank' | '_parent' | '_top' | (string & {}); diff --git a/packages/svelte/package.json b/packages/svelte/package.json index ce257c687a..0ec1f9204d 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -34,6 +34,10 @@ "types": "./types/index.d.ts", "default": "./src/animate/index.js" }, + "./attachments": { + "types": "./types/index.d.ts", + "default": "./src/attachments/index.js" + }, "./compiler": { "types": "./types/index.d.ts", "require": "./compiler/index.js", diff --git a/packages/svelte/scripts/generate-types.js b/packages/svelte/scripts/generate-types.js index 377fca4343..c558a2bbf7 100644 --- a/packages/svelte/scripts/generate-types.js +++ b/packages/svelte/scripts/generate-types.js @@ -35,6 +35,7 @@ await createBundle({ [pkg.name]: `${dir}/src/index.d.ts`, [`${pkg.name}/action`]: `${dir}/src/action/public.d.ts`, [`${pkg.name}/animate`]: `${dir}/src/animate/public.d.ts`, + [`${pkg.name}/attachments`]: `${dir}/src/attachments/public.d.ts`, [`${pkg.name}/compiler`]: `${dir}/src/compiler/public.d.ts`, [`${pkg.name}/easing`]: `${dir}/src/easing/index.js`, [`${pkg.name}/legacy`]: `${dir}/src/legacy/legacy-client.js`, diff --git a/packages/svelte/src/attachments/index.js b/packages/svelte/src/attachments/index.js new file mode 100644 index 0000000000..948a19f4dd --- /dev/null +++ b/packages/svelte/src/attachments/index.js @@ -0,0 +1,27 @@ +import { ATTACHMENT_KEY } from '../constants.js'; + +/** + * Creates an object key that will be recognised as an attachment when the object is spread onto an element, + * as a programmatic alternative to using `{@attach ...}`. This can be useful for library authors, though + * is generally not needed when building an app. + * + * ```svelte + * + * + * + * ``` + * @since 5.29 + */ +export function createAttachmentKey() { + return Symbol(ATTACHMENT_KEY); +} diff --git a/packages/svelte/src/attachments/public.d.ts b/packages/svelte/src/attachments/public.d.ts new file mode 100644 index 0000000000..caf1342d0a --- /dev/null +++ b/packages/svelte/src/attachments/public.d.ts @@ -0,0 +1,12 @@ +/** + * An [attachment](https://svelte.dev/docs/svelte/@attach) is a function that runs when an element is mounted + * to the DOM, and optionally returns a function that is called when the element is later removed. + * + * It can be attached to an element with an `{@attach ...}` tag, or by spreading an object containing + * a property created with [`createAttachmentKey`](https://svelte.dev/docs/svelte/svelte-attachments#createAttachmentKey). + */ +export interface Attachment { + (element: T): void | (() => void); +} + +export * from './index.js'; diff --git a/packages/svelte/src/compiler/phases/1-parse/read/script.js b/packages/svelte/src/compiler/phases/1-parse/read/script.js index 9d9ed3a1ef..6290127811 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/script.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/script.js @@ -16,7 +16,7 @@ const ALLOWED_ATTRIBUTES = ['context', 'generics', 'lang', 'module']; /** * @param {Parser} parser * @param {number} start - * @param {Array} attributes + * @param {Array} attributes * @returns {AST.Script} */ export function read_script(parser, start, attributes) { diff --git a/packages/svelte/src/compiler/phases/1-parse/read/style.js b/packages/svelte/src/compiler/phases/1-parse/read/style.js index 56dbe124b7..e15a47e6d5 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/style.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/style.js @@ -18,7 +18,7 @@ const REGEX_HTML_COMMENT_CLOSE = /-->/; /** * @param {Parser} parser * @param {number} start - * @param {Array} attributes + * @param {Array} attributes * @returns {AST.CSS.StyleSheet} */ export default function read_style(parser, start, attributes) { diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js index 66946a8f8d..d20369b0d9 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -480,7 +480,7 @@ function read_static_attribute(parser) { /** * @param {Parser} parser - * @returns {AST.Attribute | AST.SpreadAttribute | AST.Directive | null} + * @returns {AST.Attribute | AST.SpreadAttribute | AST.Directive | AST.AttachTag | null} */ function read_attribute(parser) { const start = parser.index; @@ -488,6 +488,27 @@ function read_attribute(parser) { if (parser.eat('{')) { parser.allow_whitespace(); + if (parser.eat('@attach')) { + parser.require_whitespace(); + + const expression = read_expression(parser); + parser.allow_whitespace(); + parser.eat('}', true); + + /** @type {AST.AttachTag} */ + const attachment = { + type: 'AttachTag', + start, + end: parser.index, + expression, + metadata: { + expression: create_expression_metadata() + } + }; + + return attachment; + } + if (parser.eat('...')) { const expression = read_expression(parser); diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 766b317d06..09cb41b23f 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -18,6 +18,7 @@ import { extract_svelte_ignore } from '../../utils/extract_svelte_ignore.js'; import { ignore_map, ignore_stack, pop_ignore, push_ignore } from '../../state.js'; import { ArrowFunctionExpression } from './visitors/ArrowFunctionExpression.js'; import { AssignmentExpression } from './visitors/AssignmentExpression.js'; +import { AttachTag } from './visitors/AttachTag.js'; import { Attribute } from './visitors/Attribute.js'; import { AwaitBlock } from './visitors/AwaitBlock.js'; import { BindDirective } from './visitors/BindDirective.js'; @@ -133,6 +134,7 @@ const visitors = { }, ArrowFunctionExpression, AssignmentExpression, + AttachTag, Attribute, AwaitBlock, BindDirective, diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/AttachTag.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/AttachTag.js new file mode 100644 index 0000000000..1e318f228d --- /dev/null +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/AttachTag.js @@ -0,0 +1,13 @@ +/** @import { AST } from '#compiler' */ +/** @import { Context } from '../types' */ + +import { mark_subtree_dynamic } from './shared/fragment.js'; + +/** + * @param {AST.AttachTag} node + * @param {Context} context + */ +export function AttachTag(node, context) { + mark_subtree_dynamic(context.path); + context.next({ ...context.state, expression: node.metadata.expression }); +} diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js index 04bf3d2ff3..aca87fab81 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/shared/component.js @@ -1,3 +1,4 @@ +/** @import { Expression } from 'estree' */ /** @import { AST } from '#compiler' */ /** @import { AnalysisState, Context } from '../../types' */ import * as e from '../../../../errors.js'; @@ -74,7 +75,8 @@ export function visit_component(node, context) { attribute.type !== 'SpreadAttribute' && attribute.type !== 'LetDirective' && attribute.type !== 'OnDirective' && - attribute.type !== 'BindDirective' + attribute.type !== 'BindDirective' && + attribute.type !== 'AttachTag' ) { e.component_invalid_directive(attribute); } @@ -91,15 +93,10 @@ export function visit_component(node, context) { validate_attribute(attribute, node); if (is_expression_attribute(attribute)) { - const expression = get_attribute_expression(attribute); - if (expression.type === 'SequenceExpression') { - let i = /** @type {number} */ (expression.start); - while (--i > 0) { - const char = context.state.analysis.source[i]; - if (char === '(') break; // parenthesized sequence expressions are ok - if (char === '{') e.attribute_invalid_sequence_expression(expression); - } - } + disallow_unparenthesized_sequences( + get_attribute_expression(attribute), + context.state.analysis.source + ); } } @@ -113,6 +110,10 @@ export function visit_component(node, context) { if (attribute.type === 'BindDirective' && attribute.name !== 'this') { context.state.analysis.uses_component_bindings = true; } + + if (attribute.type === 'AttachTag') { + disallow_unparenthesized_sequences(attribute.expression, context.state.analysis.source); + } } // If the component has a slot attribute — `` — @@ -158,3 +159,18 @@ export function visit_component(node, context) { context.visit({ ...node.fragment, nodes: nodes[slot_name] }, state); } } + +/** + * @param {Expression} expression + * @param {string} source + */ +function disallow_unparenthesized_sequences(expression, source) { + if (expression.type === 'SequenceExpression') { + let i = /** @type {number} */ (expression.start); + while (--i > 0) { + const char = source[i]; + if (char === '(') break; // parenthesized sequence expressions are ok + if (char === '{') e.attribute_invalid_sequence_expression(expression); + } + } +} diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js index f0da5a4918..9a2f4dd34c 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js @@ -56,6 +56,7 @@ import { TitleElement } from './visitors/TitleElement.js'; import { TransitionDirective } from './visitors/TransitionDirective.js'; import { UpdateExpression } from './visitors/UpdateExpression.js'; import { UseDirective } from './visitors/UseDirective.js'; +import { AttachTag } from './visitors/AttachTag.js'; import { VariableDeclaration } from './visitors/VariableDeclaration.js'; /** @type {Visitors} */ @@ -131,6 +132,7 @@ const visitors = { TransitionDirective, UpdateExpression, UseDirective, + AttachTag, VariableDeclaration }; diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/AttachTag.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AttachTag.js new file mode 100644 index 0000000000..062604cacc --- /dev/null +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/AttachTag.js @@ -0,0 +1,21 @@ +/** @import { Expression } from 'estree' */ +/** @import { AST } from '#compiler' */ +/** @import { ComponentContext } from '../types' */ +import * as b from '../../../../utils/builders.js'; + +/** + * @param {AST.AttachTag} node + * @param {ComponentContext} context + */ +export function AttachTag(node, context) { + context.state.init.push( + b.stmt( + b.call( + '$.attach', + context.state.node, + b.thunk(/** @type {Expression} */ (context.visit(node.expression))) + ) + ) + ); + context.next(); +} diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index 7468fcbbc7..ab981878ad 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -83,7 +83,7 @@ export function RegularElement(node, context) { /** @type {AST.StyleDirective[]} */ const style_directives = []; - /** @type {Array} */ + /** @type {Array} */ const other_directives = []; /** @type {ExpressionStatement[]} */ @@ -153,6 +153,10 @@ export function RegularElement(node, context) { has_use = true; other_directives.push(attribute); break; + + case 'AttachTag': + other_directives.push(attribute); + break; } } diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js index c4071c67fe..6d3d8a68e6 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js @@ -257,6 +257,14 @@ export function build_component(node, component_name, context, anchor = context. ); } } + } else if (attribute.type === 'AttachTag') { + let expression = /** @type {Expression} */ (context.visit(attribute.expression)); + + if (attribute.metadata.expression.has_state) { + expression = b.arrow([b.id('$$node')], b.call(expression, b.id('$$node'))); + } + + push_prop(b.prop('get', b.call('$.attachment'), expression, true)); } } diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index a544cd1dec..54fdda92b8 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -174,6 +174,16 @@ export namespace AST { }; } + /** A `{@attach foo(...)} tag */ + export interface AttachTag extends BaseNode { + type: 'AttachTag'; + expression: Expression; + /** @internal */ + metadata: { + expression: ExpressionMetadata; + }; + } + /** An `animate:` directive */ export interface AnimateDirective extends BaseNode { type: 'AnimateDirective'; @@ -273,7 +283,7 @@ export namespace AST { interface BaseElement extends BaseNode { name: string; - attributes: Array; + attributes: Array; fragment: Fragment; } @@ -546,6 +556,7 @@ export namespace AST { | AST.Attribute | AST.SpreadAttribute | Directive + | AST.AttachTag | AST.Comment | Block; diff --git a/packages/svelte/src/constants.js b/packages/svelte/src/constants.js index 8861e440fc..2ecd4afee2 100644 --- a/packages/svelte/src/constants.js +++ b/packages/svelte/src/constants.js @@ -55,3 +55,5 @@ export const IGNORABLE_RUNTIME_WARNINGS = /** @type {const} */ ([ * TODO this is currently unused */ export const ELEMENTS_WITHOUT_TEXT = ['audio', 'datalist', 'dl', 'optgroup', 'select', 'video']; + +export const ATTACHMENT_KEY = '@attach'; diff --git a/packages/svelte/src/internal/client/dom/elements/attachments.js b/packages/svelte/src/internal/client/dom/elements/attachments.js new file mode 100644 index 0000000000..6e3089a384 --- /dev/null +++ b/packages/svelte/src/internal/client/dom/elements/attachments.js @@ -0,0 +1,15 @@ +import { effect } from '../../reactivity/effects.js'; + +/** + * @param {Element} node + * @param {() => (node: Element) => void} get_fn + */ +export function attach(node, get_fn) { + effect(() => { + const fn = get_fn(); + + // we use `&&` rather than `?.` so that things like + // `{@attach DEV && something_dev_only()}` work + return fn && fn(node); + }); +} diff --git a/packages/svelte/src/internal/client/dom/elements/attributes.js b/packages/svelte/src/internal/client/dom/elements/attributes.js index f63f55cc6e..3d1acbd31c 100644 --- a/packages/svelte/src/internal/client/dom/elements/attributes.js +++ b/packages/svelte/src/internal/client/dom/elements/attributes.js @@ -13,10 +13,11 @@ import { set_active_effect, set_active_reaction } from '../../runtime.js'; +import { attach } from './attachments.js'; import { clsx } from '../../../shared/attributes.js'; import { set_class } from './class.js'; import { set_style } from './style.js'; -import { NAMESPACE_HTML } from '../../../../constants.js'; +import { ATTACHMENT_KEY, NAMESPACE_HTML } from '../../../../constants.js'; export const CLASS = Symbol('class'); export const STYLE = Symbol('style'); @@ -446,6 +447,12 @@ export function set_attributes(element, prev, next, css_hash, skip_warning = fal set_hydrating(true); } + for (let symbol of Object.getOwnPropertySymbols(next)) { + if (symbol.description === ATTACHMENT_KEY) { + attach(element, () => next[symbol]); + } + } + return current; } diff --git a/packages/svelte/src/internal/client/index.js b/packages/svelte/src/internal/client/index.js index 14d6e29f5b..b5f746b276 100644 --- a/packages/svelte/src/internal/client/index.js +++ b/packages/svelte/src/internal/client/index.js @@ -1,3 +1,4 @@ +export { createAttachmentKey as attachment } from '../../attachments/index.js'; export { FILENAME, HMR, NAMESPACE_SVG } from '../../constants.js'; export { push, pop } from './context.js'; export { assign, assign_and, assign_or, assign_nullish } from './dev/assign.js'; @@ -22,6 +23,7 @@ export { element } from './dom/blocks/svelte-element.js'; export { head } from './dom/blocks/svelte-head.js'; export { append_styles } from './dom/css.js'; export { action } from './dom/elements/actions.js'; +export { attach } from './dom/elements/attachments.js'; export { remove_input_defaults, set_attribute, diff --git a/packages/svelte/src/internal/client/reactivity/props.js b/packages/svelte/src/internal/client/reactivity/props.js index 8bfd8f9e25..77c58720e1 100644 --- a/packages/svelte/src/internal/client/reactivity/props.js +++ b/packages/svelte/src/internal/client/reactivity/props.js @@ -218,9 +218,15 @@ const spread_props_handler = { for (let p of target.props) { if (is_function(p)) p = p(); + if (!p) continue; + for (const key in p) { if (!keys.includes(key)) keys.push(key); } + + for (const key of Object.getOwnPropertySymbols(p)) { + if (!keys.includes(key)) keys.push(key); + } } return keys; diff --git a/packages/svelte/tests/parser-modern/samples/attachments/input.svelte b/packages/svelte/tests/parser-modern/samples/attachments/input.svelte new file mode 100644 index 0000000000..9faae8d1bf --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/attachments/input.svelte @@ -0,0 +1 @@ +
{}} {@attach (node) => {}}>
diff --git a/packages/svelte/tests/parser-modern/samples/attachments/output.json b/packages/svelte/tests/parser-modern/samples/attachments/output.json new file mode 100644 index 0000000000..42e9880fcc --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/attachments/output.json @@ -0,0 +1,141 @@ +{ + "css": null, + "js": [], + "start": 0, + "end": 57, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "RegularElement", + "start": 0, + "end": 57, + "name": "div", + "attributes": [ + { + "type": "AttachTag", + "start": 5, + "end": 27, + "expression": { + "type": "ArrowFunctionExpression", + "start": 14, + "end": 26, + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 26 + } + }, + "id": null, + "expression": false, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 15, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 15 + }, + "end": { + "line": 1, + "column": 19 + } + }, + "name": "node" + } + ], + "body": { + "type": "BlockStatement", + "start": 24, + "end": 26, + "loc": { + "start": { + "line": 1, + "column": 24 + }, + "end": { + "line": 1, + "column": 26 + } + }, + "body": [] + } + } + }, + { + "type": "AttachTag", + "start": 28, + "end": 50, + "expression": { + "type": "ArrowFunctionExpression", + "start": 37, + "end": 49, + "loc": { + "start": { + "line": 1, + "column": 37 + }, + "end": { + "line": 1, + "column": 49 + } + }, + "id": null, + "expression": false, + "generator": false, + "async": false, + "params": [ + { + "type": "Identifier", + "start": 38, + "end": 42, + "loc": { + "start": { + "line": 1, + "column": 38 + }, + "end": { + "line": 1, + "column": 42 + } + }, + "name": "node" + } + ], + "body": { + "type": "BlockStatement", + "start": 47, + "end": 49, + "loc": { + "start": { + "line": 1, + "column": 47 + }, + "end": { + "line": 1, + "column": 49 + } + }, + "body": [] + } + } + } + ], + "fragment": { + "type": "Fragment", + "nodes": [] + } + } + ] + }, + "options": null +} diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-basic/_config.js b/packages/svelte/tests/runtime-runes/samples/attachment-basic/_config.js new file mode 100644 index 0000000000..1be4737069 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-basic/_config.js @@ -0,0 +1,6 @@ +import { test } from '../../test'; + +export default test({ + ssrHtml: `
`, + html: `
DIV
` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-basic/main.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-basic/main.svelte new file mode 100644 index 0000000000..1a1f74e4a9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-basic/main.svelte @@ -0,0 +1 @@ +
node.textContent = node.nodeName}>
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/Child.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/Child.svelte new file mode 100644 index 0000000000..6760da61fa --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/Child.svelte @@ -0,0 +1,5 @@ + + +
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/_config.js b/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/_config.js new file mode 100644 index 0000000000..23907c62d2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/_config.js @@ -0,0 +1,15 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: `
`, + + test({ target, assert, logs }) { + const button = target.querySelector('button'); + + assert.deepEqual(logs, ['one DIV']); + + flushSync(() => button?.click()); + assert.deepEqual(logs, ['one DIV', 'cleanup one', 'two DIV']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/main.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/main.svelte new file mode 100644 index 0000000000..d1d7e65126 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-component-spread/main.svelte @@ -0,0 +1,30 @@ + + + + + diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-component/Child.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-component/Child.svelte new file mode 100644 index 0000000000..6760da61fa --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-component/Child.svelte @@ -0,0 +1,5 @@ + + +
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-component/_config.js b/packages/svelte/tests/runtime-runes/samples/attachment-component/_config.js new file mode 100644 index 0000000000..b6ef016be5 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-component/_config.js @@ -0,0 +1,14 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + ssrHtml: `
`, + html: `
one
`, + + test({ target, assert }) { + const button = target.querySelector('button'); + + flushSync(() => button?.click()); + assert.htmlEqual(target.innerHTML, '
two
'); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-component/main.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-component/main.svelte new file mode 100644 index 0000000000..29e26689db --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-component/main.svelte @@ -0,0 +1,15 @@ + + + + + diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-reactive/_config.js b/packages/svelte/tests/runtime-runes/samples/attachment-reactive/_config.js new file mode 100644 index 0000000000..7d0502590b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-reactive/_config.js @@ -0,0 +1,14 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + ssrHtml: `
`, + html: `
1
`, + + test: ({ assert, target }) => { + const btn = target.querySelector('button'); + + flushSync(() => btn?.click()); + assert.htmlEqual(target.innerHTML, `
2
`); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-reactive/main.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-reactive/main.svelte new file mode 100644 index 0000000000..9fa3cfdb67 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-reactive/main.svelte @@ -0,0 +1,6 @@ + + +
node.textContent = value}>
+ diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-spread/_config.js b/packages/svelte/tests/runtime-runes/samples/attachment-spread/_config.js new file mode 100644 index 0000000000..96fc207450 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-spread/_config.js @@ -0,0 +1,8 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + test({ assert, logs, target }) { + assert.deepEqual(logs, ['hello']); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-spread/main.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-spread/main.svelte new file mode 100644 index 0000000000..dbd8c47ada --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-spread/main.svelte @@ -0,0 +1,9 @@ + + +
diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-svelte-element/_config.js b/packages/svelte/tests/runtime-runes/samples/attachment-svelte-element/_config.js new file mode 100644 index 0000000000..1be4737069 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-svelte-element/_config.js @@ -0,0 +1,6 @@ +import { test } from '../../test'; + +export default test({ + ssrHtml: `
`, + html: `
DIV
` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-svelte-element/main.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-svelte-element/main.svelte new file mode 100644 index 0000000000..bd4b52342f --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-svelte-element/main.svelte @@ -0,0 +1 @@ + node.textContent = node.nodeName}> diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index b233cfcc0b..ff9764b88b 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -624,6 +624,44 @@ declare module 'svelte/animate' { export {}; } +declare module 'svelte/attachments' { + /** + * An [attachment](https://svelte.dev/docs/svelte/@attach) is a function that runs when an element is mounted + * to the DOM, and optionally returns a function that is called when the element is later removed. + * + * It can be attached to an element with an `{@attach ...}` tag, or by spreading an object containing + * a property created with [`createAttachmentKey`](https://svelte.dev/docs/svelte/svelte-attachments#createAttachmentKey). + */ + export interface Attachment { + (element: T): void | (() => void); + } + /** + * Creates an object key that will be recognised as an attachment when the object is spread onto an element, + * as a programmatic alternative to using `{@attach ...}`. This can be useful for library authors, though + * is generally not needed when building an app. + * + * ```svelte + * + * + * + * ``` + * @since 5.29 + */ + export function createAttachmentKey(): symbol; + + export {}; +} + declare module 'svelte/compiler' { import type { SourceMap } from 'magic-string'; import type { ArrayExpression, ArrowFunctionExpression, VariableDeclaration, VariableDeclarator, Expression, Identifier, MemberExpression, Node, ObjectExpression, Pattern, Program, ChainExpression, SimpleCallExpression, SequenceExpression } from 'estree'; @@ -1055,6 +1093,12 @@ declare module 'svelte/compiler' { expression: SimpleCallExpression | (ChainExpression & { expression: SimpleCallExpression }); } + /** A `{@attach foo(...)} tag */ + export interface AttachTag extends BaseNode { + type: 'AttachTag'; + expression: Expression; + } + /** An `animate:` directive */ export interface AnimateDirective extends BaseNode { type: 'AnimateDirective'; @@ -1137,7 +1181,7 @@ declare module 'svelte/compiler' { interface BaseElement extends BaseNode { name: string; - attributes: Array; + attributes: Array; fragment: Fragment; } @@ -1327,6 +1371,7 @@ declare module 'svelte/compiler' { | AST.Attribute | AST.SpreadAttribute | Directive + | AST.AttachTag | AST.Comment | Block; From 51b858dfd1e38f92c0086146e5fa7f55ed7a8069 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 13:36:51 -0400 Subject: [PATCH 0402/1429] Version Packages (#15914) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/poor-days-pay.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/poor-days-pay.md diff --git a/.changeset/poor-days-pay.md b/.changeset/poor-days-pay.md deleted file mode 100644 index 8fbff10586..0000000000 --- a/.changeset/poor-days-pay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: attachments diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 935d2b7cc6..136219af42 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.29.0 + +### Minor Changes + +- feat: attachments ([#15000](https://github.com/sveltejs/svelte/pull/15000)) + ## 5.28.7 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 0ec1f9204d..312aa50c25 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.28.7", + "version": "5.29.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index d7c041762f..4499374896 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.28.7'; +export const VERSION = '5.29.0'; export const PUBLIC_VERSION = '5'; From 66a21552f64eea77a46e22a46eb46d4ff010d949 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Wed, 14 May 2025 21:58:58 +0200 Subject: [PATCH 0403/1429] feat: allow generics on snippets (#15915) * feat: allow generics on snippets * chore: fix lint * reuse bracket matching logic * remove some unused stuff * chore: update name, test and types * chore: fix lint --------- Co-authored-by: Rich Harris --- .changeset/ten-colts-grab.md | 5 + .../compiler/phases/1-parse/read/context.js | 75 +---- .../src/compiler/phases/1-parse/state/tag.js | 20 ++ .../compiler/phases/1-parse/utils/bracket.js | 113 +++++-- .../svelte/src/compiler/types/template.d.ts | 1 + .../samples/generic-snippets/input.svelte | 10 + .../samples/generic-snippets/output.json | 299 ++++++++++++++++++ packages/svelte/types/index.d.ts | 1 + 8 files changed, 421 insertions(+), 103 deletions(-) create mode 100644 .changeset/ten-colts-grab.md create mode 100644 packages/svelte/tests/parser-modern/samples/generic-snippets/input.svelte create mode 100644 packages/svelte/tests/parser-modern/samples/generic-snippets/output.json diff --git a/.changeset/ten-colts-grab.md b/.changeset/ten-colts-grab.md new file mode 100644 index 0000000000..6e0e20bc84 --- /dev/null +++ b/.changeset/ten-colts-grab.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: allow generics on snippets diff --git a/packages/svelte/src/compiler/phases/1-parse/read/context.js b/packages/svelte/src/compiler/phases/1-parse/read/context.js index f4c73dcf40..b118901830 100644 --- a/packages/svelte/src/compiler/phases/1-parse/read/context.js +++ b/packages/svelte/src/compiler/phases/1-parse/read/context.js @@ -1,7 +1,7 @@ /** @import { Location } from 'locate-character' */ /** @import { Pattern } from 'estree' */ /** @import { Parser } from '../index.js' */ -import { is_bracket_open, is_bracket_close, get_bracket_close } from '../utils/bracket.js'; +import { match_bracket } from '../utils/bracket.js'; import { parse_expression_at } from '../acorn.js'; import { regex_not_newline_characters } from '../../patterns.js'; import * as e from '../../../errors.js'; @@ -33,7 +33,9 @@ export default function read_pattern(parser) { }; } - if (!is_bracket_open(parser.template[i])) { + const char = parser.template[i]; + + if (char !== '{' && char !== '[') { e.expected_pattern(i); } @@ -71,75 +73,6 @@ export default function read_pattern(parser) { } } -/** - * @param {Parser} parser - * @param {number} start - */ -function match_bracket(parser, start) { - const bracket_stack = []; - - let i = start; - - while (i < parser.template.length) { - let char = parser.template[i++]; - - if (char === "'" || char === '"' || char === '`') { - i = match_quote(parser, i, char); - continue; - } - - if (is_bracket_open(char)) { - bracket_stack.push(char); - } else if (is_bracket_close(char)) { - const popped = /** @type {string} */ (bracket_stack.pop()); - const expected = /** @type {string} */ (get_bracket_close(popped)); - - if (char !== expected) { - e.expected_token(i - 1, expected); - } - - if (bracket_stack.length === 0) { - return i; - } - } - } - - e.unexpected_eof(parser.template.length); -} - -/** - * @param {Parser} parser - * @param {number} start - * @param {string} quote - */ -function match_quote(parser, start, quote) { - let is_escaped = false; - let i = start; - - while (i < parser.template.length) { - const char = parser.template[i++]; - - if (is_escaped) { - is_escaped = false; - continue; - } - - if (char === quote) { - return i; - } - - if (char === '\\') { - is_escaped = true; - } - - if (quote === '`' && char === '$' && parser.template[i] === '{') { - i = match_bracket(parser, i); - } - } - - e.unterminated_string_constant(start); -} - /** * @param {Parser} parser * @returns {any} diff --git a/packages/svelte/src/compiler/phases/1-parse/state/tag.js b/packages/svelte/src/compiler/phases/1-parse/state/tag.js index 0eb98c27e8..4153463c83 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/tag.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/tag.js @@ -8,9 +8,12 @@ import { parse_expression_at } from '../acorn.js'; import read_pattern from '../read/context.js'; import read_expression, { get_loose_identifier } from '../read/expression.js'; import { create_fragment } from '../utils/create.js'; +import { match_bracket } from '../utils/bracket.js'; const regex_whitespace_with_closing_curly_brace = /^\s*}/; +const pointy_bois = { '<': '>' }; + /** @param {Parser} parser */ export default function tag(parser) { const start = parser.index; @@ -351,6 +354,22 @@ function open(parser) { const params_start = parser.index; + // snippets could have a generic signature, e.g. `#snippet foo(...)` + /** @type {string | undefined} */ + let type_params; + + // if we match a generic opening + if (parser.ts && parser.match('<')) { + const start = parser.index; + const end = match_bracket(parser, start, pointy_bois); + + type_params = parser.template.slice(start + 1, end - 1); + + parser.index = end; + } + + parser.allow_whitespace(); + const matched = parser.eat('(', true, false); if (matched) { @@ -388,6 +407,7 @@ function open(parser) { end: name_end, name }, + typeParams: type_params, parameters: function_expression.params, body: create_fragment(), metadata: { diff --git a/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js b/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js index b7c8cb43cd..8c69a58c99 100644 --- a/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js +++ b/packages/svelte/src/compiler/phases/1-parse/utils/bracket.js @@ -1,34 +1,5 @@ -const SQUARE_BRACKET_OPEN = '['; -const SQUARE_BRACKET_CLOSE = ']'; -const CURLY_BRACKET_OPEN = '{'; -const CURLY_BRACKET_CLOSE = '}'; -const PARENTHESES_OPEN = '('; -const PARENTHESES_CLOSE = ')'; - -/** @param {string} char */ -export function is_bracket_open(char) { - return char === SQUARE_BRACKET_OPEN || char === CURLY_BRACKET_OPEN; -} - -/** @param {string} char */ -export function is_bracket_close(char) { - return char === SQUARE_BRACKET_CLOSE || char === CURLY_BRACKET_CLOSE; -} - -/** @param {string} open */ -export function get_bracket_close(open) { - if (open === SQUARE_BRACKET_OPEN) { - return SQUARE_BRACKET_CLOSE; - } - - if (open === CURLY_BRACKET_OPEN) { - return CURLY_BRACKET_CLOSE; - } - - if (open === PARENTHESES_OPEN) { - return PARENTHESES_CLOSE; - } -} +/** @import { Parser } from '../index.js' */ +import * as e from '../../../errors.js'; /** * @param {number} num @@ -121,7 +92,7 @@ function count_leading_backslashes(string, search_start_index) { * @returns {number | undefined} The index of the closing bracket, or undefined if not found. */ export function find_matching_bracket(template, index, open) { - const close = get_bracket_close(open); + const close = default_brackets[open]; let brackets = 1; let i = index; while (brackets > 0 && i < template.length) { @@ -162,3 +133,81 @@ export function find_matching_bracket(template, index, open) { } return undefined; } + +/** @type {Record} */ +const default_brackets = { + '{': '}', + '(': ')', + '[': ']' +}; + +/** + * @param {Parser} parser + * @param {number} start + * @param {Record} brackets + */ +export function match_bracket(parser, start, brackets = default_brackets) { + const close = Object.values(brackets); + const bracket_stack = []; + + let i = start; + + while (i < parser.template.length) { + let char = parser.template[i++]; + + if (char === "'" || char === '"' || char === '`') { + i = match_quote(parser, i, char); + continue; + } + + if (char in brackets) { + bracket_stack.push(char); + } else if (close.includes(char)) { + const popped = /** @type {string} */ (bracket_stack.pop()); + const expected = /** @type {string} */ (brackets[popped]); + + if (char !== expected) { + e.expected_token(i - 1, expected); + } + + if (bracket_stack.length === 0) { + return i; + } + } + } + + e.unexpected_eof(parser.template.length); +} + +/** + * @param {Parser} parser + * @param {number} start + * @param {string} quote + */ +function match_quote(parser, start, quote) { + let is_escaped = false; + let i = start; + + while (i < parser.template.length) { + const char = parser.template[i++]; + + if (is_escaped) { + is_escaped = false; + continue; + } + + if (char === quote) { + return i; + } + + if (char === '\\') { + is_escaped = true; + } + + if (quote === '`' && char === '$' && parser.template[i] === '{') { + i = match_bracket(parser, i); + } + } + + e.unterminated_string_constant(start); +} diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index 54fdda92b8..6dec1f2dbe 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -468,6 +468,7 @@ export namespace AST { type: 'SnippetBlock'; expression: Identifier; parameters: Pattern[]; + typeParams?: string; body: Fragment; /** @internal */ metadata: { diff --git a/packages/svelte/tests/parser-modern/samples/generic-snippets/input.svelte b/packages/svelte/tests/parser-modern/samples/generic-snippets/input.svelte new file mode 100644 index 0000000000..4ee619728d --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/generic-snippets/input.svelte @@ -0,0 +1,10 @@ + + +{#snippet generic(val: T)} + {val} +{/snippet} + +{#snippet complex_generic">>(val: T)} + {val} +{/snippet} \ No newline at end of file diff --git a/packages/svelte/tests/parser-modern/samples/generic-snippets/output.json b/packages/svelte/tests/parser-modern/samples/generic-snippets/output.json new file mode 100644 index 0000000000..b66ee7288f --- /dev/null +++ b/packages/svelte/tests/parser-modern/samples/generic-snippets/output.json @@ -0,0 +1,299 @@ +{ + "css": null, + "js": [], + "start": 30, + "end": 192, + "type": "Root", + "fragment": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 28, + "end": 30, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "SnippetBlock", + "start": 30, + "end": 92, + "expression": { + "type": "Identifier", + "start": 40, + "end": 47, + "name": "generic" + }, + "typeParams": "T extends string", + "parameters": [ + { + "type": "Identifier", + "start": 66, + "end": 72, + "loc": { + "start": { + "line": 4, + "column": 36 + }, + "end": { + "line": 4, + "column": 42 + } + }, + "name": "val", + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 69, + "end": 72, + "loc": { + "start": { + "line": 4, + "column": 39 + }, + "end": { + "line": 4, + "column": 42 + } + }, + "typeAnnotation": { + "type": "TSTypeReference", + "start": 71, + "end": 72, + "loc": { + "start": { + "line": 4, + "column": 41 + }, + "end": { + "line": 4, + "column": 42 + } + }, + "typeName": { + "type": "Identifier", + "start": 71, + "end": 72, + "loc": { + "start": { + "line": 4, + "column": 41 + }, + "end": { + "line": 4, + "column": 42 + } + }, + "name": "T" + } + } + } + } + ], + "body": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 74, + "end": 76, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "ExpressionTag", + "start": 76, + "end": 81, + "expression": { + "type": "Identifier", + "start": 77, + "end": 80, + "loc": { + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 5 + } + }, + "name": "val" + } + }, + { + "type": "Text", + "start": 81, + "end": 82, + "raw": "\n", + "data": "\n" + } + ] + } + }, + { + "type": "Text", + "start": 92, + "end": 94, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "SnippetBlock", + "start": 94, + "end": 192, + "expression": { + "type": "Identifier", + "start": 104, + "end": 119, + "name": "complex_generic" + }, + "typeParams": "T extends { bracket: \"<\" } | \"<\" | Set<\"<>\">", + "parameters": [ + { + "type": "Identifier", + "start": 166, + "end": 172, + "loc": { + "start": { + "line": 8, + "column": 72 + }, + "end": { + "line": 8, + "column": 78 + } + }, + "name": "val", + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 169, + "end": 172, + "loc": { + "start": { + "line": 8, + "column": 75 + }, + "end": { + "line": 8, + "column": 78 + } + }, + "typeAnnotation": { + "type": "TSTypeReference", + "start": 171, + "end": 172, + "loc": { + "start": { + "line": 8, + "column": 77 + }, + "end": { + "line": 8, + "column": 78 + } + }, + "typeName": { + "type": "Identifier", + "start": 171, + "end": 172, + "loc": { + "start": { + "line": 8, + "column": 77 + }, + "end": { + "line": 8, + "column": 78 + } + }, + "name": "T" + } + } + } + } + ], + "body": { + "type": "Fragment", + "nodes": [ + { + "type": "Text", + "start": 174, + "end": 176, + "raw": "\n\t", + "data": "\n\t" + }, + { + "type": "ExpressionTag", + "start": 176, + "end": 181, + "expression": { + "type": "Identifier", + "start": 177, + "end": 180, + "loc": { + "start": { + "line": 9, + "column": 2 + }, + "end": { + "line": 9, + "column": 5 + } + }, + "name": "val" + } + }, + { + "type": "Text", + "start": 181, + "end": 182, + "raw": "\n", + "data": "\n" + } + ] + } + } + ] + }, + "options": null, + "instance": { + "type": "Script", + "start": 0, + "end": 28, + "context": "default", + "content": { + "type": "Program", + "start": 18, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 0 + } + }, + "body": [], + "sourceType": "module" + }, + "attributes": [ + { + "type": "Attribute", + "start": 8, + "end": 17, + "name": "lang", + "value": [ + { + "start": 14, + "end": 16, + "type": "Text", + "raw": "ts", + "data": "ts" + } + ] + } + ] + } +} diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index ff9764b88b..bb958c5108 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1301,6 +1301,7 @@ declare module 'svelte/compiler' { type: 'SnippetBlock'; expression: Identifier; parameters: Pattern[]; + typeParams?: string; body: Fragment; } From e2a13a3beb246fe3f3e471353921a2db5ca1c57d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 16:03:05 -0400 Subject: [PATCH 0404/1429] Version Packages (#15916) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/ten-colts-grab.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/ten-colts-grab.md diff --git a/.changeset/ten-colts-grab.md b/.changeset/ten-colts-grab.md deleted file mode 100644 index 6e0e20bc84..0000000000 --- a/.changeset/ten-colts-grab.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: allow generics on snippets diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 136219af42..b25235276b 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.30.0 + +### Minor Changes + +- feat: allow generics on snippets ([#15915](https://github.com/sveltejs/svelte/pull/15915)) + ## 5.29.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 312aa50c25..5c27d88952 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.29.0", + "version": "5.30.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 4499374896..69492b1bf2 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.29.0'; +export const VERSION = '5.30.0'; export const PUBLIC_VERSION = '5'; From 43e32ba2ce7bf30a4854af9318c8388a4d1265d6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 14 May 2025 17:37:25 -0400 Subject: [PATCH 0405/1429] docs: update attach example (#15920) --- .../docs/03-template-syntax/09-@attach.md | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/documentation/docs/03-template-syntax/09-@attach.md b/documentation/docs/03-template-syntax/09-@attach.md index c988386933..2df0882e34 100644 --- a/documentation/docs/03-template-syntax/09-@attach.md +++ b/documentation/docs/03-template-syntax/09-@attach.md @@ -59,14 +59,10 @@ Since the `tooltip(content)` expression runs inside an [effect]($effect), the at ## Inline attachments -Attachments can also be created inline ([demo](/playground/untitled#H4sIAAAAAAAAE71WbW_aSBD-Kyt0VaBJyGKbqoUkOhdI68qGUkh6pPSDMY6xYwyH12Ab8d9vZtYE6DX38aQQe3fennlm1jvbUmTP3VKj9KcthO3MShelJz9041Ljx7YksiWKcAP2C0V9uazGazcUuDexY_d3-84iEm4kwE3pOnZW_lLcjqOx8OfLxUqwLVvafiTYjj2tFnN2Vr3yVvbUB4NqEJ81x9H11cEounbsaG3HaL_xp2J2s1WVHa5mru_NxMtyW6TAytKgwm5u2RYlYwF4YsEIVSrYDZMaVc8VLblXPlOmZ5UmxkP9P9ynJ9cR5fKxk7EIXQGQIV9wsXL_TtxY6JE_t4W_iO5wv_yURA6uWLhYLMuicrAdi_-2RAMCUGgTReUC8gUTB9mueC2WK1ckq4j9AhVytiPHDX_Fh_-PXBVvhcsdEHl7fSXZkeTHIgtdKp7c3UegUjRYjfM3hQ9ZjpOty407efbF5dyOnxssWYXlcWlqC7sBmDz3Kl575-k8bGIXvdMuvn7uKo_Zx3Ayv_Mnn-7FaH4X2Mo0m6gPyWObR5P5g2q0dc9q6fVeS8uMdifttRxvOg_DKf-ydkEHZBuj_ayZgeFZw472JfuoTb6niZPzyP78jTvtxdpUp-o0q6tWVl87c2dtBfrGan3Ip3Mn-hqkm9Ff3xbGp_6HLwqvWwOtDnFqZvAYmMPOxgyehTW8T7oZzy1fU8yhAXuW6La9xPJ5arW0fNLiWTfTamCnmsED2DlJd6BzM3DA1gBbPQVbDv444Qw6iTXgKfjxwC43B5QbyDzPgrXRNlAm0MZoW0nX5_B06Ak-Mc-k10L7kQe81M3gHvYAz0CvkTwAvC2IOdDT7kADDq0MdSHvxMp0XnAJeXyLrQCx8hTxj3J6L2Igbp5KDIRbSNw6YSPcuDfsI5ac8KI80yFWX0AeitHuox4-pa-BpoEvzKMOOSMfWDeBGIFXwP4gzOE9cu71kF_FEpgf8AF-eYq4wQZ5z8A_2HtUF_LRwjXEaYFvrBnkA7rg00L9pCfjJYjHxNzmG8qbeBlgjndBwT1ypyCG7gtPngcY-aTd8TBPM-h41vfiiX6hjsAT9g3yw4t9ReLGdR_rSjUEOfBDtQRcyKUhSI4cwG_SNlTiD3vou5XiO2IB_zniBhusJeanORnHPpLcU92oZ9F3RjUiTizkDnx2BPUv4KK6Qc9RHIwbTGPZ632vCzqjDHlxEFOK9l3C-Yx1UiQ_XDtgkjUkf0MjR59QJ5XiEqZ-geMZasBzmds9YIK-xadPfIkenTsPsWPP_YYHB2OkxXlIqT6DopYDXaOa-1i_jvwW0JkiPHhG8AwUsfpYV6gF4tFzeXYQD9ZDo76kHoV1l3r5MYa9WtG3VA-sPfYKxW5xhbiRvYm9IqhX8HwO8Ix0UL8471hLOtd16mPip4N5UR6AgRdnJ8dvCMip1vCjbw3khfFS6h9lI-jswjnHnpY16yPHWdGPGeHzMcdZTj1J_d3B_JVRjvnopCv5wD7RVdLDPqG4kscTTpQNfvPgbI3g_f-pS4--a3TGUynH_hvJb9QpDzXJg3fo3eyld1Xq3YHjmbn23lTh7sm1m3Gpwur8Df2umMq16vtlyqLF5cpdurb4zb12Gfu522Dv-HruR_IWpQGmuDdhGMILvNQQq8TdXbwyVB3NP6dT1angaKxyUxqlXuaNf40L8qKWg8-W0XV9weQdDYPXzX4YqsprvXlQpru5Dbf0kRIMSsZ-u8wvGPydeNxPTk-LFSvjlLQEY96Ex_XBXxWv_mroRp6Yoej8hmmV0wnNB7MlEK81j3dT2PXZGxnyRJKBpOyDAYkq7Pb2FsLupzips3KnoPVOY-esXFPes7csrewtYA8Eb5lli1k19qOyAAkMMLxyEsZbuW70i5MMnRR8HntxFvErXiZhguMfmL8gPOXmB3DC-E8aEafNVzVqqEGQXtdRUAcDvq6ioopSr-97tugAqvcyOar3iy3VnZLanbb1T1jZfrjxo2mp8WSHsbv7Bx1mHBBZDAAA)): +Attachments can also be created inline ([demo](/playground/untitled#H4sIAAAAAAAAE71Wf3OaWBT9KoyTTnW3MS-I3dYmnWXVtnRAazRJzbozRSQEApiRhwKO333vuY8m225m_9yZGOT9OPfcc84D943UTfxGr_G7K6Xr3TVeNW7D2M8avT_3DVk-YAoDNF4vNB8e2tnWjyXGlm7mPzfurVPpp5JgGmeZtwkf5PtFupCxLzVvHa832rl2lElX-s2Xm2DZFNqp_hs-rZetd4v07ORpT3qmQHu7MF2td0BZp8k6z_xkvfXP902_pZ2_1_aYWEiqm0kN8I4r79qbdZ6umnq3q_2iNf22F4dE6qt2oimwdpim_uY6XMm7Fuo-IQT_iTD_CeGTHwZ38ieIJUFQRxirR1Xf39Dw0X5z0I72Af4tD61vvPNwWKQnqmfPTbduhsEd2J3vO_oBd3dc6fF2X7umNdWGf0vBRhSS6qoV7cCXfTXWfKmvWG61_si_vfU92Wz-E4RhsLhNIYinsox9QKGVd8-tuACCeKXRX12P-T_eKf7fhTq0Hvt-f3ailtSeoxJHRo1-58NoPe1UiBc1hkL8Yeh45y_vQ3mcuNl9T8s3cXPRWLnS7YWJG_gn2Tb4tUjid8jua-PVl08j_ab8I14mH8Llx0s5Tz5Err4ql52r_GYg0mVy1bEGZuD0ze64b5TWYFiM-16wSuJ4JT5vfVpDcztrcG_YkRU4s6HxufzDWF4XuVeJ1P10IbzBemt3Vp1V2e04ZXfrJd7Wicyd039brRIv_RIVu_nXi7X1cfL2sy66ztToUp1TO7qJ7NlwZ0f30pld5qNSVE5o6PbMojFHjgZB7oSicPpGteyLclQap7SvY0dXtM_LR1NT2JFHey3aaxa0VxCeYJ7RMHemoiCcgPZV9pR7o7kgcOjeGliYk9hjDZx8FAq6enwlTPSZj_vYPw9Il64dXdIY8ZmapzwfEd8-1ZyaxWhqkIZOibXUd-6Upqi1pD4uMicCV1GA_7zi73UN8BaF4sC8peJtMjfmjbHZBFwq5ov50qRaE0l96NZggnW4KqypYRAW-uhSz9ADvklwJF2J-5W0Z5fQPBhDX92R6I_0IFxRgDftge4l4dP-gH1hjD7uqU6fsOEZ9UNrCdPB-nys6uXgY6O3ZMd9sy5T9PghqrWHdjo4jB51CgLiKJaDYYA-7WgYONf1FbjkI-mE3EAfUY_rijfuJ_CVPaR50oe9JF7Q0pI8Dw3osxxYHdYPGbp2CnwHF8KvwJv2wEv0Z3ilQI6U9uwbZxbYJXvEmjjQjjCHkvNLvNg3yhzXQd1olamsT4IRrZmX0MUDpwL7R8zzHj7pSh9hPHFSHjLezKqAST51uC5zmtQ87skDUaneLokT5RbXkPWSYz53Abgjc8_o4KFGUZ-Hgv2Z1l5OTYM9D-HfUD0L-EwxH5wRnIG61gS-khfgY1bq7IAP_DA4l5xRuh9xlm8yGjutc8t-wHtkhWv3hc7aqGwiK5KzgvM5xRkZYn193uEln-su55j1GaIv7oM4iPrsVHiG0Dx7TR9-1lBfqFdwfvSd5LNL5xyZVp5NoHFZ57FkfiF6vKs4k5zvIfrX5xX6MXmt0gM5MTu8DjnhukrHHzTRd3jm0dma0_f_x5cxP9f4jBdqHvmbq2fUjzqcKh2Cp-yWj9ntcHanXmBXxhu7Q--eyjhfNFpaV7zgz4nWEUb7zUOhpevjjf_gu_KZ99pxFlZ-T3sttkmYqrco_26q35v0Ewzv5EZPbnL_8BfduWGMnyyN3q0bZ_7hb_7KG_L4CQAA)): ```svelte - - { - let frame = requestAnimationFrame(function loop(t) { - frame = requestAnimationFrame(loop); - paint(context, t); - }); - - return () => { - cancelAnimationFrame(frame); - }; + context.fillStyle = color; + context.fillRect(0, 0, canvas.width, canvas.height); }); }} > ``` +> [!NOTE] +> The nested effect runs whenever `color` changes, while the outer effect (where `canvas.getContext(...)` is called) only runs once, since it doesn't read any reactive state. + ## Passing attachments to components When used on a component, `{@attach ...}` will create a prop whose key is a [`Symbol`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol). If the component then [spreads](/tutorial/svelte/spread-props) props onto an element, the element will receive those attachments. From 623cfb06531873c9b765c77572dc2a2f2abece36 Mon Sep 17 00:00:00 2001 From: Scott Wu Date: Thu, 15 May 2025 06:01:24 +0800 Subject: [PATCH 0406/1429] docs: Clarify when is a variable proxied (#15804) * Update 02-$state.md * Update 02-$state.md * Update 02-$state.md * Update documentation/docs/02-runes/02-$state.md --------- Co-authored-by: Rich Harris --- documentation/docs/02-runes/02-$state.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/02-runes/02-$state.md b/documentation/docs/02-runes/02-$state.md index 16630a977b..aac006d2f5 100644 --- a/documentation/docs/02-runes/02-$state.md +++ b/documentation/docs/02-runes/02-$state.md @@ -20,7 +20,7 @@ Unlike other frameworks you may have encountered, there is no API for interactin If `$state` is used with an array or a simple object, the result is a deeply reactive _state proxy_. [Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) allow Svelte to run code when you read or write properties, including via methods like `array.push(...)`, triggering granular updates. -> [!NOTE] Classes like `Set` and `Map` will not be proxied, but Svelte provides reactive implementations for various built-ins like these that can be imported from [`svelte/reactivity`](./svelte-reactivity). +> [!NOTE] Class instances are not proxied. You can create [reactive state fields](#Classes) on classes that you define. Svelte provides reactive implementations of built-ins like `Set` and `Map` that can be imported from [`svelte/reactivity`](svelte-reactivity). State is proxified recursively until Svelte finds something other than an array or simple object. In a case like this... From 0e22b541104783d5ac5079272cefe20d79de5fe7 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Thu, 15 May 2025 00:08:36 +0200 Subject: [PATCH 0407/1429] fix: add `typeParams` to `SnippetBlock` for legacy parser (#15921) --- .changeset/polite-tips-enjoy.md | 5 + packages/svelte/src/compiler/legacy.js | 3 +- .../samples/generic-snippets/input.svelte | 10 + .../samples/generic-snippets/output.json | 244 ++++++++++++++++++ 4 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 .changeset/polite-tips-enjoy.md create mode 100644 packages/svelte/tests/parser-legacy/samples/generic-snippets/input.svelte create mode 100644 packages/svelte/tests/parser-legacy/samples/generic-snippets/output.json diff --git a/.changeset/polite-tips-enjoy.md b/.changeset/polite-tips-enjoy.md new file mode 100644 index 0000000000..ab95388f7f --- /dev/null +++ b/.changeset/polite-tips-enjoy.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: add `typeParams` to `SnippetBlock` for legacy parser diff --git a/packages/svelte/src/compiler/legacy.js b/packages/svelte/src/compiler/legacy.js index e3f88c8f1d..f6b7e4b054 100644 --- a/packages/svelte/src/compiler/legacy.js +++ b/packages/svelte/src/compiler/legacy.js @@ -378,7 +378,8 @@ export function convert(source, ast) { end: node.end, expression: node.expression, parameters: node.parameters, - children: node.body.nodes.map((child) => visit(child)) + children: node.body.nodes.map((child) => visit(child)), + typeParams: node.typeParams }; }, // @ts-expect-error diff --git a/packages/svelte/tests/parser-legacy/samples/generic-snippets/input.svelte b/packages/svelte/tests/parser-legacy/samples/generic-snippets/input.svelte new file mode 100644 index 0000000000..4ee619728d --- /dev/null +++ b/packages/svelte/tests/parser-legacy/samples/generic-snippets/input.svelte @@ -0,0 +1,10 @@ + + +{#snippet generic(val: T)} + {val} +{/snippet} + +{#snippet complex_generic">>(val: T)} + {val} +{/snippet} \ No newline at end of file diff --git a/packages/svelte/tests/parser-legacy/samples/generic-snippets/output.json b/packages/svelte/tests/parser-legacy/samples/generic-snippets/output.json new file mode 100644 index 0000000000..37fb499e7b --- /dev/null +++ b/packages/svelte/tests/parser-legacy/samples/generic-snippets/output.json @@ -0,0 +1,244 @@ +{ + "html": { + "type": "Fragment", + "start": 30, + "end": 192, + "children": [ + { + "type": "Text", + "start": 28, + "end": 30, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "SnippetBlock", + "start": 30, + "end": 92, + "expression": { + "type": "Identifier", + "start": 40, + "end": 47, + "name": "generic" + }, + "parameters": [ + { + "type": "Identifier", + "start": 66, + "end": 72, + "loc": { + "start": { + "line": 4, + "column": 36 + }, + "end": { + "line": 4, + "column": 42 + } + }, + "name": "val", + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 69, + "end": 72, + "loc": { + "start": { + "line": 4, + "column": 39 + }, + "end": { + "line": 4, + "column": 42 + } + }, + "typeAnnotation": { + "type": "TSTypeReference", + "start": 71, + "end": 72, + "loc": { + "start": { + "line": 4, + "column": 41 + }, + "end": { + "line": 4, + "column": 42 + } + }, + "typeName": { + "type": "Identifier", + "start": 71, + "end": 72, + "loc": { + "start": { + "line": 4, + "column": 41 + }, + "end": { + "line": 4, + "column": 42 + } + }, + "name": "T" + } + } + } + } + ], + "children": [ + { + "type": "MustacheTag", + "start": 76, + "end": 81, + "expression": { + "type": "Identifier", + "start": 77, + "end": 80, + "loc": { + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 5 + } + }, + "name": "val" + } + } + ], + "typeParams": "T extends string" + }, + { + "type": "Text", + "start": 92, + "end": 94, + "raw": "\n\n", + "data": "\n\n" + }, + { + "type": "SnippetBlock", + "start": 94, + "end": 192, + "expression": { + "type": "Identifier", + "start": 104, + "end": 119, + "name": "complex_generic" + }, + "parameters": [ + { + "type": "Identifier", + "start": 166, + "end": 172, + "loc": { + "start": { + "line": 8, + "column": 72 + }, + "end": { + "line": 8, + "column": 78 + } + }, + "name": "val", + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start": 169, + "end": 172, + "loc": { + "start": { + "line": 8, + "column": 75 + }, + "end": { + "line": 8, + "column": 78 + } + }, + "typeAnnotation": { + "type": "TSTypeReference", + "start": 171, + "end": 172, + "loc": { + "start": { + "line": 8, + "column": 77 + }, + "end": { + "line": 8, + "column": 78 + } + }, + "typeName": { + "type": "Identifier", + "start": 171, + "end": 172, + "loc": { + "start": { + "line": 8, + "column": 77 + }, + "end": { + "line": 8, + "column": 78 + } + }, + "name": "T" + } + } + } + } + ], + "children": [ + { + "type": "MustacheTag", + "start": 176, + "end": 181, + "expression": { + "type": "Identifier", + "start": 177, + "end": 180, + "loc": { + "start": { + "line": 9, + "column": 2 + }, + "end": { + "line": 9, + "column": 5 + } + }, + "name": "val" + } + } + ], + "typeParams": "T extends { bracket: \"<\" } | \"<\" | Set<\"<>\">" + } + ] + }, + "instance": { + "type": "Script", + "start": 0, + "end": 28, + "context": "default", + "content": { + "type": "Program", + "start": 18, + "end": 19, + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 2, + "column": 0 + } + }, + "body": [], + "sourceType": "module" + } + } +} From 271fd336340acaa28007f426fa76d90a1fc904c7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 14 May 2025 18:27:24 -0400 Subject: [PATCH 0408/1429] Version Packages (#15922) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/polite-tips-enjoy.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/polite-tips-enjoy.md diff --git a/.changeset/polite-tips-enjoy.md b/.changeset/polite-tips-enjoy.md deleted file mode 100644 index ab95388f7f..0000000000 --- a/.changeset/polite-tips-enjoy.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: add `typeParams` to `SnippetBlock` for legacy parser diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index b25235276b..2b65a1889f 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.30.1 + +### Patch Changes + +- fix: add `typeParams` to `SnippetBlock` for legacy parser ([#15921](https://github.com/sveltejs/svelte/pull/15921)) + ## 5.30.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 5c27d88952..b6e8d06c63 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.30.0", + "version": "5.30.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 69492b1bf2..b849318036 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.30.0'; +export const VERSION = '5.30.1'; export const PUBLIC_VERSION = '5'; From ae71152013ef47cb917c371156a69224248d174c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 15 May 2025 11:18:55 -0400 Subject: [PATCH 0409/1429] docs: clarify keyed each blocks (#15923) --- documentation/docs/03-template-syntax/03-each.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/documentation/docs/03-template-syntax/03-each.md b/documentation/docs/03-template-syntax/03-each.md index 70666f6a57..006cadd152 100644 --- a/documentation/docs/03-template-syntax/03-each.md +++ b/documentation/docs/03-template-syntax/03-each.md @@ -43,7 +43,9 @@ An each block can also specify an _index_, equivalent to the second argument in {#each expression as name, index (key)}...{/each} ``` -If a _key_ expression is provided — which must uniquely identify each list item — Svelte will use it to diff the list when data changes, rather than adding or removing items at the end. The key can be any object, but strings and numbers are recommended since they allow identity to persist when the objects themselves change. +If a _key_ expression is provided — which must uniquely identify each list item — Svelte will use it to intelligently update the list when data changes by inserting, moving and deleting items, rather than adding or removing items at the end and updating the state in the middle. + +The key can be any object, but strings and numbers are recommended since they allow identity to persist when the objects themselves change. ```svelte {#each items as item (item.id)} From 17a7cf51e44dfb7704e6b2559e14e63f22972cd3 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 15 May 2025 11:19:09 -0400 Subject: [PATCH 0410/1429] docs: clarify ordering of attributes with spread (#15917) --- documentation/docs/03-template-syntax/01-basic-markup.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/documentation/docs/03-template-syntax/01-basic-markup.md b/documentation/docs/03-template-syntax/01-basic-markup.md index fe5f8b02aa..feecfe033e 100644 --- a/documentation/docs/03-template-syntax/01-basic-markup.md +++ b/documentation/docs/03-template-syntax/01-basic-markup.md @@ -82,12 +82,14 @@ As with elements, `name={name}` can be replaced with the `{name}` shorthand. ``` +## Spread attributes + _Spread attributes_ allow many attributes or properties to be passed to an element or component at once. -An element or component can have multiple spread attributes, interspersed with regular ones. +An element or component can have multiple spread attributes, interspersed with regular ones. Order matters — if `things.a` exists it will take precedence over `a="b"`, while `c="d"` would take precedence over `things.c`: ```svelte - + ``` ## Events From a5a0b49003d9cb544ed7919a4320870bda237833 Mon Sep 17 00:00:00 2001 From: Matteo Battista Date: Thu, 15 May 2025 18:14:18 +0200 Subject: [PATCH 0411/1429] fix: update_branch with (anchor).data possible undefined on ios devices (#15851) * fix: update_branch with (anchor).data possible undefined * error sooner * add tests * changeset --------- Co-authored-by: Rich Harris --- .changeset/silly-apples-remain.md | 5 +++++ .../svelte/src/internal/client/dom/blocks/each.js | 3 ++- .../svelte/src/internal/client/dom/blocks/if.js | 4 +++- .../svelte/src/internal/client/dom/hydration.js | 13 +++++++++++++ .../samples/cloudflare-mirage-borking-2/_config.js | 6 ++++++ .../cloudflare-mirage-borking-2/_expected.html | 1 + .../cloudflare-mirage-borking-2/_override.html | 1 + .../samples/cloudflare-mirage-borking-2/main.svelte | 5 +++++ .../samples/cloudflare-mirage-borking/_config.js | 6 ++++++ .../cloudflare-mirage-borking/_expected.html | 1 + .../cloudflare-mirage-borking/_override.html | 1 + .../samples/cloudflare-mirage-borking/main.svelte | 9 +++++++++ 12 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 .changeset/silly-apples-remain.md create mode 100644 packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_config.js create mode 100644 packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_expected.html create mode 100644 packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_override.html create mode 100644 packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/main.svelte create mode 100644 packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_config.js create mode 100644 packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_expected.html create mode 100644 packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_override.html create mode 100644 packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/main.svelte diff --git a/.changeset/silly-apples-remain.md b/.changeset/silly-apples-remain.md new file mode 100644 index 0000000000..10d43db550 --- /dev/null +++ b/.changeset/silly-apples-remain.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: handle more hydration mismatches diff --git a/packages/svelte/src/internal/client/dom/blocks/each.js b/packages/svelte/src/internal/client/dom/blocks/each.js index 92c953b541..2997664fa2 100644 --- a/packages/svelte/src/internal/client/dom/blocks/each.js +++ b/packages/svelte/src/internal/client/dom/blocks/each.js @@ -12,6 +12,7 @@ import { hydrate_next, hydrate_node, hydrating, + read_hydration_instruction, remove_nodes, set_hydrate_node, set_hydrating @@ -160,7 +161,7 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f let mismatch = false; if (hydrating) { - var is_else = /** @type {Comment} */ (anchor).data === HYDRATION_START_ELSE; + var is_else = read_hydration_instruction(anchor) === HYDRATION_START_ELSE; if (is_else !== (length === 0)) { // hydration mismatch — remove the server-rendered DOM and start over diff --git a/packages/svelte/src/internal/client/dom/blocks/if.js b/packages/svelte/src/internal/client/dom/blocks/if.js index 925abb9d9d..bf1098c3f4 100644 --- a/packages/svelte/src/internal/client/dom/blocks/if.js +++ b/packages/svelte/src/internal/client/dom/blocks/if.js @@ -4,6 +4,7 @@ import { hydrate_next, hydrate_node, hydrating, + read_hydration_instruction, remove_nodes, set_hydrate_node, set_hydrating @@ -56,7 +57,8 @@ export function if_block(node, fn, [root_index, hydrate_index] = [0, 0]) { if (hydrating && hydrate_index !== -1) { if (root_index === 0) { - const data = /** @type {Comment} */ (anchor).data; + const data = read_hydration_instruction(anchor); + if (data === HYDRATION_START) { hydrate_index = 0; } else if (data === HYDRATION_START_ELSE) { diff --git a/packages/svelte/src/internal/client/dom/hydration.js b/packages/svelte/src/internal/client/dom/hydration.js index 8523ff97d5..ab3256da82 100644 --- a/packages/svelte/src/internal/client/dom/hydration.js +++ b/packages/svelte/src/internal/client/dom/hydration.js @@ -103,3 +103,16 @@ export function remove_nodes() { node = next; } } + +/** + * + * @param {TemplateNode} node + */ +export function read_hydration_instruction(node) { + if (!node || node.nodeType !== 8) { + w.hydration_mismatch(); + throw HYDRATION_ERROR; + } + + return /** @type {Comment} */ (node).data; +} diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_config.js b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_config.js new file mode 100644 index 0000000000..56ba73b064 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_config.js @@ -0,0 +1,6 @@ +import { test } from '../../test'; + +// https://github.com/sveltejs/svelte/issues/15819 +export default test({ + expect_hydration_error: true +}); diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_expected.html b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_expected.html new file mode 100644 index 0000000000..5179fb04a5 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_expected.html @@ -0,0 +1 @@ +

start

cond

diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_override.html b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_override.html new file mode 100644 index 0000000000..2a1c323288 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/_override.html @@ -0,0 +1 @@ +

start

cond

diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/main.svelte b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/main.svelte new file mode 100644 index 0000000000..bfb4f2cdb8 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking-2/main.svelte @@ -0,0 +1,5 @@ + + +

start

{#if cond}

cond

{/if} diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_config.js b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_config.js new file mode 100644 index 0000000000..56ba73b064 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_config.js @@ -0,0 +1,6 @@ +import { test } from '../../test'; + +// https://github.com/sveltejs/svelte/issues/15819 +export default test({ + expect_hydration_error: true +}); diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_expected.html b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_expected.html new file mode 100644 index 0000000000..f6c03b87c1 --- /dev/null +++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_expected.html @@ -0,0 +1 @@ +

start

pre123 mid diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_override.html b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_override.html new file mode 100644 index 0000000000..c84efbb00b --- /dev/null +++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/_override.html @@ -0,0 +1 @@ +

start

pre
123 mid diff --git a/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/main.svelte b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/main.svelte new file mode 100644 index 0000000000..2c9a94686e --- /dev/null +++ b/packages/svelte/tests/hydration/samples/cloudflare-mirage-borking/main.svelte @@ -0,0 +1,9 @@ + + +

start

+pre123 +{#if cond} +mid +{/if} From 60b22ab933c45ce329f4e7919be0b0b73a65ecbb Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Sat, 17 May 2025 10:07:21 +0200 Subject: [PATCH 0412/1429] fix: falsy attachments types (#15939) --- .changeset/chilled-otters-hear.md | 5 +++++ packages/svelte/elements.d.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/chilled-otters-hear.md diff --git a/.changeset/chilled-otters-hear.md b/.changeset/chilled-otters-hear.md new file mode 100644 index 0000000000..53a9872d96 --- /dev/null +++ b/.changeset/chilled-otters-hear.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: falsy attachments types diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts index c637137365..237e96c699 100644 --- a/packages/svelte/elements.d.ts +++ b/packages/svelte/elements.d.ts @@ -863,8 +863,8 @@ export interface HTMLAttributes extends AriaAttributes, D // allow any data- attribute [key: `data-${string}`]: any; - // allow any attachment - [key: symbol]: Attachment; + // allow any attachment and falsy values (by using false we prevent the usage of booleans values by themselves) + [key: symbol]: Attachment | false | undefined | null; } export type HTMLAttributeAnchorTarget = '_self' | '_blank' | '_parent' | '_top' | (string & {}); From c7e4b8e765ee3e5f184d736ed401e63124065753 Mon Sep 17 00:00:00 2001 From: pengqiseven <134899215+pengqiseven@users.noreply.github.com> Date: Sat, 17 May 2025 16:10:54 +0800 Subject: [PATCH 0413/1429] chore: remove redundant word in comment (#15942) Signed-off-by: pengqiseven <912170095@qq.com> --- .../svelte/src/compiler/phases/2-analyze/visitors/Attribute.js | 2 +- .../phases/3-transform/client/visitors/RegularElement.js | 2 +- packages/svelte/src/internal/client/dom/elements/class.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js index 3ba81767cc..773aa59744 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js +++ b/packages/svelte/src/compiler/phases/2-analyze/visitors/Attribute.js @@ -211,7 +211,7 @@ function get_delegated_event(event_name, handler, context) { if ( binding !== null && - // Bail out if the the binding is a rest param + // Bail out if the binding is a rest param (binding.declaration_kind === 'rest_param' || // Bail out if we reference anything from the EachBlock (for now) that mutates in non-runes mode, (((!context.state.analysis.runes && binding.kind === 'each') || diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js index ab981878ad..1d39289f3d 100644 --- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js +++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/RegularElement.js @@ -564,7 +564,7 @@ export function build_style_directives_object(style_directives, context) { /** * Serializes an assignment to an element property by adding relevant statements to either only - * the init or the the init and update arrays, depending on whether or not the value is dynamic. + * the init or the init and update arrays, depending on whether or not the value is dynamic. * Resulting code for static looks something like this: * ```js * element.property = value; diff --git a/packages/svelte/src/internal/client/dom/elements/class.js b/packages/svelte/src/internal/client/dom/elements/class.js index fc081b8956..038ce33f3e 100644 --- a/packages/svelte/src/internal/client/dom/elements/class.js +++ b/packages/svelte/src/internal/client/dom/elements/class.js @@ -24,7 +24,7 @@ export function set_class(dom, is_html, value, hash, prev_classes, next_classes) if (!hydrating || next_class_name !== dom.getAttribute('class')) { // Removing the attribute when the value is only an empty string causes // performance issues vs simply making the className an empty string. So - // we should only remove the class if the the value is nullish + // we should only remove the class if the value is nullish // and there no hash/directives : if (next_class_name == null) { dom.removeAttribute('class'); From c365634ace068698e29c0331399f3a8c03ad5467 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 17 May 2025 05:04:39 -0400 Subject: [PATCH 0414/1429] Version Packages (#15931) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/chilled-otters-hear.md | 5 ----- .changeset/silly-apples-remain.md | 5 ----- packages/svelte/CHANGELOG.md | 8 ++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 10 insertions(+), 12 deletions(-) delete mode 100644 .changeset/chilled-otters-hear.md delete mode 100644 .changeset/silly-apples-remain.md diff --git a/.changeset/chilled-otters-hear.md b/.changeset/chilled-otters-hear.md deleted file mode 100644 index 53a9872d96..0000000000 --- a/.changeset/chilled-otters-hear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: falsy attachments types diff --git a/.changeset/silly-apples-remain.md b/.changeset/silly-apples-remain.md deleted file mode 100644 index 10d43db550..0000000000 --- a/.changeset/silly-apples-remain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: handle more hydration mismatches diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 2b65a1889f..9d5cfe1334 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,13 @@ # svelte +## 5.30.2 + +### Patch Changes + +- fix: falsy attachments types ([#15939](https://github.com/sveltejs/svelte/pull/15939)) + +- fix: handle more hydration mismatches ([#15851](https://github.com/sveltejs/svelte/pull/15851)) + ## 5.30.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index b6e8d06c63..4ec70a88b5 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.30.1", + "version": "5.30.2", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index b849318036..a5cc8b7191 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.30.1'; +export const VERSION = '5.30.2'; export const PUBLIC_VERSION = '5'; From b2ce3fa85ed77ba173da9ab27d7bc36f17b7e9c7 Mon Sep 17 00:00:00 2001 From: Mateusz Kadlubowski Date: Sun, 18 May 2025 16:25:11 +0800 Subject: [PATCH 0415/1429] (fix/ast types) fix: Add missing `AttachTag` in `Tag` union type inside the `AST` namespace from `"svelte/compiler"` (#15946) --- .changeset/funny-carrots-teach.md | 5 +++++ packages/svelte/src/compiler/types/template.d.ts | 8 +++++++- packages/svelte/types/index.d.ts | 8 +++++++- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 .changeset/funny-carrots-teach.md diff --git a/.changeset/funny-carrots-teach.md b/.changeset/funny-carrots-teach.md new file mode 100644 index 0000000000..53ff135e84 --- /dev/null +++ b/.changeset/funny-carrots-teach.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: Add missing `AttachTag` in `Tag` union type inside the `AST` namespace from `"svelte/compiler"` diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index 6dec1f2dbe..b51c9e9a8d 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -547,7 +547,13 @@ export namespace AST { | AST.SvelteWindow | AST.SvelteBoundary; - export type Tag = AST.ExpressionTag | AST.HtmlTag | AST.ConstTag | AST.DebugTag | AST.RenderTag; + export type Tag = + | AST.AttachTag + | AST.ConstTag + | AST.DebugTag + | AST.ExpressionTag + | AST.HtmlTag + | AST.RenderTag; export type TemplateNode = | AST.Root diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index bb958c5108..1fda9a36b8 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -1362,7 +1362,13 @@ declare module 'svelte/compiler' { | AST.SvelteWindow | AST.SvelteBoundary; - export type Tag = AST.ExpressionTag | AST.HtmlTag | AST.ConstTag | AST.DebugTag | AST.RenderTag; + export type Tag = + | AST.AttachTag + | AST.ConstTag + | AST.DebugTag + | AST.ExpressionTag + | AST.HtmlTag + | AST.RenderTag; export type TemplateNode = | AST.Root From 42e7e8168d01f2510c73bf23bec1e0a6d9e44a66 Mon Sep 17 00:00:00 2001 From: Christian Decker <50999401+cmdecker95@users.noreply.github.com> Date: Sun, 18 May 2025 04:50:12 -0400 Subject: [PATCH 0416/1429] chore: fix docs typo in 02-context.md (#15944) --- documentation/docs/06-runtime/02-context.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/docs/06-runtime/02-context.md b/documentation/docs/06-runtime/02-context.md index 4204bcfe6d..f395de421c 100644 --- a/documentation/docs/06-runtime/02-context.md +++ b/documentation/docs/06-runtime/02-context.md @@ -125,7 +125,7 @@ In many cases this is perfectly fine, but there is a risk: if you mutate the sta ```svelte + + diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure-private-3/_config.js b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure-private-3/_config.js new file mode 100644 index 0000000000..dd847ce2f2 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure-private-3/_config.js @@ -0,0 +1,13 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: ``, + ssrHtml: ``, + + async test({ assert, target }) { + flushSync(); + + assert.htmlEqual(target.innerHTML, ``); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure-private-3/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure-private-3/main.svelte new file mode 100644 index 0000000000..47b8c901eb --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-closure-private-3/main.svelte @@ -0,0 +1,12 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor-conflicting-get-name/_config.js b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-conflicting-get-name/_config.js new file mode 100644 index 0000000000..f47bee71df --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-conflicting-get-name/_config.js @@ -0,0 +1,3 @@ +import { test } from '../../test'; + +export default test({}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor-conflicting-get-name/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-conflicting-get-name/main.svelte new file mode 100644 index 0000000000..e2c4f302b3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-conflicting-get-name/main.svelte @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor-derived-unowned/_config.js b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-derived-unowned/_config.js new file mode 100644 index 0000000000..4cf1aea213 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-derived-unowned/_config.js @@ -0,0 +1,45 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + // The component context class instance gets shared between tests, strangely, causing hydration to fail? + mode: ['client', 'server'], + + async test({ assert, target, logs }) { + const btn = target.querySelector('button'); + + flushSync(() => { + btn?.click(); + }); + + assert.deepEqual(logs, [0, 'class trigger false', 'local trigger false', 1]); + + flushSync(() => { + btn?.click(); + }); + + assert.deepEqual(logs, [0, 'class trigger false', 'local trigger false', 1, 2]); + + flushSync(() => { + btn?.click(); + }); + + assert.deepEqual(logs, [0, 'class trigger false', 'local trigger false', 1, 2, 3]); + + flushSync(() => { + btn?.click(); + }); + + assert.deepEqual(logs, [ + 0, + 'class trigger false', + 'local trigger false', + 1, + 2, + 3, + 4, + 'class trigger true', + 'local trigger true' + ]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor-derived-unowned/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-derived-unowned/main.svelte new file mode 100644 index 0000000000..03687d01bb --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-derived-unowned/main.svelte @@ -0,0 +1,37 @@ + + + + + diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor-predeclared-field/_config.js b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-predeclared-field/_config.js new file mode 100644 index 0000000000..02cf36d900 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-predeclared-field/_config.js @@ -0,0 +1,20 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: ``, + + test({ assert, target }) { + const btn = target.querySelector('button'); + + flushSync(() => { + btn?.click(); + }); + assert.htmlEqual(target.innerHTML, ``); + + flushSync(() => { + btn?.click(); + }); + assert.htmlEqual(target.innerHTML, ``); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor-predeclared-field/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-predeclared-field/main.svelte new file mode 100644 index 0000000000..5dbbb10afd --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-predeclared-field/main.svelte @@ -0,0 +1,12 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor-subclass/_config.js b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-subclass/_config.js new file mode 100644 index 0000000000..32cca6c693 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-subclass/_config.js @@ -0,0 +1,20 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: ``, + + test({ assert, target }) { + const btn = target.querySelector('button'); + + flushSync(() => { + btn?.click(); + }); + assert.htmlEqual(target.innerHTML, ``); + + flushSync(() => { + btn?.click(); + }); + assert.htmlEqual(target.innerHTML, ``); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor-subclass/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-subclass/main.svelte new file mode 100644 index 0000000000..d8feb554cd --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor-subclass/main.svelte @@ -0,0 +1,22 @@ + + + diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor/_config.js b/packages/svelte/tests/runtime-runes/samples/class-state-constructor/_config.js new file mode 100644 index 0000000000..f35dc57228 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor/_config.js @@ -0,0 +1,20 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + html: ``, + + test({ assert, target }) { + const btn = target.querySelector('button'); + + flushSync(() => { + btn?.click(); + }); + assert.htmlEqual(target.innerHTML, ``); + + flushSync(() => { + btn?.click(); + }); + assert.htmlEqual(target.innerHTML, ``); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/class-state-constructor/main.svelte b/packages/svelte/tests/runtime-runes/samples/class-state-constructor/main.svelte new file mode 100644 index 0000000000..aa8ba1658b --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/class-state-constructor/main.svelte @@ -0,0 +1,18 @@ + + + diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-1/errors.json b/packages/svelte/tests/validator/samples/class-state-constructor-1/errors.json new file mode 100644 index 0000000000..82765c51c1 --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-1/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "state_field_duplicate", + "message": "`count` has already been declared on this class", + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 24 + } + } +] diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-1/input.svelte.js b/packages/svelte/tests/validator/samples/class-state-constructor-1/input.svelte.js new file mode 100644 index 0000000000..05cd4d9d9d --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-1/input.svelte.js @@ -0,0 +1,7 @@ +export class Counter { + count = $state(0); + + constructor() { + this.count = $state(0); + } +} diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-10/errors.json b/packages/svelte/tests/validator/samples/class-state-constructor-10/errors.json new file mode 100644 index 0000000000..c4cb0991d0 --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-10/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "state_field_invalid_assignment", + "message": "Cannot assign to a state field before its declaration", + "start": { + "line": 4, + "column": 3 + }, + "end": { + "line": 4, + "column": 18 + } + } +] diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-10/input.svelte.js b/packages/svelte/tests/validator/samples/class-state-constructor-10/input.svelte.js new file mode 100644 index 0000000000..e5ad562727 --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-10/input.svelte.js @@ -0,0 +1,9 @@ +export class Counter { + constructor() { + if (true) { + this.count = -1; + } + + this.count = $state(0); + } +} diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-2/errors.json b/packages/svelte/tests/validator/samples/class-state-constructor-2/errors.json new file mode 100644 index 0000000000..82765c51c1 --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-2/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "state_field_duplicate", + "message": "`count` has already been declared on this class", + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 24 + } + } +] diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-2/input.svelte.js b/packages/svelte/tests/validator/samples/class-state-constructor-2/input.svelte.js new file mode 100644 index 0000000000..e37be4b3e6 --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-2/input.svelte.js @@ -0,0 +1,7 @@ +export class Counter { + constructor() { + this.count = $state(0); + this.count = 1; + this.count = $state(0); + } +} diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-3/errors.json b/packages/svelte/tests/validator/samples/class-state-constructor-3/errors.json new file mode 100644 index 0000000000..175c41f98c --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-3/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "state_field_duplicate", + "message": "`count` has already been declared on this class", + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 28 + } + } +] diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-3/input.svelte.js b/packages/svelte/tests/validator/samples/class-state-constructor-3/input.svelte.js new file mode 100644 index 0000000000..f9196ff3cd --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-3/input.svelte.js @@ -0,0 +1,7 @@ +export class Counter { + constructor() { + this.count = $state(0); + this.count = 1; + this.count = $state.raw(0); + } +} diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-4/errors.json b/packages/svelte/tests/validator/samples/class-state-constructor-4/errors.json new file mode 100644 index 0000000000..9f959874c8 --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-4/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "state_invalid_placement", + "message": "`$state(...)` can only be used as a variable declaration initializer, a class field declaration, or the first assignment to a class field at the top level of the constructor.", + "start": { + "line": 4, + "column": 16 + }, + "end": { + "line": 4, + "column": 25 + } + } +] diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-4/input.svelte.js b/packages/svelte/tests/validator/samples/class-state-constructor-4/input.svelte.js new file mode 100644 index 0000000000..bf1aada1b5 --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-4/input.svelte.js @@ -0,0 +1,7 @@ +export class Counter { + constructor() { + if (true) { + this.count = $state(0); + } + } +} diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-5/errors.json b/packages/svelte/tests/validator/samples/class-state-constructor-5/errors.json new file mode 100644 index 0000000000..af2f30dade --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-5/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "state_field_duplicate", + "message": "`count` has already been declared on this class", + "start": { + "line": 5, + "column": 2 + }, + "end": { + "line": 5, + "column": 27 + } + } +] diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-5/input.svelte.js b/packages/svelte/tests/validator/samples/class-state-constructor-5/input.svelte.js new file mode 100644 index 0000000000..bc3d19a14f --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-5/input.svelte.js @@ -0,0 +1,7 @@ +export class Counter { + // prettier-ignore + 'count' = $state(0); + constructor() { + this['count'] = $state(0); + } +} diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-6/errors.json b/packages/svelte/tests/validator/samples/class-state-constructor-6/errors.json new file mode 100644 index 0000000000..ae7a47f31b --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-6/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "state_field_duplicate", + "message": "`count` has already been declared on this class", + "start": { + "line": 4, + "column": 2 + }, + "end": { + "line": 4, + "column": 27 + } + } +] diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-6/input.svelte.js b/packages/svelte/tests/validator/samples/class-state-constructor-6/input.svelte.js new file mode 100644 index 0000000000..2ebe52e685 --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-6/input.svelte.js @@ -0,0 +1,6 @@ +export class Counter { + count = $state(0); + constructor() { + this['count'] = $state(0); + } +} diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-7/errors.json b/packages/svelte/tests/validator/samples/class-state-constructor-7/errors.json new file mode 100644 index 0000000000..64e56f8d5c --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-7/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "state_invalid_placement", + "message": "`$state(...)` can only be used as a variable declaration initializer, a class field declaration, or the first assignment to a class field at the top level of the constructor.", + "start": { + "line": 5, + "column": 16 + }, + "end": { + "line": 5, + "column": 25 + } + } +] diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-7/input.svelte.js b/packages/svelte/tests/validator/samples/class-state-constructor-7/input.svelte.js new file mode 100644 index 0000000000..50c8559837 --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-7/input.svelte.js @@ -0,0 +1,7 @@ +const count = 'count'; + +export class Counter { + constructor() { + this[count] = $state(0); + } +} diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-8/errors.json b/packages/svelte/tests/validator/samples/class-state-constructor-8/errors.json new file mode 100644 index 0000000000..2e0bd10ff8 --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-8/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "state_field_invalid_assignment", + "message": "Cannot assign to a state field before its declaration", + "start": { + "line": 3, + "column": 2 + }, + "end": { + "line": 3, + "column": 17 + } + } +] diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-8/input.svelte.js b/packages/svelte/tests/validator/samples/class-state-constructor-8/input.svelte.js new file mode 100644 index 0000000000..0a76c6fec9 --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-8/input.svelte.js @@ -0,0 +1,6 @@ +export class Counter { + constructor() { + this.count = -1; + this.count = $state(0); + } +} diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-9/errors.json b/packages/svelte/tests/validator/samples/class-state-constructor-9/errors.json new file mode 100644 index 0000000000..b7dd4c8ed4 --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-9/errors.json @@ -0,0 +1,14 @@ +[ + { + "code": "state_field_invalid_assignment", + "message": "Cannot assign to a state field before its declaration", + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 12 + } + } +] diff --git a/packages/svelte/tests/validator/samples/class-state-constructor-9/input.svelte.js b/packages/svelte/tests/validator/samples/class-state-constructor-9/input.svelte.js new file mode 100644 index 0000000000..a8469e13af --- /dev/null +++ b/packages/svelte/tests/validator/samples/class-state-constructor-9/input.svelte.js @@ -0,0 +1,7 @@ +export class Counter { + count = -1; + + constructor() { + this.count = $state(0); + } +} diff --git a/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/errors.json b/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/errors.json index 32594e4268..e1906b181a 100644 --- a/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/errors.json +++ b/packages/svelte/tests/validator/samples/const-tag-invalid-rune-usage/errors.json @@ -1,7 +1,7 @@ [ { "code": "state_invalid_placement", - "message": "`$derived(...)` can only be used as a variable declaration initializer or a class field", + "message": "`$derived(...)` can only be used as a variable declaration initializer, a class field declaration, or the first assignment to a class field at the top level of the constructor.", "start": { "line": 2, "column": 15 From 21bf947ca8269ada01a2ec7962aeedc841026ac5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 11:56:41 -0400 Subject: [PATCH 0418/1429] Version Packages (#15947) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/funny-carrots-teach.md | 5 ----- .changeset/mean-squids-scream.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 .changeset/funny-carrots-teach.md delete mode 100644 .changeset/mean-squids-scream.md diff --git a/.changeset/funny-carrots-teach.md b/.changeset/funny-carrots-teach.md deleted file mode 100644 index 53ff135e84..0000000000 --- a/.changeset/funny-carrots-teach.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: Add missing `AttachTag` in `Tag` union type inside the `AST` namespace from `"svelte/compiler"` diff --git a/.changeset/mean-squids-scream.md b/.changeset/mean-squids-scream.md deleted file mode 100644 index 2157ea85a6..0000000000 --- a/.changeset/mean-squids-scream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: allow state fields to be declared inside class constructors diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 9d5cfe1334..d40ff09317 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,15 @@ # svelte +## 5.31.0 + +### Minor Changes + +- feat: allow state fields to be declared inside class constructors ([#15820](https://github.com/sveltejs/svelte/pull/15820)) + +### Patch Changes + +- fix: Add missing `AttachTag` in `Tag` union type inside the `AST` namespace from `"svelte/compiler"` ([#15946](https://github.com/sveltejs/svelte/pull/15946)) + ## 5.30.2 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 4ec70a88b5..ee35cda2bd 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.30.2", + "version": "5.31.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index a5cc8b7191..ed99ef6795 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.30.2'; +export const VERSION = '5.31.0'; export const PUBLIC_VERSION = '5'; From b7b393d50fab48d4f38d0f72c5c177de5c236024 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Mon, 19 May 2025 18:26:10 +0200 Subject: [PATCH 0419/1429] chore: watch for messages changes in dev generate script (#15950) * chore: watch for messages changes in dev generate script * no need for this to be async * tweak * guard * only create one timeout --------- Co-authored-by: Rich Harris --- packages/svelte/package.json | 2 +- .../svelte/scripts/process-messages/index.js | 680 +++++++++--------- 2 files changed, 357 insertions(+), 325 deletions(-) diff --git a/packages/svelte/package.json b/packages/svelte/package.json index ee35cda2bd..e0f6afe6fc 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -136,7 +136,7 @@ ], "scripts": { "build": "node scripts/process-messages && rollup -c && pnpm generate:types && node scripts/check-treeshakeability.js", - "dev": "node scripts/process-messages && rollup -cw", + "dev": "node scripts/process-messages -w & rollup -cw", "check": "tsc --project tsconfig.runtime.json && tsc && cd ./tests/types && tsc", "check:watch": "tsc --watch", "generate:version": "node ./scripts/generate-version.js", diff --git a/packages/svelte/scripts/process-messages/index.js b/packages/svelte/scripts/process-messages/index.js index 80619acfa7..81c59271de 100644 --- a/packages/svelte/scripts/process-messages/index.js +++ b/packages/svelte/scripts/process-messages/index.js @@ -1,409 +1,441 @@ // @ts-check +import process from 'node:process'; import fs from 'node:fs'; import * as acorn from 'acorn'; import { walk } from 'zimmerframe'; import * as esrap from 'esrap'; -/** @type {Record>} */ -const messages = {}; -const seen = new Set(); - const DIR = '../../documentation/docs/98-reference/.generated'; -fs.rmSync(DIR, { force: true, recursive: true }); -fs.mkdirSync(DIR); -for (const category of fs.readdirSync('messages')) { - if (category.startsWith('.')) continue; +const watch = process.argv.includes('-w'); - messages[category] = {}; +function run() { + /** @type {Record>} */ + const messages = {}; + const seen = new Set(); - for (const file of fs.readdirSync(`messages/${category}`)) { - if (!file.endsWith('.md')) continue; + fs.rmSync(DIR, { force: true, recursive: true }); + fs.mkdirSync(DIR); - const markdown = fs - .readFileSync(`messages/${category}/${file}`, 'utf-8') - .replace(/\r\n/g, '\n'); + for (const category of fs.readdirSync('messages')) { + if (category.startsWith('.')) continue; - const sorted = []; + messages[category] = {}; - for (const match of markdown.matchAll(/## ([\w]+)\n\n([^]+?)(?=$|\n\n## )/g)) { - const [_, code, text] = match; + for (const file of fs.readdirSync(`messages/${category}`)) { + if (!file.endsWith('.md')) continue; - if (seen.has(code)) { - throw new Error(`Duplicate message code ${category}/${code}`); - } + const markdown = fs + .readFileSync(`messages/${category}/${file}`, 'utf-8') + .replace(/\r\n/g, '\n'); - sorted.push({ code, _ }); + const sorted = []; - const sections = text.trim().split('\n\n'); - const details = []; + for (const match of markdown.matchAll(/## ([\w]+)\n\n([^]+?)(?=$|\n\n## )/g)) { + const [_, code, text] = match; - while (!sections[sections.length - 1].startsWith('> ')) { - details.unshift(/** @type {string} */ (sections.pop())); - } + if (seen.has(code)) { + throw new Error(`Duplicate message code ${category}/${code}`); + } + + sorted.push({ code, _ }); - if (sections.length === 0) { - throw new Error('No message text'); + const sections = text.trim().split('\n\n'); + const details = []; + + while (!sections[sections.length - 1].startsWith('> ')) { + details.unshift(/** @type {string} */ (sections.pop())); + } + + if (sections.length === 0) { + throw new Error('No message text'); + } + + seen.add(code); + messages[category][code] = { + messages: sections.map((section) => section.replace(/^> /gm, '').replace(/^>\n/gm, '\n')), + details: details.join('\n\n') + }; } - seen.add(code); - messages[category][code] = { - messages: sections.map((section) => section.replace(/^> /gm, '').replace(/^>\n/gm, '\n')), - details: details.join('\n\n') - }; + sorted.sort((a, b) => (a.code < b.code ? -1 : 1)); + + fs.writeFileSync( + `messages/${category}/${file}`, + sorted.map((x) => x._.trim()).join('\n\n') + '\n' + ); } - sorted.sort((a, b) => (a.code < b.code ? -1 : 1)); fs.writeFileSync( - `messages/${category}/${file}`, - sorted.map((x) => x._.trim()).join('\n\n') + '\n' + `${DIR}/${category}.md`, + '\n\n' + + Object.entries(messages[category]) + .map(([code, { messages, details }]) => { + const chunks = [ + `### ${code}`, + ...messages.map((message) => '```\n' + message + '\n```') + ]; + + if (details) { + chunks.push(details); + } + + return chunks.join('\n\n'); + }) + .sort() + .join('\n\n') + + '\n' ); } - fs.writeFileSync( - `${DIR}/${category}.md`, - '\n\n' + - Object.entries(messages[category]) - .map(([code, { messages, details }]) => { - const chunks = [`### ${code}`, ...messages.map((message) => '```\n' + message + '\n```')]; - - if (details) { - chunks.push(details); - } - - return chunks.join('\n\n'); - }) - .sort() - .join('\n\n') + - '\n' - ); -} - -/** - * @param {string} name - * @param {string} dest - */ -function transform(name, dest) { - const source = fs - .readFileSync(new URL(`./templates/${name}.js`, import.meta.url), 'utf-8') - .replace(/\r\n/g, '\n'); - /** - * @type {Array<{ - * type: string; - * value: string; - * start: number; - * end: number - * }>} + * @param {string} name + * @param {string} dest */ - const comments = []; - - let ast = acorn.parse(source, { - ecmaVersion: 'latest', - sourceType: 'module', - onComment: (block, value, start, end) => { - if (block && /\n/.test(value)) { - let a = start; - while (a > 0 && source[a - 1] !== '\n') a -= 1; - - let b = a; - while (/[ \t]/.test(source[b])) b += 1; - - const indentation = source.slice(a, b); - value = value.replace(new RegExp(`^${indentation}`, 'gm'), ''); - } - - comments.push({ type: block ? 'Block' : 'Line', value, start, end }); - } - }); + function transform(name, dest) { + const source = fs + .readFileSync(new URL(`./templates/${name}.js`, import.meta.url), 'utf-8') + .replace(/\r\n/g, '\n'); - ast = walk(ast, null, { - _(node, { next }) { - let comment; + /** + * @type {Array<{ + * type: string; + * value: string; + * start: number; + * end: number + * }>} + */ + const comments = []; + + let ast = acorn.parse(source, { + ecmaVersion: 'latest', + sourceType: 'module', + onComment: (block, value, start, end) => { + if (block && /\n/.test(value)) { + let a = start; + while (a > 0 && source[a - 1] !== '\n') a -= 1; + + let b = a; + while (/[ \t]/.test(source[b])) b += 1; + + const indentation = source.slice(a, b); + value = value.replace(new RegExp(`^${indentation}`, 'gm'), ''); + } - while (comments[0] && comments[0].start < node.start) { - comment = comments.shift(); - // @ts-expect-error - (node.leadingComments ||= []).push(comment); + comments.push({ type: block ? 'Block' : 'Line', value, start, end }); } + }); - next(); - - if (comments[0]) { - const slice = source.slice(node.end, comments[0].start); + ast = walk(ast, null, { + _(node, { next }) { + let comment; - if (/^[,) \t]*$/.test(slice)) { + while (comments[0] && comments[0].start < node.start) { + comment = comments.shift(); // @ts-expect-error - node.trailingComments = [comments.shift()]; + (node.leadingComments ||= []).push(comment); } - } - }, - // @ts-expect-error - Identifier(node, context) { - if (node.name === 'CODES') { - return { - type: 'ArrayExpression', - elements: Object.keys(messages[name]).map((code) => ({ - type: 'Literal', - value: code - })) - }; - } - } - }); - - if (comments.length > 0) { - // @ts-expect-error - (ast.trailingComments ||= []).push(...comments); - } - - const category = messages[name]; - - // find the `export function CODE` node - const index = ast.body.findIndex((node) => { - if ( - node.type === 'ExportNamedDeclaration' && - node.declaration && - node.declaration.type === 'FunctionDeclaration' - ) { - return node.declaration.id.name === 'CODE'; - } - }); - - if (index === -1) throw new Error(`missing export function CODE in ${name}.js`); - const template_node = ast.body[index]; - ast.body.splice(index, 1); + next(); - for (const code in category) { - const { messages } = category[code]; - /** @type {string[]} */ - const vars = []; + if (comments[0]) { + const slice = source.slice(node.end, comments[0].start); - const group = messages.map((text, i) => { - for (const match of text.matchAll(/%(\w+)%/g)) { - const name = match[1]; - if (!vars.includes(name)) { - vars.push(match[1]); + if (/^[,) \t]*$/.test(slice)) { + // @ts-expect-error + node.trailingComments = [comments.shift()]; + } + } + }, + // @ts-expect-error + Identifier(node, context) { + if (node.name === 'CODES') { + return { + type: 'ArrayExpression', + elements: Object.keys(messages[name]).map((code) => ({ + type: 'Literal', + value: code + })) + }; } } - - return { - text, - vars: vars.slice() - }; }); - /** @type {import('estree').Expression} */ - let message = { type: 'Literal', value: '' }; - let prev_vars; + if (comments.length > 0) { + // @ts-expect-error + (ast.trailingComments ||= []).push(...comments); + } - for (let i = 0; i < group.length; i += 1) { - const { text, vars } = group[i]; + const category = messages[name]; - if (vars.length === 0) { - message = { - type: 'Literal', - value: text - }; - prev_vars = vars; - continue; + // find the `export function CODE` node + const index = ast.body.findIndex((node) => { + if ( + node.type === 'ExportNamedDeclaration' && + node.declaration && + node.declaration.type === 'FunctionDeclaration' + ) { + return node.declaration.id.name === 'CODE'; } + }); - const parts = text.split(/(%\w+%)/); + if (index === -1) throw new Error(`missing export function CODE in ${name}.js`); - /** @type {import('estree').Expression[]} */ - const expressions = []; + const template_node = ast.body[index]; + ast.body.splice(index, 1); - /** @type {import('estree').TemplateElement[]} */ - const quasis = []; + for (const code in category) { + const { messages } = category[code]; + /** @type {string[]} */ + const vars = []; - for (let i = 0; i < parts.length; i += 1) { - const part = parts[i]; - if (i % 2 === 0) { - const str = part.replace(/(`|\${)/g, '\\$1'); - quasis.push({ - type: 'TemplateElement', - value: { raw: str, cooked: str }, - tail: i === parts.length - 1 - }); - } else { - expressions.push({ - type: 'Identifier', - name: part.slice(1, -1) - }); + const group = messages.map((text, i) => { + for (const match of text.matchAll(/%(\w+)%/g)) { + const name = match[1]; + if (!vars.includes(name)) { + vars.push(match[1]); + } } - } + + return { + text, + vars: vars.slice() + }; + }); /** @type {import('estree').Expression} */ - const expression = { - type: 'TemplateLiteral', - expressions, - quasis - }; - - if (prev_vars) { - if (vars.length === prev_vars.length) { - throw new Error('Message overloads must have new parameters'); + let message = { type: 'Literal', value: '' }; + let prev_vars; + + for (let i = 0; i < group.length; i += 1) { + const { text, vars } = group[i]; + + if (vars.length === 0) { + message = { + type: 'Literal', + value: text + }; + prev_vars = vars; + continue; } - message = { - type: 'ConditionalExpression', - test: { - type: 'Identifier', - name: vars[prev_vars.length] - }, - consequent: expression, - alternate: message - }; - } else { - message = expression; - } + const parts = text.split(/(%\w+%)/); - prev_vars = vars; - } + /** @type {import('estree').Expression[]} */ + const expressions = []; - const clone = walk(/** @type {import('estree').Node} */ (template_node), null, { - // @ts-expect-error Block is a block comment, which is not recognised - Block(node, context) { - if (!node.value.includes('PARAMETER')) return; - - const value = /** @type {string} */ (node.value) - .split('\n') - .map((line) => { - if (line === ' * MESSAGE') { - return messages[messages.length - 1] - .split('\n') - .map((line) => ` * ${line}`) - .join('\n'); - } + /** @type {import('estree').TemplateElement[]} */ + const quasis = []; - if (line.includes('PARAMETER')) { - return vars - .map((name, i) => { - const optional = i >= group[0].vars.length; + for (let i = 0; i < parts.length; i += 1) { + const part = parts[i]; + if (i % 2 === 0) { + const str = part.replace(/(`|\${)/g, '\\$1'); + quasis.push({ + type: 'TemplateElement', + value: { raw: str, cooked: str }, + tail: i === parts.length - 1 + }); + } else { + expressions.push({ + type: 'Identifier', + name: part.slice(1, -1) + }); + } + } - return optional - ? ` * @param {string | undefined | null} [${name}]` - : ` * @param {string} ${name}`; - }) - .join('\n'); - } + /** @type {import('estree').Expression} */ + const expression = { + type: 'TemplateLiteral', + expressions, + quasis + }; - return line; - }) - .filter((x) => x !== '') - .join('\n'); + if (prev_vars) { + if (vars.length === prev_vars.length) { + throw new Error('Message overloads must have new parameters'); + } - if (value !== node.value) { - return { ...node, value }; + message = { + type: 'ConditionalExpression', + test: { + type: 'Identifier', + name: vars[prev_vars.length] + }, + consequent: expression, + alternate: message + }; + } else { + message = expression; } - }, - FunctionDeclaration(node, context) { - if (node.id.name !== 'CODE') return; - const params = []; + prev_vars = vars; + } - for (const param of node.params) { - if (param.type === 'Identifier' && param.name === 'PARAMETER') { - params.push(...vars.map((name) => ({ type: 'Identifier', name }))); - } else { - params.push(param); + const clone = walk(/** @type {import('estree').Node} */ (template_node), null, { + // @ts-expect-error Block is a block comment, which is not recognised + Block(node, context) { + if (!node.value.includes('PARAMETER')) return; + + const value = /** @type {string} */ (node.value) + .split('\n') + .map((line) => { + if (line === ' * MESSAGE') { + return messages[messages.length - 1] + .split('\n') + .map((line) => ` * ${line}`) + .join('\n'); + } + + if (line.includes('PARAMETER')) { + return vars + .map((name, i) => { + const optional = i >= group[0].vars.length; + + return optional + ? ` * @param {string | undefined | null} [${name}]` + : ` * @param {string} ${name}`; + }) + .join('\n'); + } + + return line; + }) + .filter((x) => x !== '') + .join('\n'); + + if (value !== node.value) { + return { ...node, value }; } - } + }, + FunctionDeclaration(node, context) { + if (node.id.name !== 'CODE') return; + + const params = []; - return /** @type {import('estree').FunctionDeclaration} */ ({ - .../** @type {import('estree').FunctionDeclaration} */ (context.next()), - params, - id: { - ...node.id, - name: code + for (const param of node.params) { + if (param.type === 'Identifier' && param.name === 'PARAMETER') { + params.push(...vars.map((name) => ({ type: 'Identifier', name }))); + } else { + params.push(param); + } } - }); - }, - TemplateLiteral(node, context) { - /** @type {import('estree').TemplateElement} */ - let quasi = { - type: 'TemplateElement', - value: { - ...node.quasis[0].value - }, - tail: node.quasis[0].tail - }; - /** @type {import('estree').TemplateLiteral} */ - let out = { - type: 'TemplateLiteral', - quasis: [quasi], - expressions: [] - }; + return /** @type {import('estree').FunctionDeclaration} */ ({ + .../** @type {import('estree').FunctionDeclaration} */ (context.next()), + params, + id: { + ...node.id, + name: code + } + }); + }, + TemplateLiteral(node, context) { + /** @type {import('estree').TemplateElement} */ + let quasi = { + type: 'TemplateElement', + value: { + ...node.quasis[0].value + }, + tail: node.quasis[0].tail + }; - for (let i = 0; i < node.expressions.length; i += 1) { - const q = structuredClone(node.quasis[i + 1]); - const e = node.expressions[i]; + /** @type {import('estree').TemplateLiteral} */ + let out = { + type: 'TemplateLiteral', + quasis: [quasi], + expressions: [] + }; - if (e.type === 'Literal' && e.value === 'CODE') { - quasi.value.raw += code + q.value.raw; - continue; - } + for (let i = 0; i < node.expressions.length; i += 1) { + const q = structuredClone(node.quasis[i + 1]); + const e = node.expressions[i]; - if (e.type === 'Identifier' && e.name === 'MESSAGE') { - if (message.type === 'Literal') { - const str = /** @type {string} */ (message.value).replace(/(`|\${)/g, '\\$1'); - quasi.value.raw += str + q.value.raw; + if (e.type === 'Literal' && e.value === 'CODE') { + quasi.value.raw += code + q.value.raw; continue; } - if (message.type === 'TemplateLiteral') { - const m = structuredClone(message); - quasi.value.raw += m.quasis[0].value.raw; - out.quasis.push(...m.quasis.slice(1)); - out.expressions.push(...m.expressions); - quasi = m.quasis[m.quasis.length - 1]; - quasi.value.raw += q.value.raw; - continue; + if (e.type === 'Identifier' && e.name === 'MESSAGE') { + if (message.type === 'Literal') { + const str = /** @type {string} */ (message.value).replace(/(`|\${)/g, '\\$1'); + quasi.value.raw += str + q.value.raw; + continue; + } + + if (message.type === 'TemplateLiteral') { + const m = structuredClone(message); + quasi.value.raw += m.quasis[0].value.raw; + out.quasis.push(...m.quasis.slice(1)); + out.expressions.push(...m.expressions); + quasi = m.quasis[m.quasis.length - 1]; + quasi.value.raw += q.value.raw; + continue; + } } + + out.quasis.push((quasi = q)); + out.expressions.push(/** @type {import('estree').Expression} */ (context.visit(e))); } - out.quasis.push((quasi = q)); - out.expressions.push(/** @type {import('estree').Expression} */ (context.visit(e))); + return out; + }, + Literal(node) { + if (node.value === 'CODE') { + return { + type: 'Literal', + value: code + }; + } + }, + Identifier(node) { + if (node.name !== 'MESSAGE') return; + return message; } + }); - return out; - }, - Literal(node) { - if (node.value === 'CODE') { - return { - type: 'Literal', - value: code - }; - } - }, - Identifier(node) { - if (node.name !== 'MESSAGE') return; - return message; - } - }); + // @ts-expect-error + ast.body.push(clone); + } + + const module = esrap.print(ast); - // @ts-expect-error - ast.body.push(clone); + fs.writeFileSync( + dest, + `/* This file is generated by scripts/process-messages/index.js. Do not edit! */\n\n` + + module.code, + 'utf-8' + ); } - const module = esrap.print(ast); + transform('compile-errors', 'src/compiler/errors.js'); + transform('compile-warnings', 'src/compiler/warnings.js'); - fs.writeFileSync( - dest, - `/* This file is generated by scripts/process-messages/index.js. Do not edit! */\n\n` + - module.code, - 'utf-8' - ); + transform('client-warnings', 'src/internal/client/warnings.js'); + transform('client-errors', 'src/internal/client/errors.js'); + transform('server-errors', 'src/internal/server/errors.js'); + transform('shared-errors', 'src/internal/shared/errors.js'); + transform('shared-warnings', 'src/internal/shared/warnings.js'); } -transform('compile-errors', 'src/compiler/errors.js'); -transform('compile-warnings', 'src/compiler/warnings.js'); +if (watch) { + let running = false; + let timeout; + + fs.watch('messages', { recursive: true }, (type, file) => { + if (running) { + timeout ??= setTimeout(() => { + running = false; + timeout = null; + }); + } else { + running = true; + + // eslint-disable-next-line no-console + console.log('Regenerating messages...'); + run(); + } + }); +} -transform('client-warnings', 'src/internal/client/warnings.js'); -transform('client-errors', 'src/internal/client/errors.js'); -transform('server-errors', 'src/internal/server/errors.js'); -transform('shared-errors', 'src/internal/shared/errors.js'); -transform('shared-warnings', 'src/internal/shared/warnings.js'); +run(); From a2ddca1aa10362c7e6962e04ec60e55ec758218d Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Mon, 19 May 2025 20:25:07 +0200 Subject: [PATCH 0420/1429] fix: avoid auto-parenthesis for special-keywords-only `MediaQuery` (#15937) * fix: avoid auto-parenthesis for special keywords only `MediaQuery` * chore: update changeset * chore: i has english knowledge * fix: fix media queries with commas --- .changeset/sweet-islands-sell.md | 5 +++++ packages/svelte/src/reactivity/media-query.js | 20 ++++++++++++++++++- .../samples/media-query/_config.js | 5 +++++ .../samples/media-query/main.svelte | 5 +++++ 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 .changeset/sweet-islands-sell.md diff --git a/.changeset/sweet-islands-sell.md b/.changeset/sweet-islands-sell.md new file mode 100644 index 0000000000..b4fbbf182c --- /dev/null +++ b/.changeset/sweet-islands-sell.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: avoid auto-parenthesis for special-keywords-only `MediaQuery` diff --git a/packages/svelte/src/reactivity/media-query.js b/packages/svelte/src/reactivity/media-query.js index 22310df18d..d286709719 100644 --- a/packages/svelte/src/reactivity/media-query.js +++ b/packages/svelte/src/reactivity/media-query.js @@ -3,6 +3,19 @@ import { ReactiveValue } from './reactive-value.js'; const parenthesis_regex = /\(.+\)/; +// these keywords are valid media queries but they need to be without parenthesis +// +// eg: new MediaQuery('screen') +// +// however because of the auto-parenthesis logic in the constructor since there's no parentehesis +// in the media query they'll be surrounded by parenthesis +// +// however we can check if the media query is only composed of these keywords +// and skip the auto-parenthesis +// +// https://github.com/sveltejs/svelte/issues/15930 +const non_parenthesized_keywords = new Set(['all', 'print', 'screen', 'and', 'or', 'not', 'only']); + /** * Creates a media query and provides a `current` property that reflects whether or not it matches. * @@ -27,7 +40,12 @@ export class MediaQuery extends ReactiveValue { * @param {boolean} [fallback] Fallback value for the server */ constructor(query, fallback) { - let final_query = parenthesis_regex.test(query) ? query : `(${query})`; + let final_query = + parenthesis_regex.test(query) || + // we need to use `some` here because technically this `window.matchMedia('random,screen')` still returns true + query.split(/[\s,]+/).some((keyword) => non_parenthesized_keywords.has(keyword.trim())) + ? query + : `(${query})`; const q = window.matchMedia(final_query); super( () => q.matches, diff --git a/packages/svelte/tests/runtime-runes/samples/media-query/_config.js b/packages/svelte/tests/runtime-runes/samples/media-query/_config.js index f7a4ca05f5..d8b202955a 100644 --- a/packages/svelte/tests/runtime-runes/samples/media-query/_config.js +++ b/packages/svelte/tests/runtime-runes/samples/media-query/_config.js @@ -5,5 +5,10 @@ export default test({ async test({ window }) { expect(window.matchMedia).toHaveBeenCalledWith('(max-width: 599px), (min-width: 900px)'); expect(window.matchMedia).toHaveBeenCalledWith('(min-width: 900px)'); + expect(window.matchMedia).toHaveBeenCalledWith('screen'); + expect(window.matchMedia).toHaveBeenCalledWith('not print'); + expect(window.matchMedia).toHaveBeenCalledWith('screen,print'); + expect(window.matchMedia).toHaveBeenCalledWith('screen, print'); + expect(window.matchMedia).toHaveBeenCalledWith('screen, random'); } }); diff --git a/packages/svelte/tests/runtime-runes/samples/media-query/main.svelte b/packages/svelte/tests/runtime-runes/samples/media-query/main.svelte index 446a9213dd..fe07ed8ab0 100644 --- a/packages/svelte/tests/runtime-runes/samples/media-query/main.svelte +++ b/packages/svelte/tests/runtime-runes/samples/media-query/main.svelte @@ -3,4 +3,9 @@ const mq = new MediaQuery("(max-width: 599px), (min-width: 900px)"); const mq2 = new MediaQuery("min-width: 900px"); + const mq3 = new MediaQuery("screen"); + const mq4 = new MediaQuery("not print"); + const mq5 = new MediaQuery("screen,print"); + const mq6 = new MediaQuery("screen, print"); + const mq7 = new MediaQuery("screen, random"); From a96d01b9187a223861c51833046703fe521c9235 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 15:39:25 -0400 Subject: [PATCH 0421/1429] Version Packages (#15956) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/sweet-islands-sell.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/sweet-islands-sell.md diff --git a/.changeset/sweet-islands-sell.md b/.changeset/sweet-islands-sell.md deleted file mode 100644 index b4fbbf182c..0000000000 --- a/.changeset/sweet-islands-sell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: avoid auto-parenthesis for special-keywords-only `MediaQuery` diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index d40ff09317..4508a9b054 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # svelte +## 5.31.1 + +### Patch Changes + +- fix: avoid auto-parenthesis for special-keywords-only `MediaQuery` ([#15937](https://github.com/sveltejs/svelte/pull/15937)) + ## 5.31.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index e0f6afe6fc..45a875d19b 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.31.0", + "version": "5.31.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index ed99ef6795..e42e6741bd 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.31.0'; +export const VERSION = '5.31.1'; export const PUBLIC_VERSION = '5'; From 22a0211cbb1fed32cfdc6d496e8f8fe6bdee086c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 19 May 2025 16:32:23 -0400 Subject: [PATCH 0422/1429] docs: clarify when attachments re-run (#15927) * clarify when attachments re-run * Update documentation/docs/03-template-syntax/09-@attach.md * Update documentation/docs/03-template-syntax/09-@attach.md * update docs * fix --- .../docs/03-template-syntax/09-@attach.md | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/documentation/docs/03-template-syntax/09-@attach.md b/documentation/docs/03-template-syntax/09-@attach.md index 2df0882e34..3644cbc8e8 100644 --- a/documentation/docs/03-template-syntax/09-@attach.md +++ b/documentation/docs/03-template-syntax/09-@attach.md @@ -2,7 +2,9 @@ title: {@attach ...} --- -Attachments are functions that run when an element is mounted to the DOM. Optionally, they can return a function that is called when the element is later removed from the DOM. +Attachments are functions that run in an [effect]($effect) when an element is mounted to the DOM or when [state]($state) read inside the function updates. + +Optionally, they can return a function that is called before the attachment re-runs, or after the element is later removed from the DOM. > [!NOTE] > Attachments are available in Svelte 5.29 and newer. @@ -55,7 +57,7 @@ A useful pattern is for a function, such as `tooltip` in this example, to _retur ``` -Since the `tooltip(content)` expression runs inside an [effect]($effect), the attachment will be destroyed and recreated whenever `content` changes. +Since the `tooltip(content)` expression runs inside an [effect]($effect), the attachment will be destroyed and recreated whenever `content` changes. The same thing would happen for any state read _inside_ the attachment function when it first runs. (If this isn't what you want, see [Controlling when attachments re-run](#Controlling-when-attachments-re-run).) ## Inline attachments @@ -126,6 +128,35 @@ This allows you to create _wrapper components_ that augment elements ([demo](/pl ``` +## Controlling when attachments re-run + +Attachments, unlike [actions](use), are fully reactive: `{@attach foo(bar)}` will re-run on changes to `foo` _or_ `bar` (or any state read inside `foo`): + +```js +// @errors: 7006 2304 2552 +function foo(bar) { + return (node) => { + veryExpensiveSetupWork(node); + update(node, bar); + }; +} +``` + +In the rare case that this is a problem (for example, if `foo` does expensive and unavoidable setup work) consider passing the data inside a function and reading it in a child effect: + +```js +// @errors: 7006 2304 2552 +function foo(+++getBar+++) { + return (node) => { + veryExpensiveSetupWork(node); + ++++ $effect(() => { + update(node, getBar()); + });+++ + } +} +``` + ## Creating attachments programmatically To add attachments to an object that will be spread onto a component or element, use [`createAttachmentKey`](svelte-attachments#createAttachmentKey). From 7183886a73208d4ed34d990319ce2c0f455cad24 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Tue, 20 May 2025 00:02:08 +0200 Subject: [PATCH 0423/1429] feat: attachments `fromAction` utility (#15933) * feat: attachments `fromAction` utility * fix: typing of the utility Co-authored-by: Aidan Bleser <117548273+ieedan@users.noreply.github.com> * simplify implementation - create action first, then only create update/destroy effects if necessary * add since * regenerate * remove FromAction interface * overload * fix: use typedef instead of exported interface * get rid of the arg0, arg1 stuff * oops * god i hate overloads * defer to the reference documentation * damn ur weird typescript * gah --------- Co-authored-by: Aidan Bleser <117548273+ieedan@users.noreply.github.com> Co-authored-by: Rich Harris --- .changeset/wise-tigers-happen.md | 5 + .../docs/03-template-syntax/09-@attach.md | 4 + packages/svelte/src/attachments/index.js | 86 +++++++++++++ .../samples/attachment-from-action/_config.js | 116 ++++++++++++++++++ .../attachment-from-action/main.svelte | 37 ++++++ packages/svelte/types/index.d.ts | 101 +++++++++++++++ 6 files changed, 349 insertions(+) create mode 100644 .changeset/wise-tigers-happen.md create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-from-action/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/attachment-from-action/main.svelte diff --git a/.changeset/wise-tigers-happen.md b/.changeset/wise-tigers-happen.md new file mode 100644 index 0000000000..53cc671859 --- /dev/null +++ b/.changeset/wise-tigers-happen.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: attachments `fromAction` utility diff --git a/documentation/docs/03-template-syntax/09-@attach.md b/documentation/docs/03-template-syntax/09-@attach.md index 3644cbc8e8..b25fbb32a6 100644 --- a/documentation/docs/03-template-syntax/09-@attach.md +++ b/documentation/docs/03-template-syntax/09-@attach.md @@ -160,3 +160,7 @@ function foo(+++getBar+++) { ## Creating attachments programmatically To add attachments to an object that will be spread onto a component or element, use [`createAttachmentKey`](svelte-attachments#createAttachmentKey). + +## Converting actions to attachments + +If you're using a library that only provides actions, you can convert them to attachments with [`fromAction`](svelte-attachments#fromAction), allowing you to (for example) use them with components. diff --git a/packages/svelte/src/attachments/index.js b/packages/svelte/src/attachments/index.js index 948a19f4dd..dc2f3e93d7 100644 --- a/packages/svelte/src/attachments/index.js +++ b/packages/svelte/src/attachments/index.js @@ -1,4 +1,9 @@ +/** @import { Action, ActionReturn } from '../action/public' */ +/** @import { Attachment } from './public' */ +import { noop, render_effect } from 'svelte/internal/client'; import { ATTACHMENT_KEY } from '../constants.js'; +import { untrack } from 'svelte'; +import { teardown } from '../internal/client/reactivity/effects.js'; /** * Creates an object key that will be recognised as an attachment when the object is spread onto an element, @@ -25,3 +30,84 @@ import { ATTACHMENT_KEY } from '../constants.js'; export function createAttachmentKey() { return Symbol(ATTACHMENT_KEY); } + +/** + * Converts an [action](https://svelte.dev/docs/svelte/use) into an [attachment](https://svelte.dev/docs/svelte/@attach) keeping the same behavior. + * It's useful if you want to start using attachments on components but you have actions provided by a library. + * + * Note that the second argument, if provided, must be a function that _returns_ the argument to the + * action function, not the argument itself. + * + * ```svelte + * + *
...
+ * + * + *
bar)}>...
+ * ``` + * @template {EventTarget} E + * @template {unknown} T + * @overload + * @param {Action | ((element: E, arg: T) => void | ActionReturn)} action The action function + * @param {() => T} fn A function that returns the argument for the action + * @returns {Attachment} + */ +/** + * Converts an [action](https://svelte.dev/docs/svelte/use) into an [attachment](https://svelte.dev/docs/svelte/@attach) keeping the same behavior. + * It's useful if you want to start using attachments on components but you have actions provided by a library. + * + * Note that the second argument, if provided, must be a function that _returns_ the argument to the + * action function, not the argument itself. + * + * ```svelte + * + *
...
+ * + * + *
bar)}>...
+ * ``` + * @template {EventTarget} E + * @overload + * @param {Action | ((element: E) => void | ActionReturn)} action The action function + * @returns {Attachment} + */ +/** + * Converts an [action](https://svelte.dev/docs/svelte/use) into an [attachment](https://svelte.dev/docs/svelte/@attach) keeping the same behavior. + * It's useful if you want to start using attachments on components but you have actions provided by a library. + * + * Note that the second argument, if provided, must be a function that _returns_ the argument to the + * action function, not the argument itself. + * + * ```svelte + * + *
...
+ * + * + *
bar)}>...
+ * ``` + * + * @template {EventTarget} E + * @template {unknown} T + * @param {Action | ((element: E, arg: T) => void | ActionReturn)} action The action function + * @param {() => T} fn A function that returns the argument for the action + * @returns {Attachment} + * @since 5.32 + */ +export function fromAction(action, fn = /** @type {() => T} */ (noop)) { + return (element) => { + const { update, destroy } = untrack(() => action(element, fn()) ?? {}); + + if (update) { + var ran = false; + render_effect(() => { + const arg = fn(); + if (ran) update(arg); + }); + ran = true; + } + + if (destroy) { + teardown(destroy); + } + }; +} diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-from-action/_config.js b/packages/svelte/tests/runtime-runes/samples/attachment-from-action/_config.js new file mode 100644 index 0000000000..2e53f0d29d --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-from-action/_config.js @@ -0,0 +1,116 @@ +import { ok, test } from '../../test'; +import { flushSync } from 'svelte'; + +export default test({ + async test({ assert, target, logs }) { + const [btn, btn2, btn3] = target.querySelectorAll('button'); + + // both logs on creation it will not log on change + assert.deepEqual(logs, ['create', 0, 'action', 'create', 0, 'attachment']); + + // clicking the first button logs the right value + flushSync(() => { + btn?.click(); + }); + assert.deepEqual(logs, ['create', 0, 'action', 'create', 0, 'attachment', 0]); + + // clicking the second button logs the right value + flushSync(() => { + btn2?.click(); + }); + assert.deepEqual(logs, ['create', 0, 'action', 'create', 0, 'attachment', 0, 0]); + + // updating the arguments logs the update function for both + flushSync(() => { + btn3?.click(); + }); + assert.deepEqual(logs, [ + 'create', + 0, + 'action', + 'create', + 0, + 'attachment', + 0, + 0, + 'update', + 1, + 'action', + 'update', + 1, + 'attachment' + ]); + + // clicking the first button again shows the right value + flushSync(() => { + btn?.click(); + }); + assert.deepEqual(logs, [ + 'create', + 0, + 'action', + 'create', + 0, + 'attachment', + 0, + 0, + 'update', + 1, + 'action', + 'update', + 1, + 'attachment', + 1 + ]); + + // clicking the second button again shows the right value + flushSync(() => { + btn2?.click(); + }); + assert.deepEqual(logs, [ + 'create', + 0, + 'action', + 'create', + 0, + 'attachment', + 0, + 0, + 'update', + 1, + 'action', + 'update', + 1, + 'attachment', + 1, + 1 + ]); + + // unmounting logs the destroy function for both + flushSync(() => { + btn3?.click(); + }); + assert.deepEqual(logs, [ + 'create', + 0, + 'action', + 'create', + 0, + 'attachment', + 0, + 0, + 'update', + 1, + 'action', + 'update', + 1, + 'attachment', + 1, + 1, + 'destroy', + 'action', + 'destroy', + 'attachment' + ]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/attachment-from-action/main.svelte b/packages/svelte/tests/runtime-runes/samples/attachment-from-action/main.svelte new file mode 100644 index 0000000000..35079aa15e --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/attachment-from-action/main.svelte @@ -0,0 +1,37 @@ + + +{#if count < 2} + + +{/if} + + \ No newline at end of file diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index 1fda9a36b8..fcc1ec315c 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -658,6 +658,107 @@ declare module 'svelte/attachments' { * @since 5.29 */ export function createAttachmentKey(): symbol; + /** + * Converts an [action](https://svelte.dev/docs/svelte/use) into an [attachment](https://svelte.dev/docs/svelte/@attach) keeping the same behavior. + * It's useful if you want to start using attachments on components but you have actions provided by a library. + * + * Note that the second argument, if provided, must be a function that _returns_ the argument to the + * action function, not the argument itself. + * + * ```svelte + * + *
...
+ * + * + *
bar)}>...
+ * ``` + * */ + export function fromAction(action: Action | ((element: E, arg: T) => void | ActionReturn), fn: () => T): Attachment; + /** + * Converts an [action](https://svelte.dev/docs/svelte/use) into an [attachment](https://svelte.dev/docs/svelte/@attach) keeping the same behavior. + * It's useful if you want to start using attachments on components but you have actions provided by a library. + * + * Note that the second argument, if provided, must be a function that _returns_ the argument to the + * action function, not the argument itself. + * + * ```svelte + * + *
...
+ * + * + *
bar)}>...
+ * ``` + * */ + export function fromAction(action: Action | ((element: E) => void | ActionReturn)): Attachment; + /** + * Actions can return an object containing the two properties defined in this interface. Both are optional. + * - update: An action can have a parameter. This method will be called whenever that parameter changes, + * immediately after Svelte has applied updates to the markup. `ActionReturn` and `ActionReturn` both + * mean that the action accepts no parameters. + * - destroy: Method that is called after the element is unmounted + * + * Additionally, you can specify which additional attributes and events the action enables on the applied element. + * This applies to TypeScript typings only and has no effect at runtime. + * + * Example usage: + * ```ts + * interface Attributes { + * newprop?: string; + * 'on:event': (e: CustomEvent) => void; + * } + * + * export function myAction(node: HTMLElement, parameter: Parameter): ActionReturn { + * // ... + * return { + * update: (updatedParameter) => {...}, + * destroy: () => {...} + * }; + * } + * ``` + */ + interface ActionReturn< + Parameter = undefined, + Attributes extends Record = Record + > { + update?: (parameter: Parameter) => void; + destroy?: () => void; + /** + * ### DO NOT USE THIS + * This exists solely for type-checking and has no effect at runtime. + * Set this through the `Attributes` generic instead. + */ + $$_attributes?: Attributes; + } + + /** + * Actions are functions that are called when an element is created. + * You can use this interface to type such actions. + * The following example defines an action that only works on `
` elements + * and optionally accepts a parameter which it has a default value for: + * ```ts + * export const myAction: Action = (node, param = { someProperty: true }) => { + * // ... + * } + * ``` + * `Action` and `Action` both signal that the action accepts no parameters. + * + * You can return an object with methods `update` and `destroy` from the function and type which additional attributes and events it has. + * See interface `ActionReturn` for more details. + */ + interface Action< + Element = HTMLElement, + Parameter = undefined, + Attributes extends Record = Record + > { + ( + ...args: undefined extends Parameter + ? [node: Node, parameter?: Parameter] + : [node: Node, parameter: Parameter] + ): void | ActionReturn; + } + + // Implementation notes: + // - undefined extends X instead of X extends undefined makes this work better with both strict and nonstrict mode export {}; } From c66a43940aee4c24f4c003a50b433e64d316e40a Mon Sep 17 00:00:00 2001 From: 7nik Date: Tue, 20 May 2025 17:50:54 +0300 Subject: [PATCH 0424/1429] feat: warn on implicitly closed tags (#15932) * warn on implicitly closed tags * lint * changeset * separate tests for parent/sibling case * account for attributes in opening tag * tweak * update message --------- Co-authored-by: 7nik Co-authored-by: Rich Harris --- .changeset/blue-experts-tell.md | 5 ++++ .../.generated/compile-warnings.md | 19 ++++++++++++++ .../messages/compile-warnings/template.md | 17 ++++++++++++ .../compiler/phases/1-parse/state/element.js | 13 +++++++++- packages/svelte/src/compiler/warnings.js | 11 ++++++++ .../implicitly-closed-by-parent/input.svelte | 6 +++++ .../implicitly-closed-by-parent/warnings.json | 26 +++++++++++++++++++ .../implicitly-closed-by-sibling/input.svelte | 9 +++++++ .../warnings.json | 26 +++++++++++++++++++ 9 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 .changeset/blue-experts-tell.md create mode 100644 packages/svelte/tests/validator/samples/implicitly-closed-by-parent/input.svelte create mode 100644 packages/svelte/tests/validator/samples/implicitly-closed-by-parent/warnings.json create mode 100644 packages/svelte/tests/validator/samples/implicitly-closed-by-sibling/input.svelte create mode 100644 packages/svelte/tests/validator/samples/implicitly-closed-by-sibling/warnings.json diff --git a/.changeset/blue-experts-tell.md b/.changeset/blue-experts-tell.md new file mode 100644 index 0000000000..a12fb126d1 --- /dev/null +++ b/.changeset/blue-experts-tell.md @@ -0,0 +1,5 @@ +--- +'svelte': minor +--- + +feat: warn on implicitly closed tags diff --git a/documentation/docs/98-reference/.generated/compile-warnings.md b/documentation/docs/98-reference/.generated/compile-warnings.md index 7069f90206..b579d38602 100644 --- a/documentation/docs/98-reference/.generated/compile-warnings.md +++ b/documentation/docs/98-reference/.generated/compile-warnings.md @@ -632,6 +632,25 @@ In some situations a selector may target an element that is not 'visible' to the ``` +### element_implicitly_closed + +``` +This element is implicitly closed by the following `%tag%`, which can cause an unexpected DOM structure. Add an explicit `%closing%` to avoid surprises. +``` + +In HTML, some elements are implicitly closed by another element. For example, you cannot nest a `

` inside another `

`: + +```html + +

hello

+ + +

+

hello

+``` + +Similarly, a parent element's closing tag will implicitly close all child elements, even if the ` `<%name%>` will be treated as an HTML element unless it begins with a capital letter +## element_implicitly_closed + +> This element is implicitly closed by the following `%tag%`, which can cause an unexpected DOM structure. Add an explicit `%closing%` to avoid surprises. + +In HTML, some elements are implicitly closed by another element. For example, you cannot nest a `

` inside another `

`: + +```html + +

hello

+ + +

+

hello

+``` + +Similarly, a parent element's closing tag will implicitly close all child elements, even if the ` Self-closing HTML tags for non-void elements are ambiguous — use `<%name% ...>` rather than `<%name% ... />` diff --git a/packages/svelte/src/compiler/phases/1-parse/state/element.js b/packages/svelte/src/compiler/phases/1-parse/state/element.js index d20369b0d9..6b6c9160d8 100644 --- a/packages/svelte/src/compiler/phases/1-parse/state/element.js +++ b/packages/svelte/src/compiler/phases/1-parse/state/element.js @@ -93,7 +93,16 @@ export default function element(parser) { } } - if (parent.type !== 'RegularElement' && !parser.loose) { + if (parent.type === 'RegularElement') { + if (!parser.last_auto_closed_tag || parser.last_auto_closed_tag.tag !== name) { + const end = parent.fragment.nodes[0]?.start ?? start; + w.element_implicitly_closed( + { start: parent.start, end }, + ``, + `` + ); + } + } else if (!parser.loose) { if (parser.last_auto_closed_tag && parser.last_auto_closed_tag.tag === name) { e.element_invalid_closing_tag_autoclosed(start, name, parser.last_auto_closed_tag.reason); } else { @@ -186,6 +195,8 @@ export default function element(parser) { parser.allow_whitespace(); if (parent.type === 'RegularElement' && closing_tag_omitted(parent.name, name)) { + const end = parent.fragment.nodes[0]?.start ?? start; + w.element_implicitly_closed({ start: parent.start, end }, `<${name}>`, ``); parent.end = start; parser.pop(); parser.last_auto_closed_tag = { diff --git a/packages/svelte/src/compiler/warnings.js b/packages/svelte/src/compiler/warnings.js index c281433213..2a8316308c 100644 --- a/packages/svelte/src/compiler/warnings.js +++ b/packages/svelte/src/compiler/warnings.js @@ -114,6 +114,7 @@ export const codes = [ 'bind_invalid_each_rest', 'block_empty', 'component_name_lowercase', + 'element_implicitly_closed', 'element_invalid_self_closing_tag', 'event_directive_deprecated', 'node_invalid_placement_ssr', @@ -746,6 +747,16 @@ export function component_name_lowercase(node, name) { w(node, 'component_name_lowercase', `\`<${name}>\` will be treated as an HTML element unless it begins with a capital letter\nhttps://svelte.dev/e/component_name_lowercase`); } +/** + * This element is implicitly closed by the following `%tag%`, which can cause an unexpected DOM structure. Add an explicit `%closing%` to avoid surprises. + * @param {null | NodeLike} node + * @param {string} tag + * @param {string} closing + */ +export function element_implicitly_closed(node, tag, closing) { + w(node, 'element_implicitly_closed', `This element is implicitly closed by the following \`${tag}\`, which can cause an unexpected DOM structure. Add an explicit \`${closing}\` to avoid surprises.\nhttps://svelte.dev/e/element_implicitly_closed`); +} + /** * Self-closing HTML tags for non-void elements are ambiguous — use `<%name% ...>` rather than `<%name% ... />` * @param {null | NodeLike} node diff --git a/packages/svelte/tests/validator/samples/implicitly-closed-by-parent/input.svelte b/packages/svelte/tests/validator/samples/implicitly-closed-by-parent/input.svelte new file mode 100644 index 0000000000..f67eba18b8 --- /dev/null +++ b/packages/svelte/tests/validator/samples/implicitly-closed-by-parent/input.svelte @@ -0,0 +1,6 @@ +
+ +
+
+

hello

+
diff --git a/packages/svelte/tests/validator/samples/implicitly-closed-by-parent/warnings.json b/packages/svelte/tests/validator/samples/implicitly-closed-by-parent/warnings.json new file mode 100644 index 0000000000..1316a2b65b --- /dev/null +++ b/packages/svelte/tests/validator/samples/implicitly-closed-by-parent/warnings.json @@ -0,0 +1,26 @@ +[ + { + "code": "element_implicitly_closed", + "message": "This element is implicitly closed by the following ``, which can cause an unexpected DOM structure. Add an explicit `
` to avoid surprises.", + "start": { + "line": 1, + "column": 6 + }, + "end": { + "line": 1, + "column": 25 + } + }, + { + "code": "element_implicitly_closed", + "message": "This element is implicitly closed by the following ``, which can cause an unexpected DOM structure. Add an explicit `
` to avoid surprises.", + "start": { + "line": 4, + "column": 1 + }, + "end": { + "line": 4, + "column": 20 + } + } +] diff --git a/packages/svelte/tests/validator/samples/implicitly-closed-by-sibling/input.svelte b/packages/svelte/tests/validator/samples/implicitly-closed-by-sibling/input.svelte new file mode 100644 index 0000000000..7721f2f380 --- /dev/null +++ b/packages/svelte/tests/validator/samples/implicitly-closed-by-sibling/input.svelte @@ -0,0 +1,9 @@ +
+

+ +

+
+ +
+

+
diff --git a/packages/svelte/tests/validator/samples/implicitly-closed-by-sibling/warnings.json b/packages/svelte/tests/validator/samples/implicitly-closed-by-sibling/warnings.json new file mode 100644 index 0000000000..6ea36c5a50 --- /dev/null +++ b/packages/svelte/tests/validator/samples/implicitly-closed-by-sibling/warnings.json @@ -0,0 +1,26 @@ +[ + { + "code": "element_implicitly_closed", + "message": "This element is implicitly closed by the following `

`, which can cause an unexpected DOM structure. Add an explicit `

` to avoid surprises.", + "start": { + "line": 2, + "column": 1 + }, + "end": { + "line": 2, + "column": 18 + } + }, + { + "code": "element_implicitly_closed", + "message": "This element is implicitly closed by the following `

`, which can cause an unexpected DOM structure. Add an explicit `

` to avoid surprises.", + "start": { + "line": 8, + "column": 1 + }, + "end": { + "line": 8, + "column": 18 + } + } +] From 93443f93160792d49a85b6b8ac7571abd3fccede Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 20 May 2025 11:13:55 -0400 Subject: [PATCH 0425/1429] fix: only re-run directly applied attachment if it changed (#15962) * fix: only re-run directly applied attachment if it changed * add note * one down * fix * Revert "one down" This reverts commit 9f6c4cf84838de8d8cb93e7217de37871839ac8b. --- .changeset/slimy-drinks-divide.md | 5 ++++ .../client/dom/elements/attachments.js | 30 +++++++++++++++---- .../attachment-in-mutated-state/_config.js | 16 ++++++++++ .../attachment-in-mutated-state/main.svelte | 15 ++++++++++ 4 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 .changeset/slimy-drinks-divide.md create mode 100644 packages/svelte/tests/runtime-legacy/samples/attachment-in-mutated-state/_config.js create mode 100644 packages/svelte/tests/runtime-legacy/samples/attachment-in-mutated-state/main.svelte diff --git a/.changeset/slimy-drinks-divide.md b/.changeset/slimy-drinks-divide.md new file mode 100644 index 0000000000..420d0bed56 --- /dev/null +++ b/.changeset/slimy-drinks-divide.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: only re-run directly applied attachment if it changed diff --git a/packages/svelte/src/internal/client/dom/elements/attachments.js b/packages/svelte/src/internal/client/dom/elements/attachments.js index 6e3089a384..4fc1280138 100644 --- a/packages/svelte/src/internal/client/dom/elements/attachments.js +++ b/packages/svelte/src/internal/client/dom/elements/attachments.js @@ -1,15 +1,33 @@ -import { effect } from '../../reactivity/effects.js'; +/** @import { Effect } from '#client' */ +import { block, branch, effect, destroy_effect } from '../../reactivity/effects.js'; + +// TODO in 6.0 or 7.0, when we remove legacy mode, we can simplify this by +// getting rid of the block/branch stuff and just letting the effect rip. +// see https://github.com/sveltejs/svelte/pull/15962 /** * @param {Element} node * @param {() => (node: Element) => void} get_fn */ export function attach(node, get_fn) { - effect(() => { - const fn = get_fn(); + /** @type {false | undefined | ((node: Element) => void)} */ + var fn = undefined; + + /** @type {Effect | null} */ + var e; + + block(() => { + if (fn !== (fn = get_fn())) { + if (e) { + destroy_effect(e); + e = null; + } - // we use `&&` rather than `?.` so that things like - // `{@attach DEV && something_dev_only()}` work - return fn && fn(node); + if (fn) { + e = branch(() => { + effect(() => /** @type {(node: Element) => void} */ (fn)(node)); + }); + } + } }); } diff --git a/packages/svelte/tests/runtime-legacy/samples/attachment-in-mutated-state/_config.js b/packages/svelte/tests/runtime-legacy/samples/attachment-in-mutated-state/_config.js new file mode 100644 index 0000000000..5d37252358 --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/attachment-in-mutated-state/_config.js @@ -0,0 +1,16 @@ +import { flushSync } from 'svelte'; +import { test } from '../../test'; + +export default test({ + test({ assert, target, logs }) { + assert.deepEqual(logs, ['up']); + + const button = target.querySelector('button'); + + flushSync(() => button?.click()); + assert.deepEqual(logs, ['up']); + + flushSync(() => button?.click()); + assert.deepEqual(logs, ['up', 'down']); + } +}); diff --git a/packages/svelte/tests/runtime-legacy/samples/attachment-in-mutated-state/main.svelte b/packages/svelte/tests/runtime-legacy/samples/attachment-in-mutated-state/main.svelte new file mode 100644 index 0000000000..fbec108c7a --- /dev/null +++ b/packages/svelte/tests/runtime-legacy/samples/attachment-in-mutated-state/main.svelte @@ -0,0 +1,15 @@ + + + + +{#if state.count < 2} +
+{/if} From 21d5448ab72c1e0cf5dfd6ec989489d4f57516f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 11:28:43 -0400 Subject: [PATCH 0426/1429] Version Packages (#15959) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/blue-experts-tell.md | 5 ----- .changeset/slimy-drinks-divide.md | 5 ----- .changeset/wise-tigers-happen.md | 5 ----- packages/svelte/CHANGELOG.md | 12 ++++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 14 insertions(+), 17 deletions(-) delete mode 100644 .changeset/blue-experts-tell.md delete mode 100644 .changeset/slimy-drinks-divide.md delete mode 100644 .changeset/wise-tigers-happen.md diff --git a/.changeset/blue-experts-tell.md b/.changeset/blue-experts-tell.md deleted file mode 100644 index a12fb126d1..0000000000 --- a/.changeset/blue-experts-tell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: warn on implicitly closed tags diff --git a/.changeset/slimy-drinks-divide.md b/.changeset/slimy-drinks-divide.md deleted file mode 100644 index 420d0bed56..0000000000 --- a/.changeset/slimy-drinks-divide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -fix: only re-run directly applied attachment if it changed diff --git a/.changeset/wise-tigers-happen.md b/.changeset/wise-tigers-happen.md deleted file mode 100644 index 53cc671859..0000000000 --- a/.changeset/wise-tigers-happen.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': minor ---- - -feat: attachments `fromAction` utility diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 4508a9b054..270460e5ed 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,17 @@ # svelte +## 5.32.0 + +### Minor Changes + +- feat: warn on implicitly closed tags ([#15932](https://github.com/sveltejs/svelte/pull/15932)) + +- feat: attachments `fromAction` utility ([#15933](https://github.com/sveltejs/svelte/pull/15933)) + +### Patch Changes + +- fix: only re-run directly applied attachment if it changed ([#15962](https://github.com/sveltejs/svelte/pull/15962)) + ## 5.31.1 ### Patch Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 45a875d19b..d374880887 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.31.1", + "version": "5.32.0", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index e42e6741bd..9a198d097d 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.31.1'; +export const VERSION = '5.32.0'; export const PUBLIC_VERSION = '5'; From de8094910de7e2d491c2f26b891844c2f311f78f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gautier=20Ben=20A=C3=AFm?= <48261497+GauBen@users.noreply.github.com> Date: Tue, 20 May 2025 19:32:47 +0200 Subject: [PATCH 0427/1429] fix(14815): warn when an invalid value is given * Create nervous-stingrays-travel.md --- .changeset/nervous-stingrays-travel.md | 5 +++++ .../.generated/client-warnings.md | 13 +++++++++++++ .../messages/client-warnings/warnings.md | 11 +++++++++++ .../client/dom/elements/bindings/select.js | 19 +++++++++++++++---- .../svelte/src/internal/client/warnings.js | 11 +++++++++++ .../select-multiple-invalid-value/_config.js | 7 +++++++ .../select-multiple-invalid-value/main.svelte | 9 +++++++++ 7 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 .changeset/nervous-stingrays-travel.md create mode 100644 packages/svelte/tests/runtime-runes/samples/select-multiple-invalid-value/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/select-multiple-invalid-value/main.svelte diff --git a/.changeset/nervous-stingrays-travel.md b/.changeset/nervous-stingrays-travel.md new file mode 100644 index 0000000000..9ccc3e6c65 --- /dev/null +++ b/.changeset/nervous-stingrays-travel.md @@ -0,0 +1,5 @@ +--- +"svelte": patch +--- + +Warn when an invalid `` element should be an array, but it received a non-array value. The selection will be kept as is. +``` + +When using `` element should be an array, but it received a non-array value. The selection will be kept as is. + +When using `` element should be an array, but it received a non-array value. The selection will be kept as is. + */ +export function select_multiple_invalid_value() { + if (DEV) { + console.warn(`%c[svelte] select_multiple_invalid_value\n%cThe \`value\` property of a \`` element should be an array, but it received a non-array value. The selection will be kept as is.' + ] +}); diff --git a/packages/svelte/tests/runtime-runes/samples/select-multiple-invalid-value/main.svelte b/packages/svelte/tests/runtime-runes/samples/select-multiple-invalid-value/main.svelte new file mode 100644 index 0000000000..f3dfedf0e9 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/select-multiple-invalid-value/main.svelte @@ -0,0 +1,9 @@ + + + From ef48d35a15fc104efd6ee5cf13eceea3ace4ae0d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 14:13:31 -0400 Subject: [PATCH 0428/1429] Version Packages (#15967) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/nervous-stingrays-travel.md | 5 ----- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) delete mode 100644 .changeset/nervous-stingrays-travel.md diff --git a/.changeset/nervous-stingrays-travel.md b/.changeset/nervous-stingrays-travel.md deleted file mode 100644 index 9ccc3e6c65..0000000000 --- a/.changeset/nervous-stingrays-travel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"svelte": patch ---- - -Warn when an invalid `` value is given ([#14816](https://github.com/sveltejs/svelte/pull/14816)) + ## 5.32.0 ### Minor Changes diff --git a/packages/svelte/package.json b/packages/svelte/package.json index d374880887..64fa308000 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -2,7 +2,7 @@ "name": "svelte", "description": "Cybernetically enhanced web apps", "license": "MIT", - "version": "5.32.0", + "version": "5.32.1", "type": "module", "types": "./types/index.d.ts", "engines": { diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js index 9a198d097d..c6748487fb 100644 --- a/packages/svelte/src/version.js +++ b/packages/svelte/src/version.js @@ -4,5 +4,5 @@ * The current version, as set in package.json. * @type {string} */ -export const VERSION = '5.32.0'; +export const VERSION = '5.32.1'; export const PUBLIC_VERSION = '5'; From 4e72b7d28b5d8f469ee7ffaee02f94a111823bd4 Mon Sep 17 00:00:00 2001 From: Paolo Ricciuti Date: Wed, 21 May 2025 15:18:33 +0200 Subject: [PATCH 0429/1429] fix: import `untrack` directly from client in `svelte/attachments` (#15974) --- .changeset/short-ads-rhyme.md | 5 +++++ packages/svelte/src/attachments/index.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/short-ads-rhyme.md diff --git a/.changeset/short-ads-rhyme.md b/.changeset/short-ads-rhyme.md new file mode 100644 index 0000000000..31434530ab --- /dev/null +++ b/.changeset/short-ads-rhyme.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: import untrack directly from client in `svelte/attachments` diff --git a/packages/svelte/src/attachments/index.js b/packages/svelte/src/attachments/index.js index dc2f3e93d7..b9fde9b6d9 100644 --- a/packages/svelte/src/attachments/index.js +++ b/packages/svelte/src/attachments/index.js @@ -2,7 +2,7 @@ /** @import { Attachment } from './public' */ import { noop, render_effect } from 'svelte/internal/client'; import { ATTACHMENT_KEY } from '../constants.js'; -import { untrack } from 'svelte'; +import { untrack } from '../index-client.js'; import { teardown } from '../internal/client/reactivity/effects.js'; /** From c03ea47e4ef88782a0f43b3157d7712a03c44698 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 21 May 2025 15:09:21 -0400 Subject: [PATCH 0430/1429] chore: simplify `
` cleaning (#15980)

---
 .changeset/fifty-llamas-know.md                 |  5 +++++
 .../src/compiler/phases/3-transform/utils.js    | 17 +++++------------
 2 files changed, 10 insertions(+), 12 deletions(-)
 create mode 100644 .changeset/fifty-llamas-know.md

diff --git a/.changeset/fifty-llamas-know.md b/.changeset/fifty-llamas-know.md
new file mode 100644
index 0000000000..f11c975f3d
--- /dev/null
+++ b/.changeset/fifty-llamas-know.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+chore: simplify `
` cleaning
diff --git a/packages/svelte/src/compiler/phases/3-transform/utils.js b/packages/svelte/src/compiler/phases/3-transform/utils.js
index 5aa40c8abb..5803c04947 100644
--- a/packages/svelte/src/compiler/phases/3-transform/utils.js
+++ b/packages/svelte/src/compiler/phases/3-transform/utils.js
@@ -271,19 +271,12 @@ export function clean_nodes(
 
 	var first = trimmed[0];
 
-	// initial newline inside a `
` is disregarded, if not followed by another newline
+	// if first text node inside a 
 is a single newline, discard it, because otherwise
+	// the browser will do it for us which could break hydration
 	if (parent.type === 'RegularElement' && parent.name === 'pre' && first?.type === 'Text') {
-		const text = first.data.replace(regex_starts_with_newline, '');
-		if (text !== first.data) {
-			const tmp = text.replace(regex_starts_with_newline, '');
-			if (text === tmp) {
-				first.data = text;
-				first.raw = first.raw.replace(regex_starts_with_newline, '');
-				if (first.data === '') {
-					trimmed.shift();
-					first = trimmed[0];
-				}
-			}
+		if (first.data === '\n' || first.data === '\r\n') {
+			trimmed.shift();
+			first = trimmed[0];
 		}
 	}
 

From 50de8c53178d61f675c5fa1d2d1fc88ae94bb0cd Mon Sep 17 00:00:00 2001
From: Rich Harris 
Date: Wed, 21 May 2025 19:45:07 -0400
Subject: [PATCH 0431/1429] fix: attach __svelte_meta correctly to elements
 following a CSS wrapper (#15982)

* fix: attach __svelte_meta correctly to elements following a CSS wrapper

* Update .changeset/nervous-hotels-clean.md
---
 .changeset/nervous-hotels-clean.md            |  5 +++
 .../client/visitors/shared/component.js       |  9 +++-
 .../svelte-meta-css-wrapper/Component.svelte  |  7 ++++
 .../svelte-meta-css-wrapper/_config.js        | 42 +++++++++++++++++++
 .../svelte-meta-css-wrapper/main.svelte       |  7 ++++
 5 files changed, 69 insertions(+), 1 deletion(-)
 create mode 100644 .changeset/nervous-hotels-clean.md
 create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-meta-css-wrapper/Component.svelte
 create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-meta-css-wrapper/_config.js
 create mode 100644 packages/svelte/tests/runtime-runes/samples/svelte-meta-css-wrapper/main.svelte

diff --git a/.changeset/nervous-hotels-clean.md b/.changeset/nervous-hotels-clean.md
new file mode 100644
index 0000000000..77d5c186c7
--- /dev/null
+++ b/.changeset/nervous-hotels-clean.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: attach `__svelte_meta` correctly to elements following a CSS wrapper
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js
index 6d3d8a68e6..99ed294d26 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/component.js
@@ -1,7 +1,7 @@
 /** @import { BlockStatement, Expression, ExpressionStatement, Identifier, MemberExpression, Pattern, Property, SequenceExpression, Statement } from 'estree' */
 /** @import { AST } from '#compiler' */
 /** @import { ComponentContext } from '../../types.js' */
-import { dev, is_ignored } from '../../../../../state.js';
+import { dev, is_ignored, locator } from '../../../../../state.js';
 import { get_attribute_chunks, object } from '../../../../../utils/ast.js';
 import * as b from '#compiler/builders';
 import { build_bind_this, memoize_expression, validate_binding } from '../shared/utils.js';
@@ -440,6 +440,13 @@ export function build_component(node, component_name, context, anchor = context.
 	}
 
 	if (Object.keys(custom_css_props).length > 0) {
+		if (dev) {
+			const loc = locator(node.start);
+			if (loc) {
+				context.state.locations.push([loc.line, loc.column]);
+			}
+		}
+
 		context.state.template.push(
 			context.state.metadata.namespace === 'svg'
 				? ''
diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-meta-css-wrapper/Component.svelte b/packages/svelte/tests/runtime-runes/samples/svelte-meta-css-wrapper/Component.svelte
new file mode 100644
index 0000000000..5668311b0e
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/svelte-meta-css-wrapper/Component.svelte
@@ -0,0 +1,7 @@
+

hello from component

+ + diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-meta-css-wrapper/_config.js b/packages/svelte/tests/runtime-runes/samples/svelte-meta-css-wrapper/_config.js new file mode 100644 index 0000000000..1d76b0dd00 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-meta-css-wrapper/_config.js @@ -0,0 +1,42 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + dev: true + }, + + html: ` +

hello

+ +

hello from component

+
+

goodbye

+ `, + + async test({ target, assert }) { + const h1 = target.querySelector('h1'); + const h2 = target.querySelector('h2'); + const p = target.querySelector('p'); + + // @ts-expect-error + assert.deepEqual(h1.__svelte_meta.loc, { + file: 'main.svelte', + line: 5, + column: 0 + }); + + // @ts-expect-error + assert.deepEqual(h2.__svelte_meta.loc, { + file: 'Component.svelte', + line: 1, + column: 0 + }); + + // @ts-expect-error + assert.deepEqual(p.__svelte_meta.loc, { + file: 'main.svelte', + line: 7, + column: 0 + }); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/svelte-meta-css-wrapper/main.svelte b/packages/svelte/tests/runtime-runes/samples/svelte-meta-css-wrapper/main.svelte new file mode 100644 index 0000000000..f49a48fb52 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/svelte-meta-css-wrapper/main.svelte @@ -0,0 +1,7 @@ + + +

hello

+ +

goodbye

From 5a840982a7d06d1aaea59db8352fb2bf0188c0ec Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 11:18:39 -0400 Subject: [PATCH 0432/1429] Version Packages (#15978) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .changeset/fifty-llamas-know.md | 5 ----- .changeset/nervous-hotels-clean.md | 5 ----- .changeset/short-ads-rhyme.md | 5 ----- packages/svelte/CHANGELOG.md | 10 ++++++++++ packages/svelte/package.json | 2 +- packages/svelte/src/version.js | 2 +- 6 files changed, 12 insertions(+), 17 deletions(-) delete mode 100644 .changeset/fifty-llamas-know.md delete mode 100644 .changeset/nervous-hotels-clean.md delete mode 100644 .changeset/short-ads-rhyme.md diff --git a/.changeset/fifty-llamas-know.md b/.changeset/fifty-llamas-know.md deleted file mode 100644 index f11c975f3d..0000000000 --- a/.changeset/fifty-llamas-know.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'svelte': patch ---- - -chore: simplify `
` cleaning
diff --git a/.changeset/nervous-hotels-clean.md b/.changeset/nervous-hotels-clean.md
deleted file mode 100644
index 77d5c186c7..0000000000
--- a/.changeset/nervous-hotels-clean.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'svelte': patch
----
-
-fix: attach `__svelte_meta` correctly to elements following a CSS wrapper
diff --git a/.changeset/short-ads-rhyme.md b/.changeset/short-ads-rhyme.md
deleted file mode 100644
index 31434530ab..0000000000
--- a/.changeset/short-ads-rhyme.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'svelte': patch
----
-
-fix: import untrack directly from client in `svelte/attachments`
diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md
index b65a941bd9..4c65954c9f 100644
--- a/packages/svelte/CHANGELOG.md
+++ b/packages/svelte/CHANGELOG.md
@@ -1,5 +1,15 @@
 # svelte
 
+## 5.32.2
+
+### Patch Changes
+
+- chore: simplify `
` cleaning ([#15980](https://github.com/sveltejs/svelte/pull/15980))
+
+- fix: attach `__svelte_meta` correctly to elements following a CSS wrapper ([#15982](https://github.com/sveltejs/svelte/pull/15982))
+
+- fix: import untrack directly from client in `svelte/attachments` ([#15974](https://github.com/sveltejs/svelte/pull/15974))
+
 ## 5.32.1
 
 ### Patch Changes
diff --git a/packages/svelte/package.json b/packages/svelte/package.json
index 64fa308000..168e7abe37 100644
--- a/packages/svelte/package.json
+++ b/packages/svelte/package.json
@@ -2,7 +2,7 @@
   "name": "svelte",
   "description": "Cybernetically enhanced web apps",
   "license": "MIT",
-  "version": "5.32.1",
+  "version": "5.32.2",
   "type": "module",
   "types": "./types/index.d.ts",
   "engines": {
diff --git a/packages/svelte/src/version.js b/packages/svelte/src/version.js
index c6748487fb..c93d7e2e25 100644
--- a/packages/svelte/src/version.js
+++ b/packages/svelte/src/version.js
@@ -4,5 +4,5 @@
  * The current version, as set in package.json.
  * @type {string}
  */
-export const VERSION = '5.32.1';
+export const VERSION = '5.32.2';
 export const PUBLIC_VERSION = '5';

From d1141a19b5eaa156d3cd39cb756f0a69dd3c3b8e Mon Sep 17 00:00:00 2001
From: Paolo Ricciuti 
Date: Thu, 22 May 2025 17:21:50 +0200
Subject: [PATCH 0433/1429] feat: functional template generation (#15538)

Co-authored-by: Rich Harris 
---
 .changeset/forty-llamas-unite.md              |   5 +
 .changeset/smart-boats-accept.md              |   5 +
 .../3-transform/client/transform-client.js    |   7 +-
 .../fix-attribute-casing.js                   |  18 ++
 .../client/transform-template/index.js        |  68 ++++++++
 .../client/transform-template/template.js     | 162 ++++++++++++++++++
 .../client/transform-template/types.d.ts      |  22 +++
 .../phases/3-transform/client/types.d.ts      |  23 +--
 .../3-transform/client/visitors/AwaitBlock.js |   2 +-
 .../3-transform/client/visitors/Comment.js    |   2 +-
 .../3-transform/client/visitors/EachBlock.js  |   2 +-
 .../3-transform/client/visitors/Fragment.js   | 136 ++-------------
 .../3-transform/client/visitors/HtmlTag.js    |   2 +-
 .../3-transform/client/visitors/IfBlock.js    |   2 +-
 .../3-transform/client/visitors/KeyBlock.js   |   2 +-
 .../client/visitors/RegularElement.js         |  66 ++-----
 .../3-transform/client/visitors/RenderTag.js  |   2 +-
 .../client/visitors/SlotElement.js            |   2 +-
 .../client/visitors/SvelteBoundary.js         |   2 +-
 .../client/visitors/SvelteElement.js          |   2 +-
 .../client/visitors/shared/component.js       |  23 ++-
 .../client/visitors/shared/fragment.js        |   4 +-
 .../server/visitors/RegularElement.js         |   6 +-
 .../src/compiler/phases/3-transform/utils.js  |   2 +-
 packages/svelte/src/compiler/types/index.d.ts |   9 +
 .../svelte/src/compiler/validate-options.js   |   2 +
 packages/svelte/src/constants.js              |   2 +
 .../src/internal/client/dev/elements.js       |   2 +-
 .../src/internal/client/dom/operations.js     |  41 +++++
 .../src/internal/client/dom/reconciler.js     |   2 +-
 .../src/internal/client/dom/template.js       | 145 +++++++++++++---
 .../svelte/src/internal/client/dom/types.d.ts |   4 +
 packages/svelte/src/internal/client/index.js  |  12 +-
 .../svelte/src/internal/client/types.d.ts     |   4 +
 .../svelte/src/internal/shared/types.d.ts     |   4 -
 packages/svelte/tests/helpers.js              |   2 +
 packages/svelte/tests/hydration/test.ts       |  11 +-
 packages/svelte/tests/runtime-browser/test.ts |   3 +-
 .../svelte/tests/runtime-legacy/shared.ts     |   3 +-
 .../custom-element-attributes/main.svelte     |  26 +--
 .../samples/functional-templating/_config.js  |   9 +
 .../samples/functional-templating/main.svelte |   1 +
 .../_expected/client/index.svelte.js          |   2 +-
 .../_expected/client/index.svelte.js          |   2 +-
 .../_expected/client/main.svelte.js           |   2 +-
 .../_expected/client/index.svelte.js          |   2 +-
 .../samples/functional-templating/_config.js  |   7 +
 .../_expected/client/index.svelte.js          |  25 +++
 .../_expected/server/index.svelte.js          |   5 +
 .../functional-templating/index.svelte        |   6 +
 .../_expected/client/index.svelte.js          |   2 +-
 .../hmr/_expected/client/index.svelte.js      |   2 +-
 .../_expected/client/index.svelte.js          |   2 +-
 .../purity/_expected/client/index.svelte.js   |   2 +-
 .../_expected/client/index.svelte.js          |   2 +-
 .../_expected/server/index.svelte.js          |   2 +-
 .../_expected/client/index.svelte.js          |   2 +-
 .../_expected/server/index.svelte.js          |   2 +-
 .../_expected/client/index.svelte.js          |   2 +-
 packages/svelte/types/index.d.ts              |  18 ++
 playgrounds/sandbox/run.js                    |  18 +-
 61 files changed, 666 insertions(+), 288 deletions(-)
 create mode 100644 .changeset/forty-llamas-unite.md
 create mode 100644 .changeset/smart-boats-accept.md
 create mode 100644 packages/svelte/src/compiler/phases/3-transform/client/transform-template/fix-attribute-casing.js
 create mode 100644 packages/svelte/src/compiler/phases/3-transform/client/transform-template/index.js
 create mode 100644 packages/svelte/src/compiler/phases/3-transform/client/transform-template/template.js
 create mode 100644 packages/svelte/src/compiler/phases/3-transform/client/transform-template/types.d.ts
 create mode 100644 packages/svelte/src/internal/client/dom/types.d.ts
 create mode 100644 packages/svelte/tests/runtime-runes/samples/functional-templating/_config.js
 create mode 100644 packages/svelte/tests/runtime-runes/samples/functional-templating/main.svelte
 create mode 100644 packages/svelte/tests/snapshot/samples/functional-templating/_config.js
 create mode 100644 packages/svelte/tests/snapshot/samples/functional-templating/_expected/client/index.svelte.js
 create mode 100644 packages/svelte/tests/snapshot/samples/functional-templating/_expected/server/index.svelte.js
 create mode 100644 packages/svelte/tests/snapshot/samples/functional-templating/index.svelte

diff --git a/.changeset/forty-llamas-unite.md b/.changeset/forty-llamas-unite.md
new file mode 100644
index 0000000000..933cece1c6
--- /dev/null
+++ b/.changeset/forty-llamas-unite.md
@@ -0,0 +1,5 @@
+---
+'svelte': minor
+---
+
+feat: XHTML compliance
diff --git a/.changeset/smart-boats-accept.md b/.changeset/smart-boats-accept.md
new file mode 100644
index 0000000000..4cdfeb30d0
--- /dev/null
+++ b/.changeset/smart-boats-accept.md
@@ -0,0 +1,5 @@
+---
+'svelte': minor
+---
+
+feat: add `fragments: 'html' | 'tree'` option for wider CSP compliance
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
index f2eda3a7d2..e2e006c14b 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-client.js
@@ -154,10 +154,6 @@ export function client_component(analysis, options) {
 		legacy_reactive_imports: [],
 		legacy_reactive_statements: new Map(),
 		metadata: {
-			context: {
-				template_needs_import_node: false,
-				template_contains_script_tag: false
-			},
 			namespace: options.namespace,
 			bound_contenteditable: false
 		},
@@ -174,8 +170,7 @@ export function client_component(analysis, options) {
 		update: /** @type {any} */ (null),
 		expressions: /** @type {any} */ (null),
 		after_update: /** @type {any} */ (null),
-		template: /** @type {any} */ (null),
-		locations: /** @type {any} */ (null)
+		template: /** @type {any} */ (null)
 	};
 
 	const module = /** @type {ESTree.Program} */ (
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-template/fix-attribute-casing.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-template/fix-attribute-casing.js
new file mode 100644
index 0000000000..ce56c43d7c
--- /dev/null
+++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-template/fix-attribute-casing.js
@@ -0,0 +1,18 @@
+const svg_attributes =
+	'accent-height accumulate additive alignment-baseline allowReorder alphabetic amplitude arabic-form ascent attributeName attributeType autoReverse azimuth baseFrequency baseline-shift baseProfile bbox begin bias by calcMode cap-height class clip clipPathUnits clip-path clip-rule color color-interpolation color-interpolation-filters color-profile color-rendering contentScriptType contentStyleType cursor cx cy d decelerate descent diffuseConstant direction display divisor dominant-baseline dur dx dy edgeMode elevation enable-background end exponent externalResourcesRequired fill fill-opacity fill-rule filter filterRes filterUnits flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight format from fr fx fy g1 g2 glyph-name glyph-orientation-horizontal glyph-orientation-vertical glyphRef gradientTransform gradientUnits hanging height href horiz-adv-x horiz-origin-x id ideographic image-rendering in in2 intercept k k1 k2 k3 k4 kernelMatrix kernelUnitLength kerning keyPoints keySplines keyTimes lang lengthAdjust letter-spacing lighting-color limitingConeAngle local marker-end marker-mid marker-start markerHeight markerUnits markerWidth mask maskContentUnits maskUnits mathematical max media method min mode name numOctaves offset onabort onactivate onbegin onclick onend onerror onfocusin onfocusout onload onmousedown onmousemove onmouseout onmouseover onmouseup onrepeat onresize onscroll onunload opacity operator order orient orientation origin overflow overline-position overline-thickness panose-1 paint-order pathLength patternContentUnits patternTransform patternUnits pointer-events points pointsAtX pointsAtY pointsAtZ preserveAlpha preserveAspectRatio primitiveUnits r radius refX refY rendering-intent repeatCount repeatDur requiredExtensions requiredFeatures restart result rotate rx ry scale seed shape-rendering slope spacing specularConstant specularExponent speed spreadMethod startOffset stdDeviation stemh stemv stitchTiles stop-color stop-opacity strikethrough-position strikethrough-thickness string stroke stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width style surfaceScale systemLanguage tabindex tableValues target targetX targetY text-anchor text-decoration text-rendering textLength to transform type u1 u2 underline-position underline-thickness unicode unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical values version vert-adv-y vert-origin-x vert-origin-y viewBox viewTarget visibility width widths word-spacing writing-mode x x-height x1 x2 xChannelSelector xlink:actuate xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type xml:base xml:lang xml:space y y1 y2 yChannelSelector z zoomAndPan'.split(
+		' '
+	);
+
+const svg_attribute_lookup = new Map();
+
+svg_attributes.forEach((name) => {
+	svg_attribute_lookup.set(name.toLowerCase(), name);
+});
+
+/**
+ * @param {string} name
+ */
+export default function fix_attribute_casing(name) {
+	name = name.toLowerCase();
+	return svg_attribute_lookup.get(name) || name;
+}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-template/index.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-template/index.js
new file mode 100644
index 0000000000..d0327e702a
--- /dev/null
+++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-template/index.js
@@ -0,0 +1,68 @@
+/** @import { Location } from 'locate-character' */
+/** @import { Namespace } from '#compiler' */
+/** @import { ComponentClientTransformState } from '../types.js' */
+/** @import { Node } from './types.js' */
+import { TEMPLATE_USE_MATHML, TEMPLATE_USE_SVG } from '../../../../../constants.js';
+import { dev, locator } from '../../../../state.js';
+import * as b from '../../../../utils/builders.js';
+
+/**
+ * @param {Node[]} nodes
+ */
+function build_locations(nodes) {
+	const array = b.array([]);
+
+	for (const node of nodes) {
+		if (node.type !== 'element') continue;
+
+		const { line, column } = /** @type {Location} */ (locator(node.start));
+
+		const expression = b.array([b.literal(line), b.literal(column)]);
+		const children = build_locations(node.children);
+
+		if (children.elements.length > 0) {
+			expression.elements.push(children);
+		}
+
+		array.elements.push(expression);
+	}
+
+	return array;
+}
+
+/**
+ * @param {ComponentClientTransformState} state
+ * @param {Namespace} namespace
+ * @param {number} [flags]
+ */
+export function transform_template(state, namespace, flags = 0) {
+	const tree = state.options.fragments === 'tree';
+
+	const expression = tree ? state.template.as_tree() : state.template.as_html();
+
+	if (tree) {
+		if (namespace === 'svg') flags |= TEMPLATE_USE_SVG;
+		if (namespace === 'mathml') flags |= TEMPLATE_USE_MATHML;
+	}
+
+	let call = b.call(
+		tree ? `$.from_tree` : `$.from_${namespace}`,
+		expression,
+		flags ? b.literal(flags) : undefined
+	);
+
+	if (state.template.contains_script_tag) {
+		call = b.call(`$.with_script`, call);
+	}
+
+	if (dev) {
+		call = b.call(
+			'$.add_locations',
+			call,
+			b.member(b.id(state.analysis.name), '$.FILENAME', true),
+			build_locations(state.template.nodes)
+		);
+	}
+
+	return call;
+}
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/transform-template/template.js b/packages/svelte/src/compiler/phases/3-transform/client/transform-template/template.js
new file mode 100644
index 0000000000..8f7f8a1f43
--- /dev/null
+++ b/packages/svelte/src/compiler/phases/3-transform/client/transform-template/template.js
@@ -0,0 +1,162 @@
+/** @import { AST } from '#compiler' */
+/** @import { Node, Element } from './types'; */
+import { escape_html } from '../../../../../escaping.js';
+import { is_void } from '../../../../../utils.js';
+import * as b from '#compiler/builders';
+import fix_attribute_casing from './fix-attribute-casing.js';
+import { regex_starts_with_newline } from '../../../patterns.js';
+
+export class Template {
+	/**
+	 * `true` if HTML template contains a `
 
 
diff --git a/packages/svelte/tests/runtime-runes/samples/functional-templating/_config.js b/packages/svelte/tests/runtime-runes/samples/functional-templating/_config.js
new file mode 100644
index 0000000000..5d36f2e969
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/functional-templating/_config.js
@@ -0,0 +1,9 @@
+import { test } from '../../test';
+
+export default test({
+	compileOptions: {
+		fragments: 'tree'
+	},
+
+	html: `

hello

` +}); diff --git a/packages/svelte/tests/runtime-runes/samples/functional-templating/main.svelte b/packages/svelte/tests/runtime-runes/samples/functional-templating/main.svelte new file mode 100644 index 0000000000..302a01f335 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/functional-templating/main.svelte @@ -0,0 +1 @@ +

hello

diff --git a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/client/index.svelte.js index 3e5a12ed9d..9bb45ebf78 100644 --- a/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/await-block-scope/_expected/client/index.svelte.js @@ -5,7 +5,7 @@ function increment(_, counter) { counter.count += 1; } -var root = $.template(` `, 1); +var root = $.from_html(` `, 1); export default function Await_block_scope($$anchor) { let counter = $.proxy({ count: 0 }); diff --git a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js index 390e86a351..ba3f4b155a 100644 --- a/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/bind-component-snippet/_expected/client/index.svelte.js @@ -10,7 +10,7 @@ const snippet = ($$anchor) => { $.append($$anchor, text); }; -var root = $.template(` `, 1); +var root = $.from_html(` `, 1); export default function Bind_component_snippet($$anchor) { let value = $.state(''); diff --git a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js index 219db6ffd5..3a13fa7e15 100644 --- a/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js +++ b/packages/svelte/tests/snapshot/samples/dynamic-attributes-casing/_expected/client/main.svelte.js @@ -1,7 +1,7 @@ import 'svelte/internal/disclose-version'; import * as $ from 'svelte/internal/client'; -var root = $.template(`
`, 3); +var root = $.from_html(`
`, 3); export default function Main($$anchor) { // needs to be a snapshot test because jsdom does auto-correct the attribute casing diff --git a/packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/client/index.svelte.js index 3d46a679b8..804a7c26f1 100644 --- a/packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/each-index-non-null/_expected/client/index.svelte.js @@ -2,7 +2,7 @@ import 'svelte/internal/disclose-version'; import 'svelte/internal/flags/legacy'; import * as $ from 'svelte/internal/client'; -var root_1 = $.template(`

`); +var root_1 = $.from_html(`

`); export default function Each_index_non_null($$anchor) { var fragment = $.comment(); diff --git a/packages/svelte/tests/snapshot/samples/functional-templating/_config.js b/packages/svelte/tests/snapshot/samples/functional-templating/_config.js new file mode 100644 index 0000000000..23231c969b --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/functional-templating/_config.js @@ -0,0 +1,7 @@ +import { test } from '../../test'; + +export default test({ + compileOptions: { + fragments: 'tree' + } +}); diff --git a/packages/svelte/tests/snapshot/samples/functional-templating/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/functional-templating/_expected/client/index.svelte.js new file mode 100644 index 0000000000..792d5421e1 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/functional-templating/_expected/client/index.svelte.js @@ -0,0 +1,25 @@ +import 'svelte/internal/disclose-version'; +import 'svelte/internal/flags/legacy'; +import * as $ from 'svelte/internal/client'; + +var root = $.from_tree( + [ + ['h1', null, 'hello'], + ' ', + [ + 'div', + { class: 'potato' }, + ['p', null, 'child element'], + ' ', + ['p', null, 'another child element'] + ] + ], + 1 +); + +export default function Functional_templating($$anchor) { + var fragment = root(); + + $.next(2); + $.append($$anchor, fragment); +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/functional-templating/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/functional-templating/_expected/server/index.svelte.js new file mode 100644 index 0000000000..dc49c0c213 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/functional-templating/_expected/server/index.svelte.js @@ -0,0 +1,5 @@ +import * as $ from 'svelte/internal/server'; + +export default function Functional_templating($$payload) { + $$payload.out += `

hello

child element

another child element

`; +} \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/functional-templating/index.svelte b/packages/svelte/tests/snapshot/samples/functional-templating/index.svelte new file mode 100644 index 0000000000..c0fe8965b8 --- /dev/null +++ b/packages/svelte/tests/snapshot/samples/functional-templating/index.svelte @@ -0,0 +1,6 @@ +

hello

+ +
+

child element

+

another child element

+
diff --git a/packages/svelte/tests/snapshot/samples/hello-world/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/hello-world/_expected/client/index.svelte.js index 899c126001..68fdaa4570 100644 --- a/packages/svelte/tests/snapshot/samples/hello-world/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/hello-world/_expected/client/index.svelte.js @@ -2,7 +2,7 @@ import 'svelte/internal/disclose-version'; import 'svelte/internal/flags/legacy'; import * as $ from 'svelte/internal/client'; -var root = $.template(`

hello world

`); +var root = $.from_html(`

hello world

`); export default function Hello_world($$anchor) { var h1 = root(); diff --git a/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js index 3c8322500b..1fac1338c5 100644 --- a/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/hmr/_expected/client/index.svelte.js @@ -2,7 +2,7 @@ import 'svelte/internal/disclose-version'; import 'svelte/internal/flags/legacy'; import * as $ from 'svelte/internal/client'; -var root = $.template(`

hello world

`); +var root = $.from_html(`

hello world

`); function Hmr($$anchor) { var h1 = root(); diff --git a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js index 21f6ed9680..b46acee82e 100644 --- a/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/nullish-coallescence-omittance/_expected/client/index.svelte.js @@ -2,7 +2,7 @@ import 'svelte/internal/disclose-version'; import * as $ from 'svelte/internal/client'; var on_click = (_, count) => $.update(count); -var root = $.template(`

`, 1); +var root = $.from_html(`

`, 1); export default function Nullish_coallescence_omittance($$anchor) { let name = 'world'; diff --git a/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js index f661dbc01d..a351851875 100644 --- a/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/purity/_expected/client/index.svelte.js @@ -2,7 +2,7 @@ import 'svelte/internal/disclose-version'; import 'svelte/internal/flags/legacy'; import * as $ from 'svelte/internal/client'; -var root = $.template(`

`, 1); +var root = $.from_html(`

`, 1); export default function Purity($$anchor) { var fragment = root(); diff --git a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js index 541b56a407..78147659ff 100644 --- a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/client/index.svelte.js @@ -1,7 +1,7 @@ import 'svelte/internal/disclose-version'; import * as $ from 'svelte/internal/client'; -var root = $.template(`

we don't need to traverse these nodes

or

these

ones

these

trailing

nodes

can

be

completely

ignored

`, 3); +var root = $.from_html(`

we don't need to traverse these nodes

or

these

ones

these

trailing

nodes

can

be

completely

ignored

`, 3); export default function Skip_static_subtree($$anchor, $$props) { var fragment = root(); diff --git a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js index e694c12647..a587c05e2c 100644 --- a/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/skip-static-subtree/_expected/server/index.svelte.js @@ -3,5 +3,5 @@ import * as $ from 'svelte/internal/server'; export default function Skip_static_subtree($$payload, $$props) { let { title, content } = $$props; - $$payload.out += `

${$.escape(title)}

we don't need to traverse these nodes

or

these

ones

${$.html(content)}

these

trailing

nodes

can

be

completely

ignored

`; + $$payload.out += `

${$.escape(title)}

we don't need to traverse these nodes

or

these

ones

${$.html(content)}

these

trailing

nodes

can

be

completely

ignored

`; } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js index a67210e541..c446b3d3ef 100644 --- a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/client/index.svelte.js @@ -8,7 +8,7 @@ function reset(_, str, tpl) { $.set(tpl, ``); } -var root = $.template(` `, 1); +var root = $.from_html(` `, 1); export default function State_proxy_literal($$anchor) { let str = $.state(''); diff --git a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js index 7b2a884d70..f814dd4f84 100644 --- a/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/state-proxy-literal/_expected/server/index.svelte.js @@ -11,5 +11,5 @@ export default function State_proxy_literal($$payload) { tpl = ``; } - $$payload.out += ` `; + $$payload.out += ` `; } \ No newline at end of file diff --git a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js index d520d1ef24..464435cb0a 100644 --- a/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js +++ b/packages/svelte/tests/snapshot/samples/text-nodes-deriveds/_expected/client/index.svelte.js @@ -1,7 +1,7 @@ import 'svelte/internal/disclose-version'; import * as $ from 'svelte/internal/client'; -var root = $.template(`

`); +var root = $.from_html(`

`); export default function Text_nodes_deriveds($$anchor) { let count1 = 0; diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index fcc1ec315c..201dbed9aa 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -985,6 +985,15 @@ declare module 'svelte/compiler' { * @default false */ preserveWhitespace?: boolean; + /** + * Which strategy to use when cloning DOM fragments: + * + * - `html` populates a `